Skip to content

Merge remote-tracking branch 'origin/main' into worktree-20260511-165241#93

Merged
JoeOakhartNava merged 6 commits into
mainfrom
worktree-20260511-165241
May 12, 2026
Merged

Merge remote-tracking branch 'origin/main' into worktree-20260511-165241#93
JoeOakhartNava merged 6 commits into
mainfrom
worktree-20260511-165241

Conversation

@JoeOakhartNava

Copy link
Copy Markdown
Member

Auto-generated PR for branch worktree-20260511-165241 (created by merge-to-main-pr.sh).

Test and others added 2 commits May 11, 2026 18:20
…aliases

Pre-existing tickets created before the alias feature shipped had no
data.alias on their CREATE event, so reduce_ticket returned alias=None
and resolve_ticket_id couldn't find them by alias — defeating the whole
human-friendly identifier system for ~325 legacy tickets.

This change:
- _alias.py (new): deterministic alias computer mirroring
  ticket-alias-compute.py. 16-hex IDs get adj-noun-noun, legacy 8-hex
  IDs get adj-noun (one segment unavailable).
- _processors.process_create: when data.alias is missing, fall back to
  compute_alias(ticket_id) so reduce_ticket and downstream consumers
  surface a stable backfilled alias.
- llm_format.KEY_MAP: include 'alias' (mapped to 'a') so --format=llm
  carries the alias to agents that read ticket lists.
- ticket-create.sh: 'Created ticket' line now leads with the alias and
  shows the canonical ID parenthetically.
- ticket-alias-resolve.py (new): single-pass resolver — one Python
  process iterates all ticket dirs (was N subprocess.run calls), and
  also matches against backfilled aliases for legacy tickets.
- ticket-lib.sh resolve_ticket_id: replaces the per-file Python loop
  with a single call to the new resolver.

Tests: 6 new pytest cases for compute_alias parity with the shell
helper and process_create backfill behaviour, plus one new
resolver_alias_backfill case in test-ticket-lib.sh confirming
resolve_ticket_id finds tickets by their backfilled alias.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread plugins/dso/scripts/ticket_reducer/_alias.py Fixed
Comment thread plugins/dso/scripts/ticket-alias-resolve.py Fixed
Comment thread plugins/dso/scripts/ticket-alias-resolve.py Fixed
@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 1/6

[critical] plugins/dso/scripts/ticket_reducer/_alias.py:20-24 (correctness)

Path bug in _alias.py wordlist resolution causes deterministic failure to load wordlist at creation time. The module at plugins/dso/scripts/ticket_reducer/_alias.py computes os.path.join(here, '..', '..', 'resources', 'ticket-wordlist.txt') which resolves to plugins/resources/ticket-wordlist.txt instead of plugins/dso/resources/ticket-wordlist.txt. The path should use only one '..' before 'resources'. With this bug, _load() always encounters OSError, returns empty lists, and compute_alias() always falls back to 8-hex hex string ('0193d61d' for ticket_id '0193-d61d-abcd-1234'). This creates a critical asymmetry: ticket-alias-resolve.py has the correct path (only one '..') and loads the wordlist successfully, returning None instead of the same fallback when the wordlist is unavailable. A legacy ticket created with the fallback alias is later unresolvable by that alias.

Posted by dso_ci_review.runner; resolve this comment when addressed.

@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 2/6

[critical] plugins/dso/scripts/ticket-alias-resolve.py:45-64 plugins/dso/scripts/ticket_reducer/_alias.py:59-62 (correctness)

Asymmetric fallback behavior between _alias.py and ticket-alias-resolve.py when wordlist is unavailable causes legacy tickets to be unresolvable by their own created aliases. _alias.py line 61-62 returns hex_id[:min(len(hex_id), 8)] (an 8-character hex string fallback). ticket-alias-resolve.py line 49 returns None when wordlist is unavailable. When a legacy ticket (no data.alias) is created on a system where the wordlist is missing or unreadable, process_create assigns the 8-hex fallback alias. When that same ticket is later resolved on a system where the wordlist becomes available (or after the path bug is fixed), the resolver cannot find it because it returns a full 3-word alias instead of the 8-hex fallback, or returns None if wordlist is still missing. The created alias and resolved alias do not match.

