Skip to content

Bug-hunt round 9: verbs agree with the gates they mint for, and two dead detector legs come alive - #519

Merged
REPPL merged 21 commits into
mainfrom
bughunt-b/round-9
Aug 26, 2026
Merged

Bug-hunt round 9: verbs agree with the gates they mint for, and two dead detector legs come alive#519
REPPL merged 21 commits into
mainfrom
bughunt-b/round-9

Conversation

@REPPL

@REPPL REPPL commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

What

Bug-hunt round 9 (bughunt-b). Baseline green before any change (make preflight exit 0 with the full suite executed — 77 package results including the race lane — and gofmt -l . empty) after unshallowing this environment's clone. Five parallel hunters swept the four dimensions; every candidate went through an independent adversarial refuter. 21 candidates in; 12 substantive and 5 nitpicks confirmed and fixed, 4 refuted or prior art, 2 recorded open.

Confirmed and fixed — substantive

  1. capture --blocked-by minted records its own blocker refuses (internal/core/capture/workflow.go, iss-2608261437046287). Shape-only validation wrote a dangling cross-reference with exit 0; record_schema (blocker) then wedged preflight on the tool's own output — the invariant workflow.go states verbatim ("the tool's own resolve path can never mint a record its own blocker rejects"), swept for --intent/--spec and missed here. Capture now probes each target through the shared existence probe before reserving a path; a resolved/wontfix target stays legal (watched-fail test both directions).
  2. Intent verbs refused lint-green spec_id spellings (internal/core/intent/lifecycle.go:306, ready.go:166, audit.go:182, internal/core/spec/spec.go:82; iss-2608261437047643). Lint matches spec references by number and the store's reservation check tolerates spc-N-slug/spc-0N by recorded design (iss-68 P5), but Reconcile compared raw strings and Lookup compared exactly — a record every gate passes bricked intent ready, spec close, intent link and the audit emit. The verbs now resolve by number via shared SameNum/HasNum helpers; the strict grammar still guards CLI arguments; unparseable values match nothing.
  3. RD002's rename leg was dead and its failures read as clean (scripts/check-reviews.sh:53, iss-2608261437044382). git log --diff-filter=MR under a pathspec limited to the new path can never form a rename pair, so renaming a review file in the same commit as a full rewrite passed the append-only gate — reproduced; and the stderr swallow turned any git failure into a clean pass. One --name-status --diff-filter=DMRT history pass over the reviews root now refuses every modify/rename/delete of a dated review file (closing the previously deferred deletion case with the same line), git failures exit 2, unborn HEAD reports loudly, and a new scripts/check-reviews-cases.sh pins eight polarities in the Makefile gate and CI — 5 of 8 fail against the old script.
  4. A ledger record missing schema_version vanished while every gate stayed green (internal/core/lint/schema.go, internal/surface/cli/cli.go, iss-2608261437041050). The reader skips such a record (list omits it, the bare status board silently undercounted, dispatch says not found) and record_schema never asserted required properties — a live record hand-edited three days ago sat invisible through two bug-hunt rounds. The issue store now declares its required frontmatter from a new shared internal/core/issueschema leaf both the reader and the lint consume (an import cycle bars exporting from capture — proved empirically), the status board renders the skipped roster, and the live record is repaired in place with id, filename and provenance intact.
  5. cleanLessonProse kept the body the CleanProse consolidation claims it routed (internal/core/lifeboat/graveyard_lessons.go:313, iss-2608261437040578). Byte-identical to the pre-migration cleanSynthProseN: only <!-- neutralised, and before Sanitize — the ordering termsafe/prose.go documents as forging <? from a masked control byte (reproduced). Now delegates to termsafe.CleanProse with the unchanged cap; two watched-fail tests pin the opener class and the ordering.
  6. ScanSpecLinks failed open on the intent tree (internal/core/lint/speclinks.go:106-112, iss-2608261437049307). The intent half swallowed every walk/read error while the spec half ten lines later fails closed — and the fail-open half feeds release.staleIntents, the guard whose own doc says it exists to stop a silent under-bump. Errors now propagate; a missing intents root stays soft (the documented unpopulated-repository state).
  7. RS001's superseded rule survived at three doc sites (README.md:44, AGENTS.md:171, principles/the-record-lands-with-the-act.md:50; iss-2608261437041111). All three taught the leaves-open/ predicate — under which a bare git rm satisfies a Resolves: trailer, the exact hole round 8 closed — and the README's flagship refusal example quoted a message the gate no longer emits.
  8. README miscounted the plugin surface (README.md:110, iss-2608261437042550). "Adds one command" — the brief's surface chapters and the 2026-07-11 sign-off record three host-delegated markdown commands with no Go verb (prepare-this-repo, consult, ingest); the CLI refuses the latter two by name.
  9. The CHANGELOG promised a Breaking section the derived ingest cannot emit (CHANGELOG.md:8, iss-2608261437046261). The section set is closed at six and the refusal is structural, so the promise became unfulfillable at v0.6.0's final manual roll. Header now states the derived truth (the minor bump is the signal); adr-37 carries a dated amendment in the repo's established form; the version-arithmetic comment quotes the amendment.
  10. The site build README asserted a theme control that does not exist (site-src/README.md:11, iss-2608261437047965). The explorer shipped without it and the stylesheet's header was corrected in that same commit; the README clause was left behind, contradicting the file one directory over. Nothing in the tree sets data-theme.
  11. The live disciplines bucket missed the spc-28 rename (five files under .abcd/development/intents/disciplines/, iss-2608261437043634 + iss-2608261437043962). itd-1 instructed running /abcd:intent review — a verb the binary refuses — and five active cross-cutting rules named the removed intent-fidelity-reviewer; the bucket appears in neither spc-28's swept inventory nor its not-swept list, and intent audit refuses non-shipped intents, so no reconciliation event could ever fire. 21 substitutions plus itd-5's task_classes line repointed at the naming registry (iss-265) with intent_audit; stored artefact spellings stay frozen; spc-28's not-swept paragraph records the late sweep; drafts//planned/ stay deliberately unswept under iss-94.
  12. itd-43 specified work against the retired terminology/ tree (.abcd/development/intents/planned/itd-43-...md, iss-2608261437044340). Its problem statement asserted a term file that does not exist, its canonical-term scope bullet and AC2 pointed at a path that cannot come into existence, two open questions were answered by its own spec, and two schema files never existed in the Go rebuild. Reconciled in place (the iss-392 precedent on this same file); the live remainder — the reviews-subsystem rename, correctly blocked on itd-28 — stands untouched.

