feat(agent-plugin): offer the bootstrap decision record as an offer, not a candidate - #199
feat(agent-plugin): offer the bootstrap decision record as an offer, not a candidate#199mbeacom wants to merge 4 commits into
Conversation
…not a candidate Backfill run against a repository with no ADR corpus found decisions in the code but said nothing about the one the caller was making: whether to keep decision records at all, and whether adrkit enforces them. That record cannot be a backfill candidate. No source span proves a human ratified either choice, so it fails the skill's own evidence rule, and it governs the corpus directory -- a glob that often does not exist yet -- so it can never supply the concrete candidatePaths a backfillHandoff requires and /adr-draft re-verifies before writing. Modeling it as a candidate would have meant exempting it from that check. So it is offered rather than mined: reported under existing corpus state, routed to plain /adr-draft, and kept out of the candidates table and every handoff. The handoff contract is untouched. The edge is read off the corpus rather than assumed. Adopting adrkit is never a supersession of the decision to record decisions -- the tooling choice depends on the process choice. supersedes is reserved for a prior tooling record; a MADR corpus is migrated, not superseded. Detection runs through adr check's governing bucket, never by hand-parsing frontmatter. The wiring test was observed failing before the guidance existed (ADR-0016). Evidence is contract and static-host only -- no functional run -- and docs/reference-verification-agent-plugin.md says so explicitly. Claude-Session: https://claude.ai/code/session_018qNk7AF5cPWkqUkWFvGBqF Signed-off-by: Mark Beacom <m@beacom.dev>
Measuring the detection mechanic against synthetic corpora found a real defect in the guidance shipped by the previous commit. An unmigrated MADR corpus returns an empty `governing` bucket -- the same answer as a corpus with no process record -- because no record parses the frontmatter fence, including the record that IS the process decision. adr check reports frontmatter-fence errors and exits 1, so the signal exists, but the guidance stated the conclusion first and qualified it afterwards. An agent skimming it would offer the process decision to a repository that already has one, which is the exact failure ADR-0038 names as proof the design is wrong. The precondition now comes first: only on exit 0 does an empty bucket mean no process record exists. The MADR case is named explicitly, since it is the common real-world instance. Also merged two edge-table rows that became identical after the row-2 correction, and noted that with no corpus there is nothing to detect -- adr check against a missing corpus directory exits 2, measured. Four corpus shapes are now recorded as measured evidence rather than reasoned about: missing, source-only, process-record-present, and unmigrated MADR. Host surfacing behavior remains unverified and stays an open action item. The two new assertions were confirmed failing against the previous commit's text before the fix was written (ADR-0016). Claude-Session: https://claude.ai/code/session_018qNk7AF5cPWkqUkWFvGBqF Signed-off-by: Mark Beacom <m@beacom.dev>
… surfaces Three defects found by re-checking claims against what was actually measured. The measurements were taken with the main checkout's stale dist, which reports 0.5.0, while the evidence table claimed 0.13.0. Rebuilt from this worktree and re-ran every case at a CLI that genuinely reports 0.13.0. The results held, but the label was wrong, and one row was wrong for a different reason: the harness created docs/adr with mkdir -p, so the "no corpus" row actually measured an empty corpus. Separating them found a fifth state -- a corpus directory that exists but holds no record exits 0 with an empty bucket, where a missing one exits 2. Both read the same way here, and the skill now says so. The JSON finding key is `rule`, not `code`; the evidence table said the latter. CHANGELOG, README, and AGENTS.md still carried "no functional run of any kind" after detection had been measured. All three now state the same split the evidence document does: detection measured against synthetic corpora, host surfacing unverified. The CHANGELOG also gained the exit-code precondition, which is the rule most likely to be regressed and was absent from the entry a reader sees first. The wiring test read only SKILL.md, so the reachability defect fixed earlier could silently recur. It now asserts the offer, the not-a-candidate framing, and the never-supersedes rule on both SKILL.md and commands/adr-backfill.md. All three assertions were confirmed failing against the pre-clause command file. ADR-0038 now relates to ADR-0008, since MADR migration is a precondition of detection rather than an aside. Claude-Session: https://claude.ai/code/session_018qNk7AF5cPWkqUkWFvGBqF Signed-off-by: Mark Beacom <m@beacom.dev>
There was a problem hiding this comment.
🟡 Changes recommended
It introduces a few correctness issues in newly added documentation/ADR metadata (stored inline), which should be fixed before approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR updates the portable agent plugin’s backfill guidance so that, when run against a repo with no ADR corpus (or no governing process record), it offers the bootstrap “keep ADRs + enforce with adrkit” decision(s) as an offer (routed to /adr-draft) rather than treating it as an evidence-backed backfill candidate. It also bumps the agent-plugin surface version to 0.3.0 and documents/measures the MADR parse “empty governing bucket” trap.
Changes:
- Add bootstrap-record offer guidance across the backfill skill, command docs, and site docs, plus a wiring test to prevent regression.
- Add ADR-0038 (proposed) and extend reference-verification documentation for the measured detection behavior.
- Bump agent-plugin version to 0.3.0 across manifests/metadata (including marketplace entry) and record in CHANGELOG/AGENTS.
File summaries
| File | Description |
|---|---|
| site/src/content/docs/quickstart.mdx | Quickstart tip guiding the first record and linking to the plugin offer behavior. |
| site/src/content/docs/backfill.mdx | Docs describing the “no corpus yet” bootstrap offer and relationship rules. |
| packages/adapters/agent-plugin/test/wiring.test.ts | Adds regression assertions ensuring guidance exists on both skill + command surfaces. |
| packages/adapters/agent-plugin/skills/decision-memory/SKILL.md | Version bump + no-corpus clause aligning with bootstrap-offer guidance. |
| packages/adapters/agent-plugin/skills/decision-backfill/SKILL.md | Main new bootstrap-offer guidance, including MADR exit-code-before-bucket rule. |
| packages/adapters/agent-plugin/README.md | Updates narrative to include 0.3.0 behavior and validation status. |
| packages/adapters/agent-plugin/package.json | Bumps agent-plugin surface version to 0.3.0. |
| packages/adapters/agent-plugin/commands/adr-backfill.md | Ensures the command surface includes the bootstrap-offer guidance. |
| packages/adapters/agent-plugin/apm.yml | Bumps APM version to 0.3.0. |
| packages/adapters/agent-plugin/.claude-plugin/plugin.json | Bumps Claude plugin manifest version to 0.3.0. |
| docs/reference-verification-agent-plugin.md | Adds v0.3.0 guidance verification notes and measured detection table. |
| docs/adr/0038-offer-the-bootstrap-decision-record-as-an-offer-rather-than-a-backfill-candidate.md | New proposed ADR recording the design/measurement and open functional-run item. |
| CHANGELOG.md | Notes the new bootstrap-offer guidance under Unreleased. |
| bun.lock | Updates workspace package version for @adrkit/agent-plugin to 0.3.0. |
| AGENTS.md | Updates the agent-plugin “load-bearing” section to include the new guidance. |
| .claude-plugin/marketplace.json | Bumps marketplace entry metadata/plugin version to 0.3.0. |
Review details
- Files reviewed: 15/16 changed files
- Comments generated: 3
- Review effort level: Lite
| affects: | ||
| - type: path | ||
| pattern: "packages/adapters/agent-plugin/skills/**" | ||
| - type: path | ||
| pattern: "site/src/content/docs/quickstart.mdx" | ||
| - type: path | ||
| pattern: "docs/reference-verification-agent-plugin.md" |
|
|
||
| | Corpus state | Offered | Edge | | ||
| | --- | --- | --- | | ||
| | No corpus, or records exist but none govern the corpus directory | Process and tooling decision | `relatesTo` between them when split | |
| It governs `docs/adr/**`, so it is the record that explains the directory to | ||
| everyone who finds it later. Write it with `adr new "Record architecture | ||
| decisions as versioned markdown in git"` — it then takes `0001`, and the |
MANIFEST.md is generated by `bun run emit:manifest` and CI diffs it under network denial. Adding ADR-0038 made it stale, so clean-clone-builds failed on the record count and the missing table row. Found by CI rather than locally: the emit:* script was not in the set of gates run before pushing, only the check:* ones. The remaining clean-clone steps -- schema:emit diff, the committed Action bundle diff, and release:pack -- were verified locally this time and are clean. Claude-Session: https://claude.ai/code/session_018qNk7AF5cPWkqUkWFvGBqF Signed-off-by: Mark Beacom <m@beacom.dev>
Decisions governing this change
Active proposals touching this changeThese are not yet ratified and do not bind this change:
|
What and why
Run
/adr-backfillagainst a repository with no ADR corpus and it audits the code for decisions that were never recorded — while saying nothing about the one decision the caller is in the middle of making: whether to keep decision records here at all, and whether adrkit enforces them.That record cannot be a backfill candidate, for two independent reasons:
backfillHandoffcarries concretecandidatePaths, never globs, and/adr-draftre-runsadr checkover exactly those paths before writing. The bootstrap record governs the corpus directory — a glob that frequently does not exist yet.So it is offered, not mined: reported under existing corpus state, routed to plain
/adr-draft, kept out of the candidates table and every handoff. The handoff contract is untouched and no exemption was carved into it.The edge is read off the corpus rather than assumed. Adopting adrkit is never a supersession of the decision to record decisions — the tooling choice depends on the process choice.
supersedesis reserved for a prior tooling record (adr-tools,log4brains); a MADR corpus is migrated, not superseded.The defect worth reading
Measuring detection instead of reasoning about it found a real bug in the first version of this guidance. Against an unmigrated MADR corpus,
adr checkreturns an emptygoverningbucket — identical to "no process record exists" — because no record parses the frontmatter fence, including the record that is the process decision. Following the original wording, an agent would offer the process decision to a repository that already had one: precisely the failure ADR-0038 names as proof the design is wrong.Fixed by making the exit code a precondition rather than an afterthought. Only on exit
0does an empty bucket mean absence; on exit1the corpus did not parse and the offer is unverified until migrated.Evidence
Detection measured against five corpus shapes with a CLI built from this branch reporting
0.13.0:governingdocs/adr/absent20[]src/**only0[]docs/adr/**0["0002"]1[]+rule: frontmatter-fenceHost surfacing behavior is unverified. Whether any host actually presents the offer on an empty corpus needs a functional run and is tracked as an open action item on ADR-0038, not claimed here.
Checklist
proposed, awaiting ratificationpackages/ci/srcand@adrkit/coreunchanged — N/A (see note below)typecheck,build,test,lintpass (2813 pass / 0 fail), pluscheck:changelog,check:site-grammar,check:doc-pins,check:deps,check:clean-clone,check:freeze-hashes,check:clause8,check:no-spike-heuristics, bothclaude plugin validatetargets,adr lint, and the site buildNotes for reviewers
plugin.json,apm.yml,package.json,bun.lock, both marketplace entries, both skills'metadata.version) — Claude Code keys its plugin cache on it.packages/ci/distis deliberately untouched. Runningbun run buildto check a version rebuilt those bundles off-pin (local Bun 1.4.0 vs the pinned 1.3.14), producing ~4,400 lines of codegen drift. Reverted — the template's own warning about Mac-built bundles applies./adr-backfillloads the command, socommands/adr-backfill.mdcarries the clause too; an earlier revision edited only the skill and the offer was unreachable from the entry point. The wiring test now asserts on both.lintwill nag about forever. The guidance offers a decision to make, not a template to accept, and says so.https://claude.ai/code/session_018qNk7AF5cPWkqUkWFvGBqF