Posted by dso_ci_review.runner; resolve this comment when addressed.

@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 3/6

[important] plugins/dso/scripts/ticket-alias-resolve.py:25-43 plugins/dso/scripts/ticket-alias-resolve.py:73-75 plugins/dso/scripts/ticket_reducer/_alias.py:25-46 (design)

Code duplication and semantic divergence between two independent implementations of alias computation violates DRY and creates maintenance risk. ticket-alias-resolve.py and ticket_reducer/_alias.py both implement load_wordlist() and compute_alias() with identical logic but subtle behavioral differences (fallback logic, environment variable handling). The functions are separate implementations rather than a shared module. Future changes to wordlist parsing format, compute algorithm, or environment variable handling must be synchronized across two codebases. Additionally, ticket-alias-resolve.py does not respect TICKET_WORDLIST_PATH environment variable override (it computes the path inline at line 73), while _alias.py correctly delegates to _wordlist_path() (line 24). This divergence reduces portability and testability.

Posted by dso_ci_review.runner; resolve this comment when addressed.

@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 4/6

[important] plugins/dso/scripts/ticket-alias-resolve.py:1 plugins/dso/scripts/ticket-alias-resolve.py:46-122 (verification)

New Python script ticket-alias-resolve.py (122 lines) lacks direct unit tests and is tested only via bash integration test that exercises the happy path. The script implements the core single-pass alias/jira_key resolver logic. Edge cases including missing tracker directory, malformed JSON in CREATE events, multiple CREATE files per ticket directory, and file permission errors are not tested in isolation. Direct unit tests for load_wordlist(), compute_alias(), and main() should be added to test_ticket_alias_backfill.py or a new test module.

Posted by dso_ci_review.runner; resolve this comment when addressed.

@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 5/6

[important] plugins/dso/scripts/ticket-lib.sh:1260 (correctness)

Error suppression in ticket-lib.sh line 1263 reduces debuggability without improving correctness. stderr from the Python resolver is redirected to /dev/null. If the script file is missing, Python is not installed, or the script raises an uncaught exception (despite try/except coverage), the error is hidden and resolve_ticket_id() silently returns no matches. An operator debugging why ticket resolution stopped working will not see the underlying cause.

Posted by dso_ci_review.runner; resolve this comment when addressed.

@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 6/6

[important] tests/scripts/test_ticket_alias_backfill.py:88-91 (verification)

[DELETION REMEDIATION] test_process_create_uses_stored_alias_when_present is a tautological test that verifies the test harness rather than process_create behavior. The test plants data['alias']='stored-alias-here' into the CREATE event and asserts state['alias']=='stored-alias-here'. This passes as long as the test file is readable, not whether process_create correctly extracted the alias field from data or correctly chose to use the stored value over backfill. The test would pass even if process_create ignored data.alias entirely and returned a hardcoded value. REMEDIATION: DELETE this test. The behavior it attempts to cover (using stored alias when present) is implicitly verified by test_process_create_backfills_when_alias_missing, which tests the conditional branch where stored_alias is absent and backfill is applied. If explicit independent coverage of the stored-alias path is required, rewrite to assert a computed value differs from the stored value (verifying process_create chose stored over backfill), or pair with an integration test that creates a ticket with alias, then resolves it by that alias.

Posted by dso_ci_review.runner; resolve this comment when addressed.

…n PR #93

llm-review flagged 2 critical + 4 important findings. Addresses all six:

- F1 (claimed path bug in _alias.py): rejected — the '..' count is correct
  (verified: _wordlist_path() resolves to plugins/dso/resources/ticket-wordlist.txt).
  Added test_resolver_wordlist_path_resolves_to_existing_file as the
  executable defense.

- F2 (asymmetric fallback): fixed by single source of truth (below). The two
  files used to compute alias independently — _alias.py fell back to hex_id[:8]
  when the wordlist was missing, ticket-alias-resolve.py fell back to None.
  Now they cannot diverge.

- F3 (DRY): ticket-alias-resolve.py now imports compute_alias from
  ticket_reducer._alias. Removed duplicated load_wordlist() and inline
  compute_alias() body. TICKET_WORDLIST_PATH env var override is honoured
  uniformly via _wordlist_path().

