Commit a4c47cb
Merge remote-tracking branch 'origin/main' into worktree-20260511-165241 (#93)
* feat(tickets): backfill aliases on read + resolver supports computed 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>
* fix(tickets): consolidate alias logic + address llm-review findings on 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>
* fix(tickets): cycle-2 llm-review — environment validation + WARN on missing 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>
* fix(tickets): cycle-3 llm-review — propagate resolver subprocess exit 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>
* fix(pr-finalize): cycle-4 llm-review findings — fixes 3/5/4 + parity 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>
---------
Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent ce73f79 commit a4c47cb
12 files changed
Lines changed: 563 additions & 46 deletions
File tree
- .claude
- plugins/dso/scripts
- ticket_reducer
- tests/scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
183 | 183 | | |
184 | 184 | | |
185 | 185 | | |
186 | | - | |
| 186 | + | |
187 | 187 | | |
188 | 188 | | |
189 | 189 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
59 | 59 | | |
60 | 60 | | |
61 | 61 | | |
62 | | - | |
63 | | - | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
64 | 70 | | |
65 | 71 | | |
66 | 72 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
268 | 268 | | |
269 | 269 | | |
270 | 270 | | |
271 | | - | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
272 | 277 | | |
273 | 278 | | |
274 | 279 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
887 | 887 | | |
888 | 888 | | |
889 | 889 | | |
890 | | - | |
| 890 | + | |
| 891 | + | |
| 892 | + | |
| 893 | + | |
| 894 | + | |
| 895 | + | |
| 896 | + | |
891 | 897 | | |
892 | 898 | | |
893 | 899 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1245 | 1245 | | |
1246 | 1246 | | |
1247 | 1247 | | |
| 1248 | + | |
| 1249 | + | |
| 1250 | + | |
| 1251 | + | |
1248 | 1252 | | |
1249 | 1253 | | |
1250 | | - | |
1251 | | - | |
1252 | | - | |
1253 | | - | |
1254 | | - | |
1255 | | - | |
1256 | | - | |
1257 | | - | |
1258 | | - | |
1259 | | - | |
1260 | | - | |
1261 | | - | |
1262 | | - | |
1263 | | - | |
1264 | | - | |
1265 | | - | |
1266 | | - | |
1267 | | - | |
1268 | | - | |
1269 | | - | |
1270 | | - | |
1271 | | - | |
1272 | | - | |
1273 | | - | |
1274 | | - | |
1275 | | - | |
1276 | | - | |
1277 | | - | |
1278 | | - | |
1279 | | - | |
1280 | | - | |
1281 | | - | |
1282 | | - | |
1283 | | - | |
1284 | | - | |
1285 | | - | |
1286 | | - | |
| 1254 | + | |
| 1255 | + | |
| 1256 | + | |
| 1257 | + | |
| 1258 | + | |
| 1259 | + | |
| 1260 | + | |
| 1261 | + | |
| 1262 | + | |
| 1263 | + | |
| 1264 | + | |
| 1265 | + | |
| 1266 | + | |
| 1267 | + | |
| 1268 | + | |
| 1269 | + | |
| 1270 | + | |
| 1271 | + | |
| 1272 | + | |
| 1273 | + | |
| 1274 | + | |
| 1275 | + | |
| 1276 | + | |
| 1277 | + | |
| 1278 | + | |
| 1279 | + | |
| 1280 | + | |
| 1281 | + | |
| 1282 | + | |
| 1283 | + | |
1287 | 1284 | | |
1288 | 1285 | | |
1289 | 1286 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
60 | | - | |
61 | | - | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
62 | 71 | | |
63 | 72 | | |
64 | 73 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
| 36 | + | |
36 | 37 | | |
37 | 38 | | |
38 | 39 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
92 | 92 | | |
93 | 93 | | |
94 | 94 | | |
95 | | - | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
96 | 100 | | |
97 | 101 | | |
98 | | - | |
| 102 | + | |
| 103 | + | |
99 | 104 | | |
100 | 105 | | |
101 | 106 | | |
102 | 107 | | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
103 | 116 | | |
104 | 117 | | |
105 | 118 | | |
| |||
0 commit comments