Development record
2026-08-22 — Bug-hunt round 7, branch bughunt-b/round-7. Baseline (make preflight
decisiondcn-2608220000000001
2026-08-22 — Bug-hunt round 7, branch bughunt-b/round-7. Baseline (make preflight
- gofmt -l .) green before any change and on the final tree; started from main at 9db1316. Five parallel Opus 5 hunters swept the four dimensions; each candidate was adversarially refuted by an independent Opus 5 subagent before capture. Findings: 8 substantive (2 major, 6 minor) + 8 nitpick confirmed, 7 refuted/prior-art; nitpicks-only: no. Fixed and resolved this round, each behaviour-changing code fix carrying a test watched fail before the change and pass after (the repolint size TOCTOU excepted — closed by construction, its boundary test guards the cap not the race): iss-2608220131352917 the guard tokenizer read a leading & as a background/&& operator, so a glued or spaced &>/&>> both-streams redirection split the simple command and dropped its dangerous flag out of command position — a silent allow on every blocker-tier entry; now recognised as a redirection before the list split, target dropped, fd digit kept. iss-2608220134344680 the frontmatter scanners anchored on line 0 and TrimSpace does not strip a UTF-8 BOM, so a BOM- or multi-line-comment-led record slipped the no_git_metadata blocker and the record_schema gates; shared frontmatter.TrimBOM plus a stateful multi-line-comment skip in the lint and glossary scanners. iss-2608220142158516 abcd update and abcd history leaked the absolute home root into their success/refusal envelopes (text and --json), including the plugin-session refusal the plugin relays into agent chat; redacted to ~ via a new fsutil.RedactHome at the render boundary. iss-2608220136593438 ahoy tested .git for dir-ness, so a linked worktree or submodule (gitfile .git) was misclassified as an unmanaged folder and ahoy install exited 0 aborted with a wrong reason; now tests existence, the last isDir(.git) holdout after iss-72. iss-2608220136597127 ahoy --json serialised a never-computed all-false guard object for an unmanaged folder; the field is now a pointer omitted for a folder, matching the Banlist sibling. iss-2608220144233519 the repolint privacy scanner read exactly the cap, so a file grown past it during the read was scanned as a truncated prefix and reported clean; now cap+1 with the grown file routed to the not-scanned path. iss-2608220147106835 spec/intent/memory/capture emitted bare null for an empty --json collection; the constructors seed them non-nil so an empty store marshals []. iss-2608220150151397 the mental-model brief claimed zero live bundles while four planned intents declare a bundle-member of spc-83 (residual of iss-123's README-only fix). Nitpicks: iss-2608220142154022 update.Plan had no default so an unknown target kind fell through to swap (now fail-closed); iss-2608220145356167 docs lint returned an engine fault as exit 1 not 2; iss-2608220148289898 abcd adr-N routed by a fixed %04d filename prefix, missing a differently-padded ADR file (spc-26 amended); iss-2608220149008905 the pull_request_target checkout guard external-review.yml states in prose was unarmed (now a workflow-walking test); plus the doc corrections iss-2608220150157497 (adr-22/26 links), iss-2608220150154972 (version.md install_mode), iss-2608220150152332 (CI classifier standdown), iss-2608220150152535 (docs requirements transitives). Refuted / prior art (kept out of the ledger): the abcd-cli name in commands/launch.md (the live internal project name, record-lint-prescribed); the spc-3 predecessor-store collision in intents/README (a new instance of open iss-239, carried there); the .abcd/work roster intake.md/attribution-rewrite omission (round 4's highlight-not-inventory doctrine still governs); the CI gitleaks same-origin checksum (adr-46's accepted trust bar, residual tracked as open iss-379); the go.mod vs setup-go patch coupling (GOTOOLCHAIN=auto self-corrects); the Makefile CRLF eol pin (GNU make strips the CR on POSIX); and the wrangler/mkdocs docs-site deploy pin (dashboard-side, not an in-repo mechanism). Model routing: orchestration on Claude Fable 5; five parallel hunters and twenty-two per-finding adversarial refuters on Claude Opus 5; the dual pre-merge review runs one reviewer as Claude Opus 5 and one as Claude Fable 5.
Frontmatter
| id | dcn-2608220000000001 |
|---|---|
| type | decision |
| date | 2026-08-22 |
.abcd/work/decisions/dcn-2608220000000001-bug-hunt-round-7-branch-bughunt-b.md
open on GitHub ↗ · commit history ↗
mentions25
- adr-22Bundled dependencies become pluggable adapters over a native default
- adr-46Persisting the hook binary never weakens the verification posture — every promotion re-verifies, and SessionEnd performs no network work
- iss-72core.Status.IsGitRepo=false in a linked git worktree or submodule: isDir(.git) requires a directory but .git is a regular gitfile (gitlink) in worktrees/submodules, so a genuine checkout reports not-a-git-repo (core.go:47, C1). Detector: an exists-not-isDir check for .git with a worktree fixture. Corpus: C1.
- iss-123Round-4 residue of the brief reconciliation: nine precision-grade discrepancies remain after three fix rounds (102 -> 52 -> 25 -> 9), all empirically verified by the checkers and none of the judgement-flip-flop class. Examples: bare ahoy prints a next-step line only for unmanaged kinds but the criterion says any folder kind; embark's manifest verification excludes the post-pack synthesis layer while the chapter claims blanket tamper-evidence; a live bundle (spc-83-operator-surfaces, itd-20+itd-24) contradicts the no-live-bundles claim; the persona_registry lint checks personas.json, not the markdown page named as SSOT; history capture idempotency keys on (sha256, session, kind), not content hash alone. Full findings in the local scratch tier (2026-07-22-iss35-crosscheck-round4 file). The geometric tail also suggests the detector at 22-checker depth has a nonzero noise floor per fresh run — the pass threshold question belongs to iss-122.
- iss-239Dangling pre-rebuild spec ids in the draft corpus: itd-44 (spc-56), itd-51 (spc-33/37), itd-57 (spc-48/60/62), itd-61 (spc-75) cite specs from the numbering adr-21/adr-26 reset (live store is spc-1..spc-22). Worse, intents/README.md and itd-34 assert the itd-44 lineage 'landed as itd-44 (spc-56 thin adoption)' — no such spec or decision-verdict code exists, so the record claims a delivery the tree cannot back.
- iss-379In-process build-provenance (SLSA attestation) verification in abcd update. Trigger: the trust gap the README concedes is worth closing (e.g. after the public flip). itd-130 ships at the same-origin checksums bar (itd-105); the release workflow already attests binaries and checksums.txt, so abcd update could verify the attestation before the swap for a stronger root of trust than same-origin checksums — at the cost of a heavy dependency (sigstore-go). Twin of itd-108's deferred offline signing key (minisign): both close the 'the forge is the identity root' gap that an attestation alone does not.
- iss-2608220131352917guard tokenizer misses the ampersand redirection operators &> and &>> so a glued or spaced &>/dev/null splits the simple command and drops the dangerous flag out of command position, a silent allow on every blocker-tier entry
- iss-2608220134344680record-lint frontmatterOpen and frontmatter.Fields anchor on line 0 and TrimSpace does not strip a UTF-8 BOM, so a BOM-led or multi-line-comment-led record yields an empty field map and slips the no_git_metadata blocker and the record_schema id/supersession gates entirely
- iss-2608220136593438ahoy classify tests .git with isDir, so a linked worktree or submodule (where .git is a gitfile) is misread as unmanaged-folder, every gap detector is skipped, and ahoy install exits 0 aborted with a wrong not-a-git-repository reason; the last isDir(.git) holdout after iss-72
- iss-2608220136597127ahoy DetectionResult.Guard is a value field with a plain json tag but is only assigned for a managed repo, so an unmanaged folder serialises a never-computed all-false GuardHealth (a broken guard) beside plugin_root_status resolved; the Banlist sibling is already a pointer with omitempty for this reason
- iss-2608220142154022update.Plan dispatch switch has no default case, so an unrecognised UpdateTargetKind falls through the return nil permissive branch of a mutating refusal gate to fetch-and-swap, against unrecognized-input-never-writes; UpdateTargetFile is now an explicit case and any other kind a named refusal
- iss-2608220142158516abcd update and abcd history list/show/capture leak absolute home-rooted paths into their success/refusal envelopes (text and --json) because the CLI scrubPaths runs only on the error value; the update refusal detail fires on the documented plugin-session path and the plugin relays target_path into agent chat
- iss-2608220144233519repolint privacy scanner readTrackedFile reads io.LimitReader(f, maxScanBytes) with no cap+1 probe, so a tracked file that grows past the cap between fstat and read is scanned as a truncated prefix and reported clean, bypassing the not-scanned warn channel the same file over-cap at stat would take
- iss-2608220145356167abcd docs lint returns a bare engine error which cli.Run maps to exit 1, the same code as a blocker finding, while abcd lint and record-lint exit 2 for an engine fault; a consumer keying on exit >=2 reads a docs lint that never ran (a config containment refusal) as an ordinary findings-pass
- iss-2608220147106835abcd spec/intent/memory (and capture recent_open) emit bare null for empty --json collections instead of [], contradicting the class-wide invariant history list fixed; a consumer iterating the value (jq .[], an agent following the command doc) errors on null
- iss-2608220148289898record.describeADR routes ADR filenames by a fixed %04d- prefix, so an ADR file with non-four-digit padding (00099-, 77-) is lint-green and citation-resolvable but reported not-found by abcd adr-N; the filename-ordinal residue of iss-2608211140410761 which fixed the frontmatter-id half; spc-26 spec line amended to numeric-ordinal probe
- iss-2608220149008905external-review.yml suppresses zizmor dangerous-triggers on its pull_request_target trigger, so the safety invariant it states in prose (PR code never checked out) is unenforced; a later checkout added to that job passes every zizmor persona, a pwn request; add a Go test asserting no pull_request_target workflow checks out code
- iss-2608220150151397brief 01-product/03-mental-model.md claims the corpus has zero live bundles and points at retired-bundle history, but four planned intents (itd-20/24/63/69) declare bundle-member of spc-83-operator-surfaces and intents/README heads it Active bundles; residual of resolved iss-123 which fixed the README half only
- iss-2608220150152332AGENTS.md and CONTRIBUTING.md describe the CI check job as unconditional but since the inert-path classifier landed a docs/.abcd-only pull request stands the macOS leg, race lane, zizmor, govulncheck and smoke down; the merge-queue run still runs the full set so nothing ships under-verified
- iss-2608220150152535docs/requirements.txt line 1 claims pinned so every site build renders docs identically but only mkdocs-material is pinned; every transitive dependency floats from PyPI at build time so two builds of one commit can render differently and dependabots pip cooldown never sees the transitives
- iss-2608220150154972commands/version.md tells the agent to report install_mode from the JSON but the field is json omitempty and absent whenever no abcd-owned PATH entry is resolvable (fresh install, foreign/dangling entry, unresolved plugin root), so an agent following it reports a missing field or invents one
- iss-2608220150157497principles/sota-per-intent.md links `[adr-22](../decisions)` and `[adr-26](../decisions)` to the decisions directory instead of the ADR files; links_resolve passes because the directory exists; the only two ID-labelled links in the record whose target does not match the id
- iss-2608271702469341intake.md graduates to the durable record when the contribute-triage verb ships: .abcd/work/intake.md stays in the working tier today as the live S1-S6 external-contribution runbook (the 2026-08-27 structural review re-adjudicated its placement: live operating state, cited in force by [dcn-2608190000000002](../../decisions/dcn-2608190000000002-review-agents-keep-their-bash-grant.md) and filed-against by ledger records), but its own header declares it the script-first MVP rung that a future 'abcd contribute triage' verb absorbs. When that verb ships, the protocol content is no longer working state and the document's home moves up: promote the durable protocol description into .abcd/development/ (brief surface or discipline record) in a change that says why, leave or redirect the working-tier path for in-flight citers (iss-276 and resolved iss-2608261533173466 cite found_at .abcd/work/intake.md; [dcn-2608190000000002](../../decisions/dcn-2608190000000002-review-agents-keep-their-bash-grant.md) cites its S4 rule), and record the supersession of the highlight-not-inventory adjudication ([dcn-2608210000000001](../../decisions/dcn-2608210000000001-bug-hunt-round-4-branch-bughunt-b.md), [dcn-2608220000000001](../../decisions/dcn-2608220000000001-bug-hunt-round-7-branch-bughunt-b.md)) for this file. Trigger is the verb shipping, not a date.
- spc-3lifeboat-coverage-experiment
- spc-26type-the-id-get-your-next-move-abcd-id-dispatches-on-a-recor