fix: bughunt round 2 — eleven records fixed, iss-201 closed, iss-358 wontfixed on review evidence - #386
Conversation
Twelve substantive findings and one grouped nitpick batch, each adversarially refuted before capture. iss-355 is recorded, not fixed (required-check workflow logic, unverifiable without a live Actions run); the rest are fixed in the commits that follow. Assisted-by: Claude:claude-fable-5
lanHostRe and deviceHostRe kept the trailing ASCII word boundary the token patterns and ipv4Re/macRe already retired, so a hostname followed by '_' or an alnum silently dropped the whole match. The boundary's one useful side effect — suppressing a truncated longer label — moves to a positional truncatedLabel skip. Watched failing: three suffixed hostnames scanned to zero findings before the change. Assisted-by: Claude:claude-fable-5
Matches is ordered blockers, warns, synthetics — not winner-first — so when a synthetic block wins over registry warns, rendering Matches[1:] dropped the matched warn and echoed the winner twice. Watched failing: git clean chained with an env -S payload reported only the winner. Assisted-by: Claude:claude-fable-5
readTranscript was the one guarded read left off fsutil.ReadGuarded: no O_NOFOLLOW, so a symlink at the hook-supplied transcript_path was followed and the target's bytes persisted to the history store, and no cap+1 probe, so a file crossing the cap between stat and read stored a silently truncated prefix against spc-4's refuse-whole invariant. Watched failing: a symlinked transcript was stored as a record. Assisted-by: Claude:claude-fable-5
readHookInput and the guard hook read exactly the cap, so an over-cap payload was truncated into a severed prefix and misreported as malformed host JSON — the guard fail-open blamed the host and the prompt router injected nothing with the wrong reason. Both now read cap+1 and name the cap, mirroring guardCandidate. Watched failing: an over-cap prompt-router payload reported 'unexpected end of JSON input'. Assisted-by: Claude:claude-fable-5
…iss-345) net/url preserves raw non-ASCII in a Location query and encoding/json escapes neither C1 nor bidi/zero-width runes, so a hostile redirect could land a terminal escape or Trojan-Source override raw in docs cite refresh --json and in the committed citations baseline. The fetch boundary now percent-encodes every rune the terminal sanitizer would mask (lossless, so the record round-trips), the baseline validator refuses such a final_url from any producer, and the false JSON-needs-no- sanitising claims in termsafe and the lifeboat coverage report are corrected and pinned by a test. Watched failing: a redirect target carrying U+009B/U+202E/U+200B was recorded raw. Assisted-by: Claude:claude-fable-5
…-356) Go's IsPrivate covers RFC 1918 + ULA only, so CGNAT 100.64/10 — live internal addressing on Tailscale tailnets and carrier/cloud networks — passed every predicate, contradicting the package's own promise that internal services are out of reach. The RFC 6890 table closes the class in one place, and the NAT64/6to4 unwrap re-checks it for free. Watched failing: eight reserved addresses were fetchable. Assisted-by: Claude:claude-fable-5
privacy-hygiene silently skipped any tracked file over the 4 MiB cap and then reported the repo conforming — content nobody scanned, the didn't-scan-reported-clean shape the engine contract at repolint.go forbids. A skipped file that looks textual (first-8-KiB NUL probe, the same heuristic isBinary uses) now yields a not-scanned warn naming the file; binary assets stay quiet. Watched failing: the fixed test failed against the stashed pre-change rule. Assisted-by: Claude:claude-fable-5
reCommitSha stopped at 40 hex chars, refusing a SHA-256 repo's rev-parse
output against spc-25's own never-refuse-a-legitimate-resolution
rationale; the sibling receipt gate already accepts {7,64}. The flag
help, plugin command page, spec, and intent record move with the
surface. Watched failing: a 64-hex --commit was refused as malformed.
Assisted-by: Claude:claude-fable-5
Three help surfaces understated the binary: the root command hid the shipped record-id dispatch (Usage said bare 'abcd' takes no positional and --help never mentioned it — the sole command in the tree with an undeclared positional), launch's Short read as if --dry-run were optional while the bare form refuses, and the guard check gap list omitted the disclosed backtick limit that internal/README claims is stated there (iss-148 stays the tracking record for the parser gap itself). The generated reference moves with the tree. Assisted-by: Claude:claude-fable-5
…ons (iss-354) TestSelfScaffoldParity gates release.yml and auto-release.yml through the templates, but no test read ci.yml, whose three pins float free — the d594511 incident (CI green on a patched toolchain while the release built on the unpatched one) would recur silently on the next bump. Verified by mutation: a ci.yml pin moved to 1.25.12 fails the test. Assisted-by: Claude:claude-fable-5
…d pins (iss-356) dependabot declared gomod and github-actions only, so docs/requirements.txt had no update path at all, and the gitleaks env-var version and zizmor run-block digest aged with nothing to say so. The pip ecosystem gets the same seven-day cooldown; the two scanner pins now state in place that their bumps are a deliberate maintainer act rather than an oversight. Assisted-by: Claude:claude-fable-5
The publish step's comment claimed --verify-tag checks the tag points at the checked-out commit; gh checks remote existence only, exactly as the contract note at the top of the same file states. The rehearsal header said a dispatch runs ONLY the rehearsal job (verify runs too, having no event gate) and its final echo claimed the full gate armed when the job runs the resolution plus record-lint and a build. Both files move together under the byte-parity test. Assisted-by: Claude:claude-fable-5
…iss-349) The Guardrails row listed the pre-tool-use guard among deterministic, fail-closed gates — the opposite of adr-42's fail-open-loud mistake filter, asserted on the one page written for an evaluator and in the same sentence that cites enforcement-claims-are-facts. The lint, receipt, and pre-commit gates keep their fail-closed billing; the guard now carries its own. Assisted-by: Claude:claude-fable-5
The README said bare 'abcd ahoy' reports the off-PATH state as a named gap after the copy one-liner install. It does not: with no plugin root the PATH detector returns nothing, and with one the copied binary reads as a foreign file with a resolve-manually hint. The verb that actually prints the export line for a fresh user is ahoy install's reachability note, so the sentence now names it. Assisted-by: Claude:claude-fable-5
Six sites enumerated 'the three disciplines (itd-1, itd-5, itd-37)' while intents/disciplines/ holds six active disciplines — dropping the persona-registry, judge-calibration, and intent-decomposition gates from the mental model, scope, roadmap dashboard, and phase-0 record. The roadmap's own banner above the stale row says counts are derived from the filesystem, never hand-kept, so the sites now point at the directory; the genuinely phase-scoped mentions say so. Assisted-by: Claude:claude-fable-5
Two brief chapters and the itd-37 discipline cited ADRs at development/research/adr/ — a directory that has never existed and a layout adr-30 explicitly rejected; all forty ADRs live under decisions/adrs/. The three citations were backticked code spans, which links_resolve never reads, so they are now markdown links the blocker rule gates. adr-30's own mention of the rejected path stays: that one is intended narration. Assisted-by: Claude:claude-fable-5
notes/README.md prescribed <topic>-<kind>.md with no date slot while adr-30, development/README.md, the record-lint successor path, and the ideate writer all say dated — and 29 of the 41 notes on disk are. The outlier rule yields: the stated form is now the dated one, the kind vocabulary demotes to an optional suffix, and the pre-adr-30 undated files are declared legacy rather than renamed. Assisted-by: Claude:claude-fable-5
…e pointers (iss-356) Four pointer lines still enumerated five of the nine 05-internals chapters (the tree line and the chapter index stay as the single rosters); phase-6 carried three withdrawn-claims blocks and 06-lint a formerly-pending parenthetical, all narrating prior text against the record's present-tense-only rule while every fact they carried already stands elsewhere; 05-internals cited a ledger entry that has never existed; and 03-evidence named the adr-32-retired activity record where its sibling chapter already says the working record. The bug-hunting playbook's open-frontier item 0 is struck: iss-101 fixed it. Assisted-by: Claude:claude-fable-5
Assisted-by: Claude:claude-fable-5
The corrected sentence spelled out 'abcd ahoy install', which the bootstrap test rightly rejects in the README: the reader it addresses does not have abcd on PATH yet. 'The installer' is the form the surrounding prose already uses. Assisted-by: Claude:claude-fable-5
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
abcd | 7aaef13 | Aug 20 2026, 08:54 AM |
…vidence The pre-merge adversarial review proved the loosened lanHostRe flags ordinary snake_case selectors — stream.local_addr (Rust), args.local_rank (Python), storage.local_key (JS) — and Stage-1 redaction rewrites every finding into the history store, the one irreversible artefact abcd writes. That trade (a warn-tier miss on an underscore-suffixed hostname against corrupting real code in transcripts) goes the other way, so the change is reverted, iss-344 moves to wontfix with the measured cost, and TestSnakeCaseSelectorsStayQuiet pins the decision. Assisted-by: Claude:claude-fable-5
encodeHiddenRunes iterated runes, so an invalid UTF-8 byte in a redirect query was rewritten to U+FFFD instead of percent-encoded — lossy exactly where the comment promised lossless. Byte-wise decoding keeps the raw byte as %XX; clean and already-encoded inputs are untouched, pinned by tests. Assisted-by: Claude:claude-fable-5
…357) The urlguard test's non-reserved IP literals carry the audit waivers the adjacent fixture already uses (the repo's own lint is back at its main baseline), and the reserved-range table says why the RFC 5737 documentation blocks stay reachable. The repolint oversize probe warns instead of staying silent when it cannot read, and its message derives the cap. The hook over-cap message says the payload was discarded unparsed rather than 'nothing was read'. The rehearsal comment reflows to the block's width in the workflow and its template. iss-355's citations name the exact assert line and the ruleset mirror's path. iss-357 records the memory-ingest FinalURL sibling site the cite fix did not reach. The round log reflects the iss-344 reversal. Assisted-by: Claude:claude-fable-5
A clean whole-tree sweep is evidence about that tree, not the input space — the sentence claiming no-regression verification is exactly what failed to see the snake_case class, and the record should say so. Assisted-by: Claude:claude-fable-5
Resolves the guard help gap list (both new disclosure lines kept) and regenerates the CLI reference with the repo's own tooling. Assisted-by: Claude:claude-fable-5
…-359) Main landed its own iss-344 and iss-345 while this round was in flight — the concurrent-mint collision the ledger's new iss-344 itself records — so this round's two records are re-minted with fresh captures and their statuses reapplied (iss-358 wontfix, iss-359 resolved); every reference in code, tests, CHANGELOG, and the round log moves with them. Ids are never hand-edited. Assisted-by: Claude:claude-fable-5
fd068ee to
7aaef13
Compare
Merge gate — dual independent review + CICI on head Reviewer A (Claude Fable 5): SHIP. Mutation-tested every new test by reverting each fix (none vacuous), verified the 51-entry committed baseline against the new validate rung, confirmed the ledger's capture-before-fix ordering and iss-355's evidence against the live workflows. Two concerns, both acted on: Reviewer B (Claude Opus 5): NO-SHIP — two blockers, both accepted as correct:
Delta verification (independent third reviewer): BLOCKERS-CLOSED — the revert is byte-exact against the pattern's prior state, the selectors round-trip unmodified through Also handled mid-gate: the merge conflict after main absorbed hunt A's reconciliation (resolved by keeping both new guard-help disclosure lines and regenerating the CLI reference with the repo's tooling), the concurrent-mint id collision (iss-344/345 re-minted as iss-358/359, references moved, ids never hand-edited), and the commit-identity correction the attribution gate's own remedy prescribes. Auto-merge has been enabled by the maintainer; per the loop protocol this round performs no merge action itself. Assisted-by: Claude:claude-fable-5 Generated by Claude Code |
Autonomous all-dimensions bug-hunt, round 2 (branch
bughunt-b/round-2). Baseline (make preflight+gofmt -l .) green before any change and green again on the final merged tree (68 packages tested). Every finding survived an independent adversarial refutation before capture; every behaviour change has a test watched failing first. All findings live in the committed ledger asiss-Nrecords (never as forge issues), captured before the fixes per the record-first rule. Two ids minted this round collided with a parallel hunt's mints that landed on main mid-round; they are re-minted as iss-358/iss-359 per the collision protocol (fresh capture, ids never hand-edited).Confirmed substantive, fixed
internal/core/cite/fetch.go: a redirect-controlledFinalURLcarried C1/bidi/zero-width runes raw intodocs cite refresh --jsonand the committed citations baseline (encoding/jsonescapes only C0 + U+2028/9 — measured and pinned). The fetch boundary now percent-encodes them byte-wise (lossless on invalid UTF-8 too),Baseline.validaterefuses them from any producer, and the false JSON-needs-no-sanitising claims intermsafe.goandlifeboat/coverage.goare corrected. Tests:TestRedirectTargetControlRunesNotRecordedRaw,TestEncodeHiddenRunesKeepsInvalidBytes,TestBaselineRefusesControlRunesInFinalURL,TestJSONLeavesC1AndBidiRaw.internal/surface/cli/guard.go: the "also matched" line renderedMatches[1:], butMatchesis blockers-warns-synthetics ordered, so a synthetic block over registry warns dropped the matched warn and echoed the winner twice. Display now selects by id; coreMatchesuntouched (the corpus warn-rate gate reads it). Test:TestGuardCheckAlsoMatchedListsTheNonWinners.readTranscriptwas the one guarded read left offfsutil.ReadGuarded— noO_NOFOLLOW(a symlinkedtranscript_pathwas followed and persisted; reproduced) and no cap+1 probe (a growing file stored silently truncated against spc-4's refuse-whole invariant). Test:TestHookSessionEndRefusesSymlinkedTranscript.readHookInputand the guard hook read exactly the cap, misreporting an over-cap payload as malformed host JSON; both now read cap+1 and name the cap, mirroringguardCandidate. Tests:TestHookPromptRouterReportsAnOverCapPayload(+ at-cap boundary case),TestGuardHookReportsAnOverCapPayload.Usage: abcd, noLong); the sole command in the tree with an undeclared positional.Use/Longnow state it; reference regenerated; spc-26 golden tests green.docs/reference/terminology.mdlisted the pre-tool-use shell guard among "deterministic, fail-closed gates" — the opposite of adr-42's fail-open-loud mistake filter, on the page written for an evaluator, in the sentence citing enforcement claims are facts.abcd ahoyreports the off-PATH gap after the copy one-liner install; reproduced both branches (no plugin root → no PATH gap at all; resolvable root →symlink.foreign, resolve-manually). The sentence now names the installer's reachability note, the thing that actually prints the fix.intents/disciplines/holds six; the sites now point at the directory, and the genuinely phase-scoped mentions say so.development/research/adr/(never existed; adr-30 rejected the layout), invisible tolinks_resolveas code spans; repointed as markdown links the blocker rule gates. adr-30's own rejected-alternative mention untouched.research/notes/README.mdprescribed undated<topic>-<kind>naming against adr-30,development/README.md, the record-lint successor path, and the ideate writer (29 of 41 notes are dated); the outlier section rewritten, nothing renamed.go-versionpins toAbcdSubstitutions().GoVersion(the d594511→8b70d70 drift shipped CI-green binaries on an unpatched toolchain).TestWorkflowGoVersionsMatchSubstitutionsadded, verified by mutation.--commit, launch--dry-run requiredShort,release.yml--verify-tag/rehearsal wording (+ scaffold templates, byte parity held), dependabot pip ecosystem for/docs, five-of-nine chapter rosters, phase-6/06-lint change-narration blocks, phantom ledger citation, retired activity-record noun.Built, reviewed, reverted — wontfix (iss-358)
The scanner hostname trailing-
\bfix (lanHostRe/deviceHostRe, the residual of hunt A's iss-307 sweep) was implemented with a watched-fail test and then reverted on this PR's own pre-merge adversarial review, which proved the loosened pattern flags ordinary snake_case selectors —stream.local_addr(Rust),args.local_rank(Python),storage.local_key(JS) — and Stage-1 redaction rewrites every finding into the history store, the one irreversible artefact abcd writes. The warn-tier miss on an underscore-suffixed hostname is the accepted cost;TestSnakeCaseSelectorsStayQuietpins the decision, and the record annotates why the original whole-tree no-regression sweep was insufficient evidence.Confirmed substantive, recorded not fixed (stays open)
github.shathe batch tip, so auto-release tags the wrong commit andrelease.yml'sHEAD^2^receipt derivation resolves an unrelated PR's commit whenever the release roll is not the last entry in its batch; the ancestry guard passes and the run wedges after the immutable tag — iss-326's outcome by a route the itd-93 pre-merge check cannot close (batch composition is the queue's decision). Proven from this repo's own merge history and auto-release run numbering. Required-check workflow logic, unverifiable without a live Actions run (the iss-301/302 precedent), so the record carries two proposed fixes: a receipts-presence-searching derivation (fail-closed before any tag, mirrored into a batch-simulating rehearsal), ormax_entries_to_merge: 1.FinalURLverbatim into stored material origins: the iss-359 class one door over, recorded for a future round.Considered and rejected (refuted candidates)
external-reviewrequired check wedging underpull_request_target— refuted empirically: check runs attach to the PR head sha. Incidental observation for iss-302:role_ofreturnedadminunder read-only perms, so its severity clause about 403s did not reproduce.docs/on every PR; the inert classifier and dashboard build are recorded decisions (iss-234,wrangler.jsonc).resolvedSHA64-hex widening — prior art iss-206, and GitHub has no SHA-256 object format on this value's path..PHONYcompleteness fix — inert for the targets named.Dedup boundary
iss-307, iss-311, iss-317, iss-319, iss-321, iss-325 (hunt A's reconciliation) and the v0.6.0 wedge (iss-326/327, dispositioned into itd-93) were already held by the sibling loop or the maintainer and were not re-reported; iss-358 is the declared residual of iss-307's sweep, and iss-355 is the batch-composition route iss-326/327 do not cover.
Round 2 of the bug-hunt loop (state: #368). Gates:
make preflight+gofmt -l .+scripts/check-attribution.shgreen locally on the merged tree; CHANGELOG entries cite theiriss-Nhandles; round summary appended to.abcd/work/DECISIONS.md.Assisted-by: Claude:claude-fable-5