Development record
A Read-Only Repo-Conformance Audit
Press Release
abcd ships
abcd audit— a read-only check that tells you, in one command, whether a repository follows the working conventions: the three-tier.abcd/layout, anAGENTS.mdrouter, durable decisions, current Diátaxis docs, and privacy hygiene. It runs against any repo given only a working directory, prints a grouped human report with a fix message for each gap, and emits machine JSON carrying a stable rule id and severity per finding.abcd ahoy doctorstill answers "is the tool set up here";abcd auditanswers "does this repo conform" — two different questions, two verbs. Onboarding stops being guesswork:prepare-this-reporunsabcd auditand shows exactly what to fix."I hand-built some
.abcd/structure and thought I was done," said Alice, a maintainer. "abcd audittold me in a second that mywork/tier was missing and my decisions were sitting in the gitignored layer — the two things that actually mattered."
Why This Matters
Today abcd ahoy reports abcd's own install-plumbing gaps (config, rules, marker block, history store) and is silent on the convention gaps prepare-this-repo exists to fix — a missing committed .abcd/work/ tier, an absent AGENTS.md router, decisions leaking into the gitignored layer, docs drift, privacy hygiene. The onboarding audit is performed by an agent reading prose, not by the binary, so onboarding is glue rather than engine (captured as iss-86). abcd audit is the read-only surface that closes that gap: the binary checks the conventions, and prepare-this-repo consumes its result instead of hand-auditing. Its tri-state exit code lets a repository wire it into CI, though abcd's own CI does not run its conformance check; making a narrow, high-precision subset of its findings merge-blocking is ruled (2026-09-23, iss-2608231000561060) and not yet built.
What's In Scope
- A new read-only
abcd auditverb, distinct fromahoy doctor(doctor = tool-setup health; audit = repo conformance). Wired from both front doors: CLI pluscommands/audit.md, with ashippedrow in the04-surfacesregistry sosurface_coveragestays honest. - Engine reuse: build on
internal/core/lint(severity + fix-message + allow-context), adding the two primitives it lacks — path presence/absence and simple structural assertions (a path is gitignored; a directory holds at least one entry). - A declarative rule schema adapting repolinter's vocabulary:
{ id, severity (error|warn|off), where (conditional enablement), fix, policyInfo }, separate from the evaluator, with bundled in-binary defaults. - Five v1 rules:
three-tier-layout(error),conventions-router(error),decision-durability(warn),docs-currency(warn, reusing docs-lint),privacy-hygiene(error). - Native compact JSON on stdout (stable rule ids) plus a grouped human render with per-finding severity glyph and inline fix message, diagnostics routed to stderr. Conftest-style tri-state exit:
0clean,1warnings only,2any error. - Wiring:
prepare-this-repoPhase 2 consumesabcd audit --jsonin place of the hand-produced gap report.
What's Out of Scope
- SARIF export (
--format sarif) — deferred to a later phase; native JSON ships first, behind a serializer seam that makes SARIF a thin add-on when CI/IDE ingestion is wanted. - Repo-level rule overrides / config extension — a later phase; v1 ships bundled defaults only.
- Any auto-fix or mutation —
auditis strictly read-only; remediation stays withprepare-this-repoand the maintainer. - The
managed-repoadoption-state check (iss-88) — that stays anahoy/detection concern; folding it here would re-blur the audit/doctor split. - Any external rule engine (OPA/Rego, and the archived repolinter) — rejected on the no-new-dependency hard stop.
Acceptance Criteria
Required (per the itd-1 discipline). At least one Given-When-Then bullet describing the verifiable bar for "shipped".
- Given a repo missing
.abcd/work/, whenabcd audit --jsonruns, then thethree-tier-layoutrule reportsseverity: errorwith a fix message naming the missing tier, and the process exits2. - Given a repo whose decisions live only in the gitignored
.work.local/layer, whenabcd auditruns, thendecision-durabilityreportswarnand the process exits1(no errors present). - Given a committed file containing an absolute local path, when
abcd auditruns, thenprivacy-hygienereportserrorcitingfile:line, unless a waiver escape is present on that line. - Given a conforming repo, when
abcd auditruns, then it exits0with a green human render, andabcd audit --jsonemits{ "findings": [] }. - Given
docs/is absent, whenabcd auditruns, then thedocs-currencyrule is skipped via itswherecondition rather than failed.
SOTA
Per the sota-per-intent principle: existing alternatives + rough maturity, then the chosen path. Harvested from the SOTA-researched design plan
2026-07-13-abcd-audit-verb.md.
- Repo-convention rule engine. Alternatives: repolinter (declarative rulesets — mature but archived, Node runtime); MegaLinter (mature, Docker orchestrator of ~50 linters). Both are foreign runtimes for a single Go binary. repolinter's rule-object schema (
id/severity/where/fix/policyInfo+extends) is adopted as a data model only. → Bespoke oninternal/core/lint. No dependency. - Severity + exit-code semantics. Alternatives: OPA Conftest (
deny/warnnamespaces, exit0/1/2— mature); the OPA/Rego engine is the one option genuinely embeddable in Go but is heavy and forces Rego authoring. Conftest's severity→exit vocabulary is adopted as convention; the engine is rejected. → Adapt behind the engine. No dependency. - Result interchange. SARIF 2.1.0 (de-facto standard, ingested by GitHub code-scanning; emitted by checkov/MegaLinter/Conftest). Adopted as an optional export format behind a serializer seam, deferred to a later phase. → Adapt, later. No dependency.
- Presentation. The
doctorUX pattern (consensus: grouped checks, severity glyph, inline actionable fix). → Adopt as human-render presentation over the engine's results.
Verdict — bespoke-with-seam. No new dependency ⇒ no path-1 hard stop; the seams are load-bearing (a rule-loader seam and an output-serializer seam) ⇒ no path-3 bespoke-no-seam review. Build proceeds without a dependency gate.
Open Questions
- Repo-level rule override/config format — deferred; v1 ships bundled defaults only.
- Whether
decision-durabilitygraduates fromwarntoerroronce the convention is firmly established. - Verb placement — resolved 2026-07-13. A new read-only
abcd auditverb, distinct fromahoy doctor: doctor answers tool-setup health, audit answers repo conformance. Plan-review treats this as decided rather than re-opening.
Audit Notes
Empty. Populated by intent-fidelity-reviewer when intent moves to shipped/.
Frontmatter
| id | itd-85 |
|---|---|
| type | intent |
| lifecycle | drafts |
| kind | standalone |
| severity | major |
.abcd/development/intents/drafts/itd-85-audit-verb.md
open on GitHub ↗ · commit history ↗
Referenced by15
- adr-40Review, audit, lint, and gate are four buckets, separated by what each comparesrelated
- iss-2610262031419126The naming register reserves `/abcd:audit` for itd-16's formal-verification surface, but itd-85 takes `abcd audit` for a repo-conformance check.related
- iss-2610262033013099itd-85 wires a new abcd audit verb into prepare-this-repo's Phase 2 as the conformance report; the ahoy page already gives that phase to abcd lint's JSON form.related
- iss-2610262034151413itd-85 names `abcd audit` as the repository-conformance check; the intent chapter says the conformance check is `abcd lint` and reserves `/abcd:audit` for hash-chain checks.related
- iss-2610262034164290itd-85 has `prepare-this-repo` read `abcd audit --json`; the board chapter says the install's preparation workflow runs `abcd lint` for its audit half.related
- iss-2610262036142715The lint chapter reserves /abcd:audit for itd-16's hash-chain fidelity surface and calls the conformance check abcd lint; itd-85, the intent the chapter cites, ships that same check as abcd audit.related
- iss-2610262039195057itd-85 makes `abcd audit` a repository conformance check, but the agent catalogue reserves the top-level `/abcd:audit` for itd-16's hash-chain surfacerelated
- iss-2610262039214406itd-85's `abcd audit` checks whether a repository conforms, which the register already gives to `/abcd:lint`related
- iss-2610262041077645The skills chapter says repo preparation now runs from the install section of `commands/ahoy.md`; the audit draft wires itself into a 'prepare-this-repo Phase 2'.related
- iss-2610262042244924The audit verb is the hash-chain umbrella in itd-16 and a repo-conformance check in itd-85related
- iss-2610262043515080itd-85 names the repo-conformance check `abcd audit`; itd-124 says the conformance check is `abcd lint` and `audit` is reserved for something elserelated
- iss-2610262043528869`abcd audit` is the repo-conformance verb in itd-85 and the lifeboat-integrity verb in itd-35related
- iss-2610262044201056The audit draft adds a separate repo-conformance check verb, while the shipped consolidation folded every 'check this repository' spelling into one lintrelated
- iss-2610262044218994`abcd audit` names a repo-conformance check in one record and is reserved for the hash-chain merkle audit in anotherrelated
- iss-2610262044229426The audit draft wires into prepare-this-repo Phase 2, but prepare-this-repo has folded into ahoy install with no aliasrelated
mentions27
- adr-35The lifeboat is a coverage experiment — read-only, out-of-tree, and proven before it is packed (supersedes adr-4)
- dcn-26071300000000022026-07-13 — `abcd audit` (itd-85): a new read-only repo-conformance verb, distinct from
- dcn-26071300000000032026-07-13 (itd-85 M1): kept `core.exists` (bool-only, swallows errors) and
- dcn-26071300000000042026-07-13 (itd-85, carry to M3): `gitutil.CheckIgnored` fails OPEN — git
- dcn-26071300000000052026-07-13 (itd-85 M2): audit engine uses severity vocabulary error|warn|off
- dcn-26071300000000062026-07-13 (itd-85 M3): privacy-hygiene uses a deterministic, identity-INDEPENDENT
- dcn-26071300000000072026-07-13 (itd-85 M3): docs-currency emits every finding at warn, downgrading
- dcn-26071300000000082026-07-13 (itd-85 M3): three-tier-layout does NOT require .abcd/.work.local/ to
- dcn-26071300000000092026-07-13 (itd-85 M3): privacy-hygiene reads tracked files through os.OpenRoot
- dcn-26071300000000102026-07-13 (itd-85 M7): acknowledged repolinter (rule schema) and Conftest
- dcn-26071400000000022026-07-14 — adr-4 SUPERSEDED by adr-35 and pruned per the ADR convention
- dcn-26080200000000092026-08-02 — iss-41 (v0.5.0 item D): the interim delivery-state rule is stated as the one the lifecycle already implies — **delivered capability is represented by the intent leaving `drafts/`, and nothing else says it** — because the alternative on offer (a new tier, or a per-intent "partly shipped" field) invents lifecycle vocabulary to describe a citation problem. The ledger's premise that `shipped/` is empty has aged out: thirteen intents live there now, so the mechanism exists and what was missing was the gate holding the changelog to it. `delivery_state` reads each version entry's delivery sections (`Added`, `Changed`; a repo's configured list is unioned with those, never substituted for them) and blocks an `itd-N` citation whose intent is still in `drafts/`. Scoping by section rather than by whole entry is what keeps it precise: the ids under `Fixed` in this corpus are provenance for a defect — which draft two branches minted at once — and blocking them would force either a falsehood or a deletion of real information. It fails closed three ways: no changelog, no intents store, and a store holding no lifecycle bucket — the last being a root pointed one level too deep, which resolves every citation to nothing and reads exactly like a clean corpus. The corpus had TWO instances, not the one filed: itd-60, whose deterministic layer shipped as `abcd docs lint` while its semantic layer is still five open questions, and itd-85, which shipped WHOLE as `abcd audit` and was simply never promoted. They get different treatments because they are different facts. itd-60 stays in `drafts/` and loses the citation — an intent is delivered whole or not at all (split-the-intent doctrine), so citing a partial delivery reads as a promotion that never happened, and splitting itd-60 to make the citation true is editorial work this issue does not carry. itd-85 loses the citation too, but under protest: the honest fix is promotion, and promotion is blocked on the `shipped/` schema wanting a non-null `spec_id` the audit verb has never had. Filed as iss-180 rather than papered over or forced — writing a retroactive spec to satisfy a lint is the failure mode this repo keeps naming. The out-of-scope half is a REUSE, not a new rule: `index_drift` gained one field, `dir_entry`, the mirror of `entry` on the directory side, which reduces a filename stem to the id a document actually enumerates. Without it a hand-written listing can only agree with a directory by transcribing whole slugs, which is a listing nobody writes, and the alternative was a second rule shaped exactly like the first. The list itself had drifted nineteen entries while claiming in its own prose to be derived and "not hand-counted": itd-47, itd-73 and itd-74 had left `drafts/` for three different buckets, and sixteen captures were missing. Regenerated, wrapped in the marked region, and the sentence promising hand-kept lockstep is replaced by the gate that enforces it. The four later-phase items that never had an intent id move OUT of the derived list — a derivation cannot produce them, so their presence was what made the claim false in the first place.
- dcn-26081600000000032026-08-16 — the assessment vocabulary settled at a maintainer grill and filed as adr-40 (proposed): **four buckets — lint / review / audit / gate — separated by what each compares**, not by determinism, not by trigger. The rule was already half-recorded (05-internals/01-agents.md rules the two verdict families disjoint: "reviews assess a change, criterion verdicts assess a promise vs reality") and three shipped surfaces contradict it — `intent review` emits family 2 so it is an audit, `abcd audit` is deterministic rule-checking so it is a lint (and it consumed the `/abcd:audit` name 02-constraints/04-naming.md reserves for itd-16), and `disembark oracle` emits family 1 while the brief calls its output an audit. Grill rulings: **one surface performs exactly one act** (multi-act surfaces split at design time — cheap, because every multi-act surface abcd has is unbuilt, so they are specified rather than refactored); the buckets are a **closed list extended by PR, not a criterion** (an autonomous run given a criterion interprets it; a table lookup matches or fails); `intent ready` survives as the fourth bucket **gate** because it names a decision rather than a comparison, preserving itd-94's exit-code contract; determinism is orthogonal (itd-16's hash-chain check is a deterministic *audit*; itd-85's conformance check is a *lint* however built); automatic-vs-manual never enters a verb name (the trigger is the facilitator's, per itd-97); the oracle is the model-access seam beneath review and audit, never a bucket. **Clean break, no aliases** — pre-1.0.0, `--impact breaking` drives version derivation (iss-171 precedent), users re-download; an alias would additionally collide with the documentation discipline's ban on change-narration, leaving an undocumented silent forward. The role mapping is the operational reason the split earns its cost: product thinker reads `audit` and essentially nothing else, which makes itd-99's "never hand a product thinker a technical decision" mechanically checkable. Same grill corrected a wrong finding: iss-246 was filed claiming abcd's record documents a surface far larger than the binary with no gate catching it; **false** — the `Status` column in 04-surfaces/README.md is machine-checked by `surface_coverage` (iss-35), the brief carries ~100 inline `design target` markers, and adr-5 makes the brief aspirational by design. The real defect is narrower: `surface_coverage` is blind *inside* a row, so six of twenty rows read `shipped` then qualify themselves in prose, and documents outside the registry cite those sub-verbs as live (all three citing **predecessor-store** spec ids spc-29/spc-30, whose prose survived the migration to the native store). Fix per adr-40 §6: sub-verb tables carrying two facts per verb — bucket and existence — with `surface_coverage` extended to registered cobra sub-commands, one table serving both disciplines. Also established: the twelve-step record walk completes at ten steps; step 2 (`capture promote`) has no verb and step 12 (`capture resolve`) cannot write `resolved_by`, so **walkability is sequenced before coherence** — renaming ~90 files across a process nobody can walk is polish before function. Plan at plans/2026-08-16-process-coherence-and-walkability.md; six intents to plan, none plannable unattended. Rejected: one polymorphic `review <id>` verb (merges the disjoint families, erases the role signal), classifying acts rather than surfaces (makes the bucket a statement of intent rather than a guarantee, and hands product thinkers lint findings), three buckets without `gate` (forces `intent ready` to become `intent lint`), and adding `verify`/`validate` (itd-109's verification suite is a packaging of lints and audits, not a fifth kind).
- iss-86abcd has no read-only convention-audit surface, and the binary gap model is disjoint from the prepare-this-repo skill audit. abcd ahoy --json on a partially-scaffolded repo reports only abcd own install-plumbing gaps (config.json, rules.json, marker blocks, history store) and is silent on the gaps the onboarding skill exists to fix: the missing committed .abcd/work/ tier, the absent AGENTS.md conventions router, and durable decisions leaking into the gitignored layer. The entire Phase 2 gap report had to be produced by hand. Detector: a read-only abcd audit (or extended ahoy doctor) verb that checks the three-tier layout, decision-durability, docs Diataxis/tense, and privacy -- every convention the skill audits by prose. Acceptance corpus (Manuscripts dogfood): (a) missing .abcd/work/ tier undetected, (b) decisions/ vs decisions/adrs/ layout drift undetected, (c) whole convention audit done manually. This is the single highest-value onboarding gap: it is what would make prepare-this-repo engine-backed instead of agent-glue.
- iss-88abcd ahoy classifies a repo with a partial or stray .abcd/ as folder_kind managed-repo even when adopted is null and index_registered is false -- managed reads as abcd manages this when it only means abcd could. No folder_kind value distinguishes a stray .abcd/ built by another workflow from an abcd-adopted repo. Observed on Manuscripts, whose .abcd/development/ was hand-built with zero abcd involvement yet is reported managed. Related: iss-62 managed-repo-identity-gate. Detector: a folder_kind vocabulary that separates adopted from merely-abcd-shaped; acceptance is ahoy --json on a hand-built .abcd/ not reporting managed.
- iss-180itd-85 shipped whole but still sits in intents/drafts/: abcd audit ships with all five v1 rules, the tri-state exit and the prepare-this-repo wiring the intent scopes, yet the intent was never promoted, so the record cannot say the capability is delivered. Promotion is blocked on the shipped/ schema, which requires a non-null spec_id and an impact value the intent has never carried (there is no spc-N for the audit verb). Disposition: mint or retro-link a spec via /abcd:intent link, set impact, run the fidelity review, and move drafts/ -> shipped/. Until then the v0.2.0 CHANGELOG entry describes abcd audit without citing itd-85 (iss-41).
- iss-238brief/05-internals/01-agents.md still reserves /abcd:audit for 'compliance / hash-chain' (itd-16's umbrella), contradicting the shipped repo-conformance audit surface documented in 04-surfaces/16-audit.md (itd-85 lineage).
- iss-240itd-85 (read-only repo-conformance audit) is fully delivered — internal/core/audit, cli/audit.go, commands/audit.md, surfaces/16-audit.md, resolved issues against it — yet the intent still sits in drafts/. Under the delivery-state doctrine the capability is only half-recorded until the maintainer runs the ship move.
- iss-392bughunt 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
- iss-2608231000561060The repo-conformance lint runs in no gate while the record says it gates CI, and it is not currently gate-ready, which are two problems needing separate decisions. Verified 2026-08-23 on origin/main. brief/04-surfaces/README.md row 16 describes /abcd:lint as checking the three-tier layout, AGENTS.md router, durable decisions, docs currency, privacy hygiene and identity positioning, and states it 'backs prepare-this-repo and gates CI (itd-85)'. It does not gate CI. The Makefile's preflight target is lint-reviews record-lint docs-lint plus build, vet, test and race, with no abcd lint, and the Makefile has no abcd lint or repolint target at all. Searching .github/workflows for an invocation of the repo-conformance lint, excluding the unrelated 'abcd docs lint', returns nothing in ci.yml or release.yml, whose only lint steps are go run ./cmd/record-lint and go run ./cmd/abcd docs lint. The surface runs only when a human types it. The second problem is why simply wiring it up does not work. On current main the lint emits 19 privacy-hygiene findings, 17 at blocker severity, and the sampled ones are not leaks. Nine of the 19 are in the scanner's own source or in ledger entries about the scanner, where the patterns appear as subject matter. Three sampled outside that set are documentation examples: a Linuxbrew prefix in a spec table and again in [dcn-2608200000000006](../../decisions/dcn-2608200000000006-homebrew-distribution-recorded-and.md), and a CHANGELOG entry recording that certain path shapes stopped being read as usernames. So gating it today would fail every build on content that is correct. iss-305, resolved, records the mechanism -- hasAbsHomePath lacks the leading-boundary predicate its scanner twin has -- and iss-307, iss-308 and iss-324 are adjacent boundary defects in the same family. A waiver marker exists, abcd-lint:allow with the legacy spelling abcd-audit:allow, and the rule's own source uses it on two lines, so the mechanism is available but is not applied across the corpus. What makes this worth deciding rather than filing and forgetting: the rule is right about the case that actually bit. An absolute home path written into a ledger issue body is exactly what its pattern catches, and on 2026-08-23 two sessions independently wrote one. Neither was caught by an instrument. record-lint did not see them because its roots are ['.abcd/development'] and the ledger is in .abcd/work; the lint that would have seen them does not run. Note the two are not interchangeable, since record-lint is scoped to the durable record while privacy-hygiene scans every tracked file, so widening record-lint's roots is a different fix from running the lint. Decide in order: whether the record's gates-CI claim is corrected or made true, and if made true, whether the false-positive rate is addressed by waivers, by the iss-305 boundary fix, or by narrowing what blocks. Leaving the claim standing while the surface runs nowhere is the shape enforcement-claims-are-facts names, and iss-2608230847432286 records the class.
- iss-2609020906544088Found while readying Phase 8: the generated CLI reference (docs/reference/cli/commands.md), the surface_coverage record-lint rule and the brief-to-surface crosscheck gate were all shipped under issue ids with no intent record, and itd-60 and itd-85 each shipped substantial code while sitting in drafts/ (itd-60's deterministic docs lint is live as abcd docs lint; itd-85's delivery is on main). Phase 8's expectation is that the record is the shipped state, and these are shipped capabilities the record does not name as intents, so a product thinker reading the brief and the intents together still cannot see them as delivered. Directions, none adopted: file a shipped intent per capability by hand with an audit note saying it was delivered before the ceremony existed; or list them in the Phase 8 file as pre-ceremony deliveries and leave the intents as is; or resolve the gap through the doc-fidelity gate itself once it exists, since it will name every surface the brief describes without an intent.
- iss-2610262035543059The out-of-scope list holds itd-85, 'A Read-Only Repo-Conformance Audit', as later-phase draft work; the lint chapter ships that read-only conformance verb and cites itd-85 as its intent
- itd-1Every Intent Has Acceptance Criteria; Every Audit Verifies Them
- itd-88Know What a Repo Can and Cannot Tell You — Before You Trust the Lifeboat
- itd-123The Intent Audit Says Audit
- itd-124The Conformance Check Calls Itself Lint
- spc-29the-conformance-check-calls-itself-lint-abcd-audit-becomes-a