- F4 (no unit tests for resolver script): added 5 direct test cases —
  missing tracker dir (fails loud, exit nonzero), malformed CREATE JSON
  (skipped without crashing), dotfile dirs (ignored), backfilled alias
  resolves a legacy ticket, jira_key precedence over alias on collision.

- F5 (stderr suppression hid debug info): removed `2>/dev/null` from the
  resolver invocation in resolve_ticket_id. The resolver now exits non-zero
  with a useful stderr message when the tracker dir is unreadable.

- F6 (tautological stored-alias test): rewrote test_process_create_uses_
  stored_alias_when_present → test_process_create_prefers_stored_over_
  backfill. The new test asserts the stored value is intentionally
  DIFFERENT from what compute_alias would yield, so a regression where
  backfill clobbers the stored alias would fail the test.

Test suite: 12/12 backfill pytest cases, 76/76 test-ticket-lib.sh.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
data = json.load(f).get("data", {}) or {}
stored_alias = data.get("alias") or ""
jira_key = data.get("jira_key") or ""
except (OSError, json.JSONDecodeError):
@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 1/3

[critical] plugins/dso/scripts/ticket-lib.sh:1258 (correctness)

The resolver script path is not validated before invocation in ticket-lib.sh line 1260. If the resolver script is deleted or python3 is not in PATH, the subprocess silently fails and returns 0 with no output. The calling loop reads 0 lines, _alias_matches and _jira_matches remain empty, and resolve_ticket_id returns 1 with no diagnostic message. The caller cannot distinguish 'no matches found' from 'resolver environment misconfigured'. This is a regression from the previous inline-Python implementation, which at least had some inline validation. Add checks: [ -f "$_resolver_script" ] && command -v python3 >/dev/null before invoking the subprocess, and exit with a diagnostic error if either check fails.

Posted by dso_ci_review.runner; resolve this comment when addressed.

@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 2/3

[important] plugins/dso/scripts/ticket_reducer/_alias.py:37 plugins/dso/scripts/ticket_reducer/_alias.py:61 (correctness)

In ticket_reducer/_alias.py, the _load() function catches OSError and silently continues with empty adjs/nouns lists. When the wordlist file is unavailable (either via direct deletion or via a broken TICKET_WORDLIST_PATH override), compute_alias() falls back to returning the first 8 hex characters of the ticket_id (line 61-62). This silently degrades legacy tickets (tickets without stored data.alias) to a 7-character fallback alias instead of computing the proper adj-noun or adj-noun-noun alias they would have received at creation time. The degradation is undetectable — no error is logged, and the function returns a valid-looking alias string. This violates the alias system's guarantee that legacy tickets surface the same alias they would have been assigned at create-time. Either move the compute_alias import to module-level with explicit error handling (fail fast if wordlist is missing), or restructure _load() to raise a diagnostic error if the wordlist file is unavailable.

Posted by dso_ci_review.runner; resolve this comment when addressed.

@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 3/3

[important] tests/scripts/test_ticket_alias_backfill.py:200 (verification)

The test test_resolver_wordlist_path_resolves_to_existing_file() (line 200-203) only validates that _wordlist_path() returns a path to an existing file. It does not call compute_alias(), does not verify wordlist parsing, and does not validate that the returned path's content is actually used. The underlying wordlist I/O and alias computation behavior is already comprehensively tested by test_compute_alias_matches_shell_helper(), which calls compute_alias() and compares output against the shell-side implementation. This test adds noise without behavioral validation and should be removed to keep the test suite maintainable.

Posted by dso_ci_review.runner; resolve this comment when addressed.

…issing wordlist

Cycle-2 review surfaced 3 new findings (1 critical, 2 important):

- C1 (critical) resolver-script + python3 not validated before invocation —
  silent failure mode hid environment misconfig as 'no matches found'.
  Added explicit checks in resolve_ticket_id; both emit a clear stderr
  diagnostic and return 1.

- I1 (important) _load() swallowed OSError silently — falling back to the
  8-hex alias without ever telling the operator the wordlist was missing.
  Added a one-shot stderr WARN keyed on the new _WARNED_MISSING module flag,
  matching the shell helper's 'FALLBACK' stderr pattern. Single-print so
  bulk callers (resolve_ticket_id scans 18k dirs) don't log-flood.

