Development record
bughunt round-3 confirmed nitpick batch — zero-padded record-id filenames evade the id-uniqueness lint in all three families; the go-version lockstep test skips .yaml workflows; scripts/*.sh lack the eol=lf pin the repo's own policy states; agents/CHANGELOG announces lifeboat-reviewer 0.1.1 while the prompt carries 0.1.0; five live records still route to the pre-flattening commands/abcd/ and retired docs/reference pages
bughunt round-3 confirmed nitpick batch — zero-padded record-id filenames evade the id-uniqueness lint in all three families; the go-version lockstep test skips .yaml workflows; scripts/*.sh lack the eol=lf pin the repo's own policy states; agents/CHANGELOG announces lifeboat-reviewer 0.1.1 while the prompt carries 0.1.0; five live records still route to the pre-flattening commands/abcd/ and retired docs/reference pages
Evidence
Five confirmed nitpicks, batched per round precedent (iss-356):
- Zero-padded record ids evade the id-uniqueness lint in all three families —
internal/core/lint/lint.go:72(issueIDRe = iss-\d+) keysvalidateIDUnique(:1667) on the raw filename match, soiss-0100-…mdbesideiss-100-…mdyields two keys and no finding (probe: record-lint exit 0 with a plantedid: "iss-100"collision), whilerecordid.fileID(internal/core/recordid/resolve.go:176-178) deliberately normalises the parsed integer andrecord_schema's filename check (internal/core/lint/schema.go:259-285) compares parsed handles — the backstop's hole is exactly the hand-added case its own doc-comment (lint.go:1770-1776) says it exists to close. Consequences narrowed by the refuter: the only liveNewResolverconsumer checks existence only, and the auto-allocator parses integers, so no runtime misbehaviour today. goversion_lockstep_test.go:28skips any workflow not ending.yml, so a.yamlworkflow (orgo-version-file:) escapes the lockstep sweep; thechecked == 0tripwire catches only a total miss..gitattributespinseol=lffor.githooks/*,internal/core/ahoy/defaults/*andhooks/*.shwith a CRLF-shebang rationale, but notscripts/*.sh— on an autocrlf-on checkout the pinned pre-push hook survives and then dies one hop later in the unpinnedscripts/check-reviews.sh(set: pipefail: invalid option name, verified). CI is unaffected (ubuntu runners checkout LF); the dev-machine path is the exposure.agents/CHANGELOG.md:130announceslifeboat-reviewer 0.1.1 (renamed from lifeboat-oracle)while the prompt carriesprompt_version: 0.1.0atagents/lifeboat-reviewer.md:4,53,70and in its canary fixture — the CHANGELOG is the sole outlier (agents/README.md:45's "all 0.1.0" agrees with the files); no consumer pins a value, so the file-side bump is the safe alignment.- Pre-flattening path residue in live (non-historical) records —
commands/abcd/intent.mdand retireddocs/reference/{commands,facilitator,review-schema}.mdnames survive inintents/planned/itd-43(:43,:73),itd-48,itd-2,intents/drafts/itd-85, andspecs/open/spc-6— outside the dcn-2608050000000002 historical carve-out (decisions, shipped intents, closed specs, research, resolved issues).brief/02-constraints/04-naming.md:74citesdocs/reference/review-schema.md, which exists nowhere; that citation needs a maintainer's judgement on the intended target and is recorded, not repointed.
Refuter verdicts: 1-5 CONFIRMED nitpick (item 1 probe-proven; item 3's CI framing and item 5's itd-43-only framing corrected). REFUTED alongside and not carried: the make smoke vacuous-gate claim (a lost build tag runs more, not less; a renamed harness exits 1) and the runbook-parity claim (the launch.md sentence scopes itself to the workflows; runbook byte-parity is impossible by design and its gate list is pinned in both profiles).
Frontmatter
| id | iss-392 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | nitpick |
.abcd/work/issues/resolved/iss-392-bughunt-round-3-confirmed-nitpick-batch.md
open on GitHub ↗ · commit history ↗
mentions15
- dcn-26080500000000022026-08-05 — iss-44 + iss-160 + iss-161 + iss-162 (v0.5.0 item D, one work item): the plugin surface is reconciled with every document describing it, and the reconciliation is a MOVE rather than a rename, because the harness's namespace rule leaves no other answer. Verb files under `commands/abcd/` registered as `/abcd:abcd:<verb>` — a `commands/` subdirectory IS a namespace segment, and this one was named after the plugin, so the prefix appeared twice and every documented `/abcd:<verb>` was an unknown command (iss-161). Flattening into `commands/` is what makes the printed guidance runnable, which is why iss-162 needed no rewrite at all: all eighteen verb files already spelled `/abcd:<verb>` in their self- and cross-references — verified by grep across every one rather than assumed — so the guidance was correct prose about a name nothing registered. On iss-160 the fix direction was settled by evidence, not preference: the loader registers every markdown file under `commands/`, requiring no frontmatter and exempting no name (this repo's own iss-110 records `agents/README.md` registering as an agent; the published plugin reference describes commands purely as flat `.md` files with no ignore list), so NO in-place form keeps a readme unregistered and the only reliable home is outside the auto-discovery root. It goes to the brief's surface registry, which already enumerates the same surface and is already machine-checked against it — folding the doc into the gate that guards it rather than leaving a second enumeration to drift. The cost is one repointed link in a 2026-07-12 plan, paid because `links_resolve` is a blocker and a dangling pointer is not history. iss-44's three instances close with them: `ahoy` gains a section per sub-verb (with `identity-check` carrying an explicit CLI-only note, because its exit code is the point of wiring it into a hook — an absence recorded as a decision is not a gap), every `make build` remedy is replaced by the `go run ./cmd/abcd …` fallback the same paragraphs already carried, and `memory`'s renders stop naming a front door from `internal/core`, which is transport-agnostic by boundary rule: the heading a plain-CLI reader sees must not claim they invoked a plugin command they may not have installed. Detectors first, both watched failing: a surface-parity test over the real command tree and the real directory (flat-and-commands-only, every sub-verb reachable or scoped, no unrunnable remedy), and a string-literal check over `internal/core/memory` — literals, not comments, because prose explaining a surface may name it. `surface_coverage` gains a `bare_command` field so the flat directory's top-level board file is excluded the way README already is, rather than demanding a registry row that cannot exist; hardcoding the name would have been wrong in a lint engine that ships to repos whose plugin is not called abcd. Left alone deliberately: the historical citations of `commands/abcd/` across decisions, shipped intents, closed specs, research and resolved issues — they describe the tree as it was, and rewriting them would trade a true record for a tidy one. Not fixed here, and reported instead: the release-gate manifest's `promptHash` pins prompt text this change edits, by an algorithm nothing in the tree computes or verifies, so it is left untouched rather than guessed at.
- dcn-26082000000000082026-08-20 — Bug-hunt round 3 (hunt B), branch bughunt-b/round-3. Baseline
- dcn-26082100000000012026-08-21 — Bug-hunt round 4, branch bughunt-b/round-4. Baseline (make preflight
- iss-44plugin surface parity: commands/abcd/ahoy.md drives only the bare read-only detect while the CLI registers install, uninstall, doctor, and dry-run — doctor and dry-run are read-only so a keep-mutation-away-from-agents policy cannot explain their absence, and the landing commit claimed the sub-verbs were wired through the plugin command; every plugin command remedy for abcd not on PATH says make build, which cannot produce an on-PATH abcd; plain-CLI abcd memory ask output is headed as a plugin slash command. Detector: a surface-parity check — every CLI sub-verb is reachable from the plugin markdown or carries an explicit scoping note, and remedy text is exercised by a test. Acceptance corpus: the three instances above.
- iss-100disembark has no conventions-tier adapters for constraints/naming, glossary, or internals, so these ground only from an authored abcd brief. Add adapters reading GLOSSARY.md / docs/glossary (naming, glossary) and docs/architecture / package layout (internals) so a repo with conventional docs but no abcd record still grounds them.
- iss-160The installed plugin surface exposes a spurious /abcd:README command: the harness registers every markdown file under commands/ as a slash command, so the folder's own README.md leaks into the command list. The doc needs a home or form that the command loader ignores.
- iss-161Plugin verbs render as /abcd:abcd:<verb> instead of /abcd:<verb>: the verb files live in commands/abcd/, and the harness maps a commands/ subdirectory to a namespace segment, so the directory name duplicates the plugin prefix. Verbs need to live flat under commands/ (or the subdirectory renamed away from the plugin name) for the documented /abcd:<verb> surface.
- iss-162The surface's own guidance is unreachable under the double namespace: ahoy's next-step output (and the command docs) reference /abcd:ahoy and skill-style args like 'install', but the harness registers the verb as /abcd:abcd:ahoy — following the printed instruction yields 'Unknown command: /abcd:ahoy' / 'Args from unknown skill: install'. The rendered guidance must match the registered command names, whatever iss-161 settles on.
- iss-356round-2 fixed nitpick batch: guard check help omits the disclosed backtick limit; urlguard misses CGNAT 100.64/10 and other RFC 6890 ranges; readHookInput misreports an over-cap payload as malformed (iss-201's class at the shared helper); repolint privacy-hygiene silently skips textual files over the 4 MiB cap; capture reCommitSha refuses 64-hex commits; launch Short hides that --dry-run is required; release.yml verify-tag comment overstates what gh verifies; release.yml header says dispatch runs only the rehearsal job; dependabot covers no pip ecosystem for docs; brief pointer lines enumerate five of nine internals chapters; phase-6 and 06-lint carry change-narration blocks; 05-internals cites a ledger entry that does not exist; 03-evidence names the retired activity record nitpick documentation
- iss-2608211139076040The iss-392 canonRecordID fix canonicalised four record-id-uniqueness scan sites but missed the sibling sites sharing the same keyspace: deliverystate.go keyed the intent bucket map and its CHANGELOG citations on the raw id spelling (so a zero-padded intent filename or citation silently failed the drafts-citation delivery_state gate open), speclinks.go/KnownIntents and the superseded_by (lint.go) and spec intent-existence (lint.go validateSpec) lookups keyed/looked-up raw (false-blocking a padded twin), and spec_id_unique keyed on the raw frontmatter id (fail-open on a padded spc twin, the very hole iss-392 claimed to close for 'all three families'). The playbook's second spine: a pattern fixed at 4 of N sites leaves N-4 latent.
- itd-2In-Session Subagent Dispatch — the Default Host-Delegated Oracle
- itd-43abcd Speaks One Word for a Specced Block of Work, and That Word Is "Spec"
- itd-48`intent-fidelity-reviewer` Gains Its Cross-Doc And Kind-Classification Roles
- itd-85A Read-Only Repo-Conformance Audit
- spc-6issue-capture