Development record
RE-SCOPED 2026-09-09 against the shipped tree. Every instance in the original
RE-SCOPED 2026-09-09 against the shipped tree. Every instance in the original acceptance corpus but one is fixed and struck below; the single survivor is what the record was mostly about.
SURVIVING — ahoy.Status is silent dead scaffolding. internal/core/ahoy/apply.go:1362
declares func Status(cwd string) (string, error), the bare-command human summary,
and NOTHING calls it: not the CLI, not the plugin surface, not a test. The
original record said "zero callers outside tests"; the true state is stronger —
there are no callers at all, so the function is not even pinned by a test that
would notice it rotting. It is not merely unwired: 01-ahoy.md lists status
among the sub-verbs that ship on the CLI while abcd ahoy status exits 2, which
iss-2609020734198139 records separately as a false claim in the design record.
Either the verb gets a front door or the function and the chapter line go; what
is not tenable is an exported renderer nothing can reach.
FIXED ELSEWHERE, struck from the corpus:
- The
scan_deephalf of the persist-unvalidated claim, split out asiss-2609090642031172and resolved with it. The collect-missing slot re-checks the prompter's answer throughinSet(ans, scanDeepChoices)and takes the partial-install path on anything else, so an answer outside the choice set —yesto a question about deep secret scanning — can no longer persist a weaker scan than the operator asked for.scanDeepChoicesandscanDeepDefaultsit beside the three enum choice sets indetect.go, and the override path and the would-change probe read the same set rather than restatingtrue/false. - The
docs_targetandoracle_backendhalves of the persist-unvalidated claim. 6cf0df8a (2026-07-15) added theinSetre-validation;apply.go:478-481now carries the comment// no valid docs target => partial (never persist a typo)and:484-487the same for the oracle backend. Neither slot can persist an answer outside its choice set. registerRepois neither untested nor silent.internal/core/ahoy/lockrace_test.godrives it directly through two concurrent registrations, and c88c94b4 (2026-08-28) replaced the swallowed error with a change-note:apply.go:835appendshistory registration for <sha> skipped (<err>)on a lock failure, and the lineage-conflict branch below it reports its own refusal (iss-128).doctoranddry-runhave behavioural tests against a real temporary repo.internal/surface/cli/cli_test.go:883,:912,:962and:1011exerciseabcd ahoy doctorin both JSON and text renders;internal/surface/cli/ahoy_banlist_reach_test.go:102exercisesabcd ahoy dry-run.
Every line number in the ORIGINAL text was wrong for the current tree (it cited
apply.go:184-192, :280, :441, :478); the file has roughly tripled since.
The detector the record asked for — a wired-verb convention plus a
coverage-plus-caller audit that distinguishes loud staging from silent
scaffolding, per .abcd/development/principles/loud-staging.md — is still the
right shape and is still unbuilt; ahoy.Status is exactly the instance it would
catch.
Grounds
- pursued: no exported ahoy renderer exists that no front door reaches; shown wrong if an exported ahoy function without an outside production caller passes the package's caller audit
Frontmatter
| id | iss-33 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | major |
.abcd/work/issues/resolved/iss-33-ahoy-verb-hygiene.md
open on GitHub ↗ · commit history ↗
mentions5
- dcn-26071200000000012026-07-12 — clean-slate hardening run STEP 0 triage. A fresh adversarial
- iss-128registerRepo's best-effort history paths lose signal silently in two edges the iss-101/102 reviews confirmed: (1) a history-lock contention timeout skips registration with no surfaced note (apply.go registerRepo discards the withHistoryLock error); (2) under concurrent double-install of the same new repo with divergent re-founding answers, the lock loser takes the refresh branch which never consults linkLineage, silently dropping a human-approved lineage link with no lineageConflict message
- iss-2609020734198139The v0.7.1 brief-surface crosscheck (Direction A, tier full, over the sixteen first surface chapters) found four false claims in the design record, none in a user-facing page. 01-ahoy.md line 225 says every non-SessionStart shim attempts hooks/bootstrap.sh when the plugin-root binary is missing; hooks.json wires bootstrap into UserPromptSubmit, SessionStart, PreToolUse and PreCompact but not SessionEnd, which resolves the plugin root, then a vouched PATH abcd, then fails loudly, so a machine with no plugin-root binary never stages its transcript at exit and the chapter says otherwise. 01-ahoy.md line 28 lists status among the sub-verbs that ship on the CLI; abcd ahoy status exits 2, status being a plugin-side alias for the bare render, and the chapter's own sub-verb table omits it. 07-memory.md line 3 disambiguates spc-38 and spc-39 by saying the store's ids stop well short of spc-38; the store now runs to spc-70 and both ids name unrelated specs, so a dozen bare references on the page point at the wrong records. 07-memory.md line 116 enumerates launch --dry-run's gates as five, omitting semantic-receipts, which 04-launch.md documents. Each is a one-line correction; the receipt for the v0.7.1 cut defers them to this record.
- iss-2609090642031172The interactive install collects scan_deep through resolveValue with the choice set true|false and then compares the returned string to true, so every other answer becomes false and the deep secret scanner is switched OFF. stdinPrompter.Prompt returns the typed line verbatim and nothing re-checks it against the choices it was handed, so a user who answers yes, y, or Yes to a question about scanning for secrets gets the opposite of what they asked for, with no diagnostic. The three neighbouring slots in the same function abandon the install on exactly this typo rather than persisting it: docs_target and oracle_backend validate through inSet and return a partial install with the comment never persist a typo, and the asymmetry is the defect. The override path applyScanDeepOverride is guarded, so the reachable route is the interactive and collect-missing one, and the gate is narrow: Visibility private, trufflehog on PATH, ScanDeep unset. A silent security downgrade is worse than a refused install, which is the judgement the neighbours already encode. Fix: re-check the answer against the choice set as the neighbours do and return a partial install on anything else, so an unparseable answer never resolves to a weaker scan than the one the operator asked for; consider whether a boolean slot should accept the ordinary spellings of yes and no before it refuses. Detector: resolveValue answering yes for scan_deep must not yield a disabled deep scan, and must take the same partial-install route the docs_target and oracle_backend typo cases take, with the existing false answer still disabling it deliberately. Found while re-scoping iss-33, whose body had recorded this half as fixed; it is not.
- iss-2609252211487887The exported-reach caller audit iss-33 asked for exists only for internal/core/ahoy (TestEveryExportedAhoyFunctionHasAFrontDoor). A crude survey of the other internal/core packages (an exported top-level function with no 'pkg.Name' selector in non-test Go outside its package) lists about 140 names; many are reached only inside their own package, which is over-export rather than dead code, but some have no production caller anywhere, e.g. launch.Ship (grep for '.Ship(' outside tests finds none). Each hit needs sorting into dead scaffolding (delete or wire), in-package-only (unexport), or a declared test seam (name it ...ForTest), and the audit then generalised to every core package.