- I2 (important) test_resolver_wordlist_path_resolves_to_existing_file was
  redundant noise — the existing parity test already exercises the file
  open path implicitly. Replaced with test_load_warns_once_when_wordlist_
  missing which asserts the new WARN behaviour.

Test suite: 12/12 backfill pytest, 76/76 test-ticket-lib.sh.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
"override.",
file=sys.stderr,
)
_WARNED_MISSING = True
@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 1/1

[important] plugins/dso/scripts/ticket-lib.sh:1268 (correctness)

Unvalidated subprocess exit code in bash resolver integration: ticket-lib.sh pipes python3 output to read without checking exit status. If the resolver crashes or exits non-zero, the read loop receives empty input and resolve_ticket_id silently returns 1 (no matches found) instead of propagating the error. This masks resolver failures and makes debugging impossible — operator cannot distinguish between 'no matching ticket' and 'resolver crashed'.

Posted by dso_ci_review.runner; resolve this comment when addressed.

… code

Cycle-3 review: process-substitution piping (`done < <(python3 ...)`) discards
the resolver's exit code. If the resolver crashes mid-scan or exits non-zero
for environment reasons, the read loop completes silently and resolve_ticket_id
reports 'no matches' — indistinguishable from 'ticket genuinely not found'.

- Capture stdout into a variable and check $? separately. Non-zero resolver
  exit now produces 'Error: alias resolver exited N for input X' on stderr
  and propagates as exit 1 from resolve_ticket_id.
- Guard the read loop against the implicit trailing newline in <<<"$var" so
  empty resolver output doesn't iterate once with empty fields.
- New pytest test_resolver_nonzero_exit_propagates_to_resolve_ticket_id
  forces the failure path (tracker-as-file → resolver OSError → exit 1) and
  asserts resolve_ticket_id surfaces the diagnostic.

Test suite: 13/13 backfill pytest, 76/76 test-ticket-lib.sh.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 1/5

[critical] plugins/dso/scripts/ticket-alias-compute.py:50 (correctness)

Alias computation diverges between ticket-alias-compute.py (shell, used at ticket creation) and ticket_reducer/_alias.py (Python, used at resolution backfill). For 8-hex ticket IDs, ticket-alias-compute.py always computes a 3-word alias (adj-noun1-noun2) using hex[8:12] even when that range is out of bounds for 8-hex IDs. ticket_reducer/_alias.py correctly returns a 2-word alias (adj-noun1) for IDs with len(hex_id) < 12. This causes ticket resolution by alias to fail: a user creates a legacy 8-hex ticket, gets output with a 3-word alias, then later tries to resolve it by that alias and the backfill computes a different 2-word alias, resulting in 'ticket not found'.

Posted by dso_ci_review.runner; resolve this comment when addressed.

@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 2/5

[critical] tests/scripts/test_ticket_alias_backfill.py:93 tests/scripts/test_ticket_alias_backfill.py:109 (verification)

test_process_create_prefers_stored_over_backfill (lines 93-109 of test_ticket_alias_backfill.py) is tautological: it plants data.alias='manually-chosen-alias' and asserts state['alias']=='manually-chosen-alias'. The test verifies JSON pass-through, not the conditional decision logic at _processors.py line 64. If the conditional were inverted to 'if NOT stored_alias:', this test would still pass. The test should be deleted — the behavior (stored alias precedence) is implicitly verified by test_process_create_backfills_when_alias_missing, which tests the complementary case (backfill when alias missing).

Posted by dso_ci_review.runner; resolve this comment when addressed.

@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 3/5

[important] plugins/dso/scripts/ticket-alias-compute.py:52 (correctness)

The alias computation logic in ticket-alias-compute.py does not properly validate ticket_id length before indexing hex_id[8:12]. For 8-hex IDs, hex_id[8:12] returns an empty string, and int('', 16) raises ValueError. This is not caught, causing the script to exit with an exception instead of the intended 3-word fallback (or raising a clear error). The corresponding backfill in _alias.py explicitly checks len(hex_id) >= 12 before accessing the third noun index, showing the correct implementation. The shell script should mirror this validation.

Posted by dso_ci_review.runner; resolve this comment when addressed.

@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 4/5

[important] plugins/dso/scripts/ticket-create.sh:271 (verification)

