Development record
Reported from a downstream repository using abcd: `abcd capture "<text>"` mints
Reported from a downstream repository using abcd: abcd capture "<text>" mints
ids like iss-2609120433096312 while every pre-existing record there uses the
short form (iss-188, iss-227). That ledger now holds two id shapes — about
48 records in the old shape and 8 in the new — and the record-dispatch
documentation says iss-N.
What is and is not wrong here
The two shapes are not a defect. Minting is forward-only by design and nothing renumbers an existing record, because an id is a citation. A ledger part-way through adoption holds both shapes and that is what adoption looks like. This repository is in the same state for every family: 301 sequential ids against 35 minted across intents, specs and ADRs, and the issue store is overwhelmingly minted.
The documentation is wrong, and that is the reportable part. Surfaces that
spell the id as iss-N tell a reader the short form is the form, when what they
will actually be handed is a sixteen-digit stamp. A downstream adopter reads the
spelling as a contract.
This is the same class as iss-2609111002410678 — AGENTS.md asserting ADRs
keep a hand-numbered ordinal when decide has minted them since the 2026-09-01
ruling. Both are surfaces describing the pre-mint world. They should be swept
together rather than one at a time, since the sweep is the same grep and the
same judgement about which <family>-N spellings are illustrative and which are
claims.
The care the sweep needs
iss-N is not uniformly wrong. It is the right spelling in a placeholder
(abcd capture resolve <iss-N> …), where N stands for whatever the id is, and
the wrong spelling in a claim about what ids look like. A blanket
search-and-replace would damage the first to fix the second.
The honest fix is to say the shape once, where a reader can find it, and keep
placeholders as placeholders: an id is iss-, itd-, spc- or adr- followed
by digits, either a short ordinal from before the mint or a sixteen-digit stamp
from after it, and both resolve.
Acceptance
- Given a reader of the record-dispatch surface, when they look up what a record id is, then they are told both shapes resolve and neither is being migrated away.
- Given the surfaces that spell an id, when the sweep runs, then
placeholders keep their
<iss-N>form and claims about the shape are corrected.
Grounds
- pursued: a reader of the dispatch surface learns both shapes resolve; a surface claiming ids are short ordinals would show it wrong
Frontmatter
| id | iss-2609120452369809 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | minor |
.abcd/work/issues/resolved/iss-2609120452369809-the-record-dispatch-documentation-says.md
open on GitHub ↗ · commit history ↗
mentions3
- iss-188scanner adjacency recovery misses greedy over-consumption in open-ended-quantifier secret patterns. iss-185's fix (scanAllPatterns, scanner.go) recovers a second token that immediately abuts a first with no separator ONLY when the first pattern is fixed-length (its match always ends exactly where the second token begins, so probing every pattern's boundary-free variant at that one byte offset is sufficient). A pattern whose quantifier is open-ended (e.g. `\bghp_[A-Za-z0-9]{36,}`, `\bsk-ant-[A-Za-z0-9_-]{40,}`, `\bsk_live_[A-Za-z0-9]{20,}`) instead greedily consumes as many class-matching bytes as are available, including a following token's own leading bytes when those happen to fall inside the same character class — shifting the true junction earlier than the reported match end, before scanAllPatterns' adjacency probe ever runs. Concrete repro: `"ghp_" + strings.Repeat("a",36) + "ghp_" + strings.Repeat("b",36)` — the first `ghp_` pattern's `{36,}` class run consumes the 36 a's AND the second token's leading "ghp" (all alnum), stopping only at the second token's `_` (not in `[A-Za-z0-9]`); ScanText reports exactly ONE finding covering `ghp_aaaa...aaaghp`, and the second token's `_bbbb...bbb` tail survives Redact completely raw with the fail-closed residual re-scan reporting clean — the same "can never contain a live secret" violation iss-185 was filed for, just for the open-ended-quantifier pattern family instead of the fixed-length one. Distinct from iss-185 (closed): that fix's mechanism (probe every pattern at a match's reported end) is necessary but not sufficient — this needs a bounded backward search (shrink the greedy match's end toward its start, checking at each candidate cut whether the shortened match is still valid for its own pattern AND some pattern's boundary-free variant matches there) without becoming O(line length) per match, since these patterns' matches can legitimately be very long (base64 blobs, minified files) and an unbounded backward scan across every byte would be a new resource-exhaustion risk on exactly the kind of large single-line input a secret scanner must handle. Surfaced by an adversarial pre-PR security review of iss-185's fix (bug-hunt loop round 3), not by the round's own hunt; not fixed in that PR to keep its diff scoped to the exact bug iss-185 named and to avoid shipping an unbounded-cost search under review pressure. Affected pattern families: github_pat (ghp_/ghs_/gho_/ghu_/ghr_), anthropic, openai_project, openai_svcacct, stripe_live, stripe_test, slack, jwt_shaped — every bundled pattern with an open (`{n,}`) rather than fixed (`{n}`) quantifier.
- iss-227installDevShim silently swallows failures: the os.Remove, MkdirAll, and WriteFileAtomic error paths are bare returns (internal/core/ahoy/apply.go, installDevShim), so a failed shim write yields status=partial or clean with no note explaining what was not done or why; only the success path calls a.note
- iss-2609111002410678`CLAUDE.md` tells every session that ADRs are the one record family still