Confirmed and fixed — nitpicks

  1. CI-account rosters (CONTRIBUTING.md:22, AGENTS.md:77, ci.yml:5-10; iss-2608261437047992): the Linux leg's blocking site-render gate and the record-lint job's RS001–RS003 steps were absent from all three closed enumerations (iss-318 established these sentences as intended-exhaustive).
  2. firstRootSHA unbounded (internal/core/lifeboat/probe.go:557, iss-2608261437048689): the one uncapped git read in the file that documents every-read-is-bounded; now RunLimited + rev-list -n 1, with a source-guard test.
  3. .gitattributes eol gap (iss-2608261437040448): site-src/install.sh.tmpl — the one distributed shell artefact — lacked the text eol=lf pin its five siblings carry; a CRLF render would publish an installer curl | sh cannot run.
  4. itd-24's glossary_terms_used (iss-2608261437046944): listed the nonexistent core/epic (the armed forbidden synonym) beside its successor core/spec; dropped losslessly.
  5. (folded into 11) itd-5's phantom task_classes enum claim.

Considered and rejected (refuted or prior art)

Recorded open (not fixed here)

  • iss-2608261447039180 — an unknown frontmatter property hides a ledger record the same way a missing one did (the second half of the visibility gap; the repaired live record demonstrated it).
  • iss-2608261437042674 — itd-5's scope step still carries the tiebreak its own 2026-07-12 amendment struck.

Verification

make preflight exit 0 on the final merged tree (lint-reviews with the new cases harness, lint-issues, record-lint, docs-lint, site-render, build, vet, test, race — 77 package results, suite executed) and gofmt -l . empty; scripts/check-attribution.sh commits clean over the range. origin/main was merged after #516/#517 landed mid-round; the merge was clean and the full gate re-ran green on the merged tree. Every behaviour change carries a test watched fail before the change and pass after: the dangling --blocked-by refusal, the spec_id spelling tolerance at all four verb sites, the RD002 rename/rewrite/deletion refusals (5 of 8 harness cases fail against the old gate), the required-frontmatter blocker, the status-board skip roster, the CleanProse opener and ordering cases, the gitutil.Run source guard, and the ScanSpecLinks dangling-symlink propagation. Each confirmed finding is captured in the ledger and resolved with fixing-commit provenance in this PR (RS001 checks all 17 trailers).

Assisted-by: Claude:claude-fable-5

REPPL added 21 commits August 26, 2026 14:37
Eighteen records: twelve confirmed substantive findings, five confirmed
nitpicks, and one adjacent defect noticed during refutation (recorded,
not fixed here).

Assisted-by: Claude:claude-fable-5
…tates it

The round-8 gate correction changed RS001 from leaves-open/ to
enters-resolved/-or-wontfix/, and the docs kept the superseded rule: the
README's flagship refusal example quoted a message the gate no longer
emits, and AGENTS.md and the record-lands-with-the-act principle both
taught the predicate under which a bare delete of the open record
satisfies the trailer — the exact hole that correction closed.

Resolves: iss-2608261437041111

Assisted-by: Claude:claude-fable-5
The README counted /abcd:prepare-this-repo as the only plugin-side
command the CLI lacks; /abcd:consult and /abcd:ingest are likewise
host-delegated markdown with no Go verb (the brief's surface chapters
and the 2026-07-11 sign-off both record the three together), so a
reader looking for 'abcd consult' or 'abcd ingest' in the terminal
found only 'unknown command'.

Resolves: iss-2608261437042550

Assisted-by: Claude:claude-fable-5
…a Breaking section

The CHANGELOG header promised each pre-1.0 break a **Breaking** section,
but the derived ingest renders a closed set of six Keep a Changelog
sections and refuses any other, so the promise is unfulfillable for
every cut since v0.6.0's final manual roll. The header now states the
derived truth; adr-37 carries a dated amendment recording that its
interim called-out-under-Breaking clause lapsed when itd-73 landed, and
the version-arithmetic comment quotes the amendment instead of the
lapsed clause.

Resolves: iss-2608261437046261

Assisted-by: Claude:claude-fable-5
The record explorer shipped without the theme control; the stylesheet's
own header was corrected in that change and the build README's claim
that the explorer ships the control was left behind, contradicting the
sibling file one directory over.

Resolves: iss-2608261437047965

Assisted-by: Claude:claude-fable-5
The glossary_terms_used machine field listed both core/epic — a term
file that does not exist, whose name is the armed forbidden synonym —
and its successor core/spec. The dangling entry sat in the blind spot
between forbidden_synonyms (frontmatter out of scope) and record_schema
(field not cross-referenced). core/spec already covers the concept, so
the retired entry is dropped losslessly.

Resolves: iss-2608261437046944

Assisted-by: Claude:claude-fable-5
The five shell pins in .gitattributes missed site-src/install.sh.tmpl —
the one shell artefact the repository distributes: the site build copies
its bytes into the /install.sh users pipe into sh, where a trailing
carriage return on every line is a syntax error. Today every publishing
path runs on a Linux runner, so the exposure is latent; the pin gives
the template the control its siblings already have.

Resolves: iss-2608261437040448

Assisted-by: Claude:claude-fable-5
The spc-28 rename (intent-fidelity-reviewer to intent-auditor, abcd
intent review to abcd intent audit) listed disciplines/ in neither its
swept inventory nor its not-swept exemptions, and the bucket is live
state with no reachable reconciliation event — intent audit refuses a
non-shipped intent, so the audited-when-first-audited promise in these
files could never fire. Twenty-one substitutions across itd-1, itd-37,
itd-81 and itd-84, including the two /abcd:intent review invocations in
itd-1's spc-12 clause; stored artefact spellings (abcd-review markers,
review-<ts> receipt directories, the logbook paths) stay frozen per
spc-28, and drafts/ and planned/ stay unswept under the iss-94
convention. spc-28's not-swept paragraph records the late sweep.

Resolves: iss-2608261437043634

Assisted-by: Claude:claude-fable-5
…udit

The discipline named a machine-readable task_classes enum in
internal/core/lint that has never existed and carried the retired
intent_review token; the naming registry declared itself the source of
truth today (iss-265) and the token flipped to intent_audit with spc-28.
The three remaining intent-auditor name renames in this file ride the
disciplines sweep landing beside this change.

Resolves: iss-2608261437043962

Assisted-by: Claude:claude-fable-5
The planned intent still specified work against the retired
terminology/ tree: it asserted an epic.md term file that does not
exist, pointed its canonical-term scope bullet and acceptance
criterion at a path that cannot come into existence, kept two open
questions its own spec records as answered, and named two schema
files the Go rebuild never carried. Each stale site now states the
delivered truth spc-8 already verified — the term file lives at
brief/glossary/core/spec.md with epic forbidden — while the live
remainder (the reviews-subsystem rename, correctly blocked on
itd-28) stands untouched.

Resolves: iss-2608261437044340

Assisted-by: Claude:claude-fable-5
…ches

A slug-suffixed (spc-9-widget) or zero-padded (spc-09) spec_id is
lint-green — record-lint's spec rules match on the spec number, and the
spec store's reservation check tolerates both spellings by recorded
design — yet Reconcile compared the raw string, spec.Store.Lookup
compared exactly, and the audit path re-applied the strict argument
grammar to the stored field, so a record every gate passes bricked
intent ready, spec close, intent link and the audit emit. Lookup now
resolves by spec number after an exact-match pass, Reconcile compares
via the shared SameNum helper, and the stored spec_id is validated with
HasNum; the strict grammar still guards CLI arguments. An unparseable
value matches nothing, so two malformed ids never compare equal.

Resolves: iss-2608261437047643

Assisted-by: Claude:claude-fable-5
The intent half of the shared traversal swallowed every walk and
per-file read error while the spec half ten lines later fails closed,
and the fail-open half feeds the release cut's stale-intent refusal —
an unreadable intent file contributed zero links, so the guard against
a silent under-bump was disarmed exactly when the tree was damaged. The
walk error is now captured and propagated, keeping the one documented
tolerance: a missing intents root is an unpopulated repository, not a
fault.

Resolves: iss-2608261437049307

Assisted-by: Claude:claude-fable-5
cleanLessonProse kept the pre-migration body its sibling cleanSynthProse
was moved off: it neutralised only the HTML-comment pair, not the full
opener class, and did so before Sanitize — the exact ordering the
canonical primitive documents as unsafe, since a masked control byte
after < forges a processing instruction. The consolidation's own doc
claimed lifeboat routed through CleanProse; this was the boundary the
sweep missed. The body now delegates with the unchanged 4096-byte cap.

Resolves: iss-2608261437040578

Assisted-by: Claude:claude-fable-5
firstRootSHA was the one git read on the untrusted-repo probe path
still using the uncapped runner, against the file's own every-read-is-
bounded contract; a repo with many parentless roots buffers one line
per root. The read now goes through RunLimited with rev-list -n 1,
which returns the same first root, and a source-guard test keeps the
package off the uncapped runner.

Resolves: iss-2608261437048689

Assisted-by: Claude:claude-fable-5
The per-file probe ran git log --diff-filter=MR under a pathspec
limited to the new path, where diffcore can never form a rename pair —
so renaming a review file in the same commit as a rewrite passed the
append-only gate, and the stderr swallow made any git failure read as
a clean corpus. One history pass over the reviews root now flags every
modify, rename, delete or type change of a dated review file, closing
the deferred deletion case with the same line; git failures refuse
with exit 2, an unborn HEAD reports loudly that nothing is covered,
and a cases harness pins all eight polarities in the Makefile gate and
CI. The brief's RD002 row states the delivered rule.

Resolves: iss-2608261437044382

Assisted-by: Claude:claude-fable-5
CONTRIBUTING, AGENTS.md and ci.yml's own header comment all described
the check job's Linux leg as three additions when the blocking
site-render gate is a fourth, and billed the record-lint job as the
reviews-charter check alone when it also runs the issue-resolution
gates. A contributor sizing up CI from these sentences would not expect
a site-render or RS-family refusal from a required check.

Resolves: iss-2608261437047992

Assisted-by: Claude:claude-fable-5
The flag validated its ids for shape only and wrote the record with
exit 0, and record_schema — armed at blocker over the issue store —
then refused the record the tool itself minted, wedging preflight
until a hand edit; the sibling provenance flags already existence-check
through the shared probe before anything is written. Capture now probes
each target across all three buckets before reserving a path, refusing
with nothing written on a miss; blocking on a resolved or wontfixed
target stays legal, and the CLI shape check and strict validator keep
their layers.

Resolves: iss-2608261437046287

Assisted-by: Claude:claude-fable-5
…cker

A committed issue record missing schema_version was invisible to every
capture surface — the list skipped it, the bare status board silently
undercounted, and the dispatch said not found — while record-lint
stayed green, because record_schema asserted structure but never the
reader's required properties. The issue store now declares its required
frontmatter from a shared leaf package both the reader and the lint
consume, so the two cannot drift; the bare status board renders the
skipped roster the way capture list already does; and the one live
record in that state — hand-edited three days ago — gets its
schema_version restored and its unknown status key removed, keeping
its id, filename and provenance intact.

Resolves: iss-2608261437041050

Assisted-by: Claude:claude-fable-5
The round-9 fix gates a missing required property; a record carrying an
unknown property is the second way the reader skips a lint-green file,
which the live record this round repaired had demonstrated.

Assisted-by: Claude:claude-fable-5
Seventeen records move open -> resolved, each stamped with its fixing
commit; the round summary lands in the decision log.

Assisted-by: Claude:claude-fable-5
Assisted-by: Claude:claude-fable-5
@REPPL
REPPL enabled auto-merge August 26, 2026 15:02
@REPPL
REPPL added this pull request to the merge queue Aug 26, 2026

REPPL commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator Author

Merge gate — round 9

CI: all 13 checks green on head f5cbae31check (ubuntu-latest), check (macos-latest), record-lint, attribution, external-review, gitleaks, zizmor, govulncheck, dependency-review, smoke, screenshots, changes (the one cancelled attribution run belongs to the superseded pre-merge head).

Two independent adversarial reviews of the full diff — both SHIP:

  • Reviewer A (Claude Opus 5): SHIP. Re-ran every watched-fail test against the base source and confirmed all twelve fail pre-fix and pass post-fix; verified captureBlockers precedes every side effect, the RD002 tab parsing and heredoc feed, no misfire of the required-fields invariant on the adr/itd/spc stores, gates fully green, ledger discipline (19 captured, 17 resolved in range with reachable stamps), and tight scope. Eleven non-blocking observations, chiefly: the tolerant spec-number regex is looser than its comment's enumerated spellings (tail unanchored — traversal risk traced to ground and closed); Link stamps the caller's spelling rather than the resolved record's id; the cases harness's fail polarity accepts any nonzero exit; a single rename is double-reported; ~8 design-record-only resolutions are stamped impact: fix where internal would keep them out of the public changelog (re-stampable before the next cut).
  • Reviewer B (Claude Fable 5): SHIP. Independently verified exact-match precedence in the number-resolving Lookup, both polarities of the RD002 rewrite on the live history (no over-refusal: reviews/README.md and the receipt directories stay outside the population), reader↔lint agreement in both directions for the required-fields invariant, that CleanProse is a strict superset of the old lesson cleaner, commit/attribution/privacy compliance, and RS001 over all 17 trailers. Twelve non-blocking observations, chiefly: two narrow residual RD002 gaps worth a follow-up capture (an edit hidden inside a merge commit's own diff; a core.quotepath-quoted filename escaping the population regex), and the octopus regression test pins agreement with itself rather than with the old read.

Both reviewers flag one thing for the merger: land this with a merge commit, not squash or rebase — the 17 resolved_by.commit stamps name branch shas, and a history rewrite would turn RS003 red on main for everyone.

Auto-merge is not authorised for this loop; the PR stays open for a human to merge.

Assisted-by: Claude:claude-fable-5


Generated by Claude Code

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 26, 2026
@REPPL
REPPL added this pull request to the merge queue Aug 26, 2026
Merged via the queue into main with commit 527bf70 Aug 26, 2026
12 of 13 checks passed
@REPPL
REPPL deleted the bughunt-b/round-9 branch August 26, 2026 16:23
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