ticket-create.sh output format change (lines 271-277) conditionally displays the alias in the creation output, but the diff provides no test that verifies this new output format end-to-end. The change depends on $ticket_alias being set and non-empty at the point of output, but no test in the provided diff exercises the 'ticket create' command and inspects its stdout to confirm the new format appears correctly. This is a user-visible change that should be tested before merge.

Posted by dso_ci_review.runner; resolve this comment when addressed.

@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 5/5

[important] tests/scripts/test_ticket_alias_backfill.py:220 (verification)

test_resolver_nonzero_exit_propagates_to_resolve_ticket_id (lines 200-221) has weak assertions that allow false negatives. The test checks that the resolver exits non-zero when the tracker dir is unreadable, but the subsequent OR'd assertion accepts a false-negative where the resolver crashes without an error message (as long as 'ticket not found' is not in stdout). This allows the test to pass even if resolve_ticket_id silently reports 'no match' instead of surfacing the resolver's OSError.

Posted by dso_ci_review.runner; resolve this comment when addressed.

…in ticket_create

Cycle-4 review surfaced 5 findings. Two criticals (F1, F2) are defended in a
separate PR comment (see GitHubPRDefenseStore); they rest on misreadings of the
flow. The three remaining findings are addressed here:

- F3 (important): ticket-alias-compute.py was unguarded against 8-hex inputs
  (would have crashed on int('', 16) had it ever been called with one).
  Added len(hex_id) >= 12 guard mirroring ticket_reducer._alias. Dead-code-
  safe today (callers only pass 16-hex) but defends against future drift.

- F4 (important): no test verified the new alias-led 'Created ticket' output.
  Updated tests/scripts/test-ticket-create.sh Test 1 to accept both formats
  and to assert the alias-led variant appears whenever the bundled wordlist
  is available. Also fixed a latent parity gap — ticket-lib-api.sh
  ticket_create had its own duplicate output line which still printed the
  legacy 'Created ticket <id>: <title>' format; ticket-create.sh shell-path
  was already updated, but the lib-api in-process path was not. Now both
  paths emit the alias-led summary identically.

- F5 (important): tightened test_resolver_nonzero_exit_propagates_to_resolve_
  ticket_id. The earlier OR'd assertion could pass on a silent crash (the
  'success-line not in stdout' branch was true vacuously). Replaced with
  two AND'd assertions: explicit diagnostic in stderr, success-line not in
  stdout.

Also: review.max_resolution_attempts lowered from 5 to 3 in dso-config.conf
per session decision — three autonomous fix/defend cycles is the cap before
user escalation; more than that is churn.

Test suite: 13/13 backfill pytest, 76/76 test-ticket-lib.sh, 17/17 test-
ticket-lib-api.sh, 48/48 test-ticket-create.sh.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@JoeOakhartNava

Copy link
Copy Markdown
Member Author

Defenses — cycle-4 findings F1 + F2

The cycle-4 review surfaced two critical findings that I am defending rather than fixing, because both rest on misreadings of the code/flow. Cycle-4 findings F3, F4, F5 are fixed in commit fd6b1ffcac.

F1 — ticket-alias-compute.py allegedly diverges from _alias.py for 8-hex IDs

The finding claims ticket-alias-compute.py "always computes a 3-word alias even when [hex[8:12]] is out of bounds for 8-hex IDs," producing a divergent alias from _alias.py's 2-word path. That outcome cannot occur in practice:

  • ticket-alias-compute.py is invoked from exactly one place: ticket-create.sh (and the lib-api ticket_create mirror).
  • Both callers generate 16-hex IDs via uuid.uuid4() (see ticket-create.sh:177-180 and ticket-lib-api.sh:797).
  • The 8-hex format (e.g. 0193-d61d) is legacy-only — it exists on tickets created before the alias feature shipped and is never produced by current creation paths, so it never reaches ticket-alias-compute.py.
  • Legacy 8-hex tickets surface aliases exclusively through the read-time backfill in _alias.compute_alias, which returns a 2-word adj-noun result. There is no parallel ticket-alias-compute.py invocation that could produce a different value for the same ticket_id.

So: no concrete scenario exists where a ticket's "created" alias and "resolved" alias differ. (That said, cycle-4 F3 — which I did fix in fd6b1ffcac — adds a defensive len(hex_id) >= 12 guard in ticket-alias-compute.py for future-proofing.)

F2 — test_process_create_prefers_stored_over_backfill allegedly tautological

The finding asserts that if the conditional in _processors.process_create were inverted (if not stored_alias:), the test would still pass. Walking through the actual code with stored_alias = "manually-chosen-alias":

# Current:
stored_alias = data.get("alias")           # "manually-chosen-alias"
if stored_alias:                            # True
    state["alias"] = stored_alias           # → "manually-chosen-alias"
else:
    state["alias"] = compute_alias(ticket_id)

With the conditional inverted to if not stored_alias::

if not stored_alias:                        # False  (stored_alias is truthy)
    state["alias"] = stored_alias           # not taken
else:
    state["alias"] = compute_alias(ticket_id)  # → some adj-noun-noun

The else branch runs, calling compute_alias("aaaa-bbbb-cccc-dddd") which yields a deterministic adj-noun-noun like "prim-yarn-sled". The assertion state["alias"] == "manually-chosen-alias" would then fail. The test is therefore not tautological — it actively distinguishes the two branch outcomes.

The test also includes an explicit pre-assertion (assert stored != backfill_would_yield) to guarantee the planted value and the backfill value are different, which is exactly the property that prevents a hardcoded-return implementation from passing the test silently.


Marking F1 and F2 as defended under the cycle-4 R5 budget. Cycle 5 review will re-run; if these defenses are not honoured I'll escalate rather than fix again.

@JoeOakhartNava JoeOakhartNava merged commit a4c47cb into main May 12, 2026
16 of 17 checks passed
@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 1/4

[critical] plugins/dso/scripts/ticket-alias-resolve.py:32 plugins/dso/scripts/ticket_reducer/_processors.py:63 tests/scripts/test_ticket_alias_backfill.py:23 (correctness)

Missing ticket_reducer/__init__.py package initialization. The diff adds three files that import from the ticket_reducer package: ticket-alias-resolve.py imports 'from ticket_reducer._alias import compute_alias', _processors.py imports the same, and test_ticket_alias_backfill.py imports 'from ticket_reducer import reduce_ticket'. Without __init__.py at plugins/dso/scripts/ticket_reducer/__init__.py, Python cannot treat ticket_reducer as a package, causing ModuleNotFoundError at runtime when any of these imports execute.

Posted by dso_ci_review.runner; resolve this comment when addressed.

@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 2/4

[important] tests/scripts/test_ticket_alias_backfill.py:23 (correctness)

Unverified external reference: test_ticket_alias_backfill.py imports 'reduce_ticket' from the ticket_reducer package at line 23. The diff does not show __init__.py or any definition of reduce_ticket in the ticket_reducer package. If reduce_ticket is not exported from __init__.py, the test file fails to import and pytest cannot run any test in this file.

Posted by dso_ci_review.runner; resolve this comment when addressed.

@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 3/4

[important] plugins/dso/scripts/ticket-create.sh:271 plugins/dso/scripts/ticket-lib-api.sh:890 (maintainability)

Duplicated output formatting logic in ticket-creation paths. The conditional block that emits 'Created ticket <alias> (<id>): <title>' appears identically in both ticket-create.sh (lines 271-277) and ticket-lib-api.sh (lines 890-897). Duplicate code creates maintenance debt: any future format change (e.g., adding a timestamp, changing parenthesis style) requires updates in two locations, and divergence between the two paths is silent without explicit test coverage in both.

Posted by dso_ci_review.runner; resolve this comment when addressed.

@github-actions

Copy link
Copy Markdown

DSO llm-review — finding 4/4

[important] tests/scripts/test-ticket-create.sh:103 (verification)

Missing test coverage for duplicate output-formatting in ticket-lib-api.sh. The new test suite in test-ticket-create.sh validates the 'Created ticket <alias> (<id>): <title>' output format when alias is available (lines 103-121), but there is no corresponding test in test-ticket-lib-api.sh to verify that the identical output formatting in that path produces the same result. This leaves the duplication at risk of divergence.

Posted by dso_ci_review.runner; resolve this comment when addressed.

@JoeOakhartNava JoeOakhartNava deleted the worktree-20260511-165241 branch May 12, 2026 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant