Skip to content

adr/v1: Agent Decision Records for agent-ways, adr-tool 2.1.0 - #609

Merged
aaronsb merged 84 commits into
mainfrom
integration/adr-v1
Sep 28, 2026
Merged

aaronsb merged 84 commits into
mainfrom
integration/adr-v1

Conversation

@aaronsb

@aaronsb aaronsb commented Sep 28, 2026

Copy link
Copy Markdown
Owner

Merges integration/adr-v1 into main: agent-ways adopts Agent Decision Records (adr/v1) for its own corpus, and ships adr-tool 2.1.0. Tracked in #589. Operator-gated: this PR is ready for review and merge, and the release follows it.

Summary

  • Decided: records are a ledger of decisions and who made them, kept in git-tracked files. The tool writes records, checks their shape, and checks that their references resolve (ADR-311). Git holds the history.
    • agent-ways' 92 records are converted to v1.
    • Records carry permanent numbers in intent folders (ADR-310).
    • The design notes became evidence and spec records (ADR-309).
    • Existing corpora convert through adr import (ADR-306).
    • New projects start on adr/v1.
  • Trades away: automatic enforcement of how records relate: frozen text, an add per capability, a prior per change. The ADR way teaches the conventions, and review and git catch breaks.
  • One-way? No. The contract is declared per project in adr.yaml. v0 projects keep working, and the tool reads their records unchanged.
  • Probes: Confident (shape-is-enough): shape and reference checks keep the ledger readable. Not confident (convention-holds): whether agents keep correcting by appending and naming what a change replaces, with nothing flagging it. The operator's answer: the way teaches, and review catches.
  • Inversion: at one end, loose notes with no structure. At the other, a tool that audits every record against its history. Here records are typed and linked, and git is the backstop.

What's in it

PR Change
#587, #591, #597 ADR-306: adr import through a round-trip import sheet
#592, #595 adr new writes v1; adr import scan/apply with a v0 reader
#596, #598 ADR-307: observable outcomes on decisions
#599 agent-ways' 92 records converted to v1 (ADR-308: a change may list several capabilities)
#602 Stale citations fixed; the evidence kind (ADR-309); permanent numbers in intent folders (ADR-310); domain add/rename/move; consider, set, supersede, enact and list queries; the design notes moved into the corpus; the guidance brought up to v1; "Agent Decision Record" throughout; the v1 template default
#605 ADR-306/307: accepted text restored, corrections appended
#606 Tier 2 adr-migrate runs through adr import
#607 ADR-311: the policy gates removed; path rewrites exact; import scan names what it skips
#588, #590, #594 CI on record-only changes; the consider way asks probes in the conversation, in batches
#608 main merged in; adr-tool 2.1.0

#604 was closed unmerged: an attempt to enforce frozen records by rebuilding history, superseded by ADR-311.

Release notes (draft for /release)

adr-tool 2.1.0

  • New: adr import scan/apply, which converts v0 or structured records through editable sheets.
  • New: adr domain add/rename/move. Numbers never change, and paths are rewritten exactly.
  • New: adr consider, set, supersede and enact; queries with adr list --kind/--verb/--capability/--field/--group-by/--json.
  • New: adr new --kind/--verb/--capability/--agent/--model.
  • New: the evidence record kind; an optional repository: key.
  • Changed: ADR stands for Agent Decision Record. The adr.yaml template starts new projects on adr/v1.
  • Removed: the checks between records: frozen history, the capability add rule, a prior per change, precedent chains, Summary legibility, vocabulary stems and enactment inventories. baseline, surfaces and mutable_after_accept in adr.yaml are ignored.
  • Compatible: a corpus that 2.0.0 accepts lints the same under 2.1.0. v0 output is unchanged.

Guidance: the ADR way, the adr skill, project-init, project-audit and the agents describe adr/v1 and its conventions.

Test plan

  • make test-adr on integration: the goldens (v0 output byte-identical), import round trip, macro tests, conversion check, template test, adr lint --check, adr cite --check
  • adr lint: 0 errors; the warnings are 92 "imported record has no Summary yet". adr cite: 0/0.
  • Main's corpus lints identically under 2.0.0 and 2.1.0 (0/0)
  • Import rehearsed on scratch copies of two other local corpora, not named in any record. A v0-frontmatter corpus scans to sheets; an inline-metadata corpus scans after its metadata is moved into frontmatter, as the migration way describes.
  • After merge: dispatch tier 2 (live-fixture.yml, tier2_scenarios: adr-migrate adr-consider adr-way). The release flavor clones main, so it can only run after this lands.
  • After release: ways update on a workstation, and a fresh session converts a legacy corpus

make test-adr lints the repo's own records (#586), but the workflow only
triggered on tool paths, so a records-only PR (the migration) skipped it.
docs(adr): ADR-306 proposes adr import through a round-trip import sheet
… in the record

Found handing over ADR-306: the probes sat in its Summary, the handover
asked the operator to read the record, and the operator could not tell
what the probes were.
…arkdown or YAML/JSON

Names the two probes so considered entries can cover them. Adds the rule
the second answer settled, and states that an imported record describes
intent, which the implementation usually drifts from.
…riter (ADR-306 §3)

sheet.py holds the import sheet's writer: canonical v1 key order, list
items indented as the corpus writes them, dates unquoted, and a Summary
skeleton for decisions. adr new builds an empty sheet from its arguments
(--kind, --verb, --capability, --agent, --model) and renders it; fields
not given stay empty for lint to name. A new rule flags placeholder text
left from adr new: a warning while proposed, an error after. An empty
basis now reports once. v0 output is unchanged.
… writer checks its round trip

- empty_record builds a new record from the kind's schema (verb, requires,
  lifecycle, defaults.status), not from the name 'decision'
- render_record writes only what the sheet holds, appends the body
  verbatim, and reads its frontmatter back, raising on a mismatch
- multi-line strings are written double-quoted on one line, so a '---'
  inside a value can't end the frontmatter; empty fields are written ~
- an invalid date stays a string for lint instead of raising
- new refuses a verb or agent the kind can't take, an unknown verb or
  capability, and explains a v1 config with no kinds; kind names match
  case-insensitively
- the placeholder rule matches the skeleton's bracketed prompts anywhere
  on a line outside fenced code, and lives with the other v1 rules
- the v0 template reuses the shared body skeleton (byte-identical)
- basis: [] is left to the requires rule only where the kind requires it
ci(adr): run the adr tool workflow when records change
docs(adr): ask the probes in the conversation, not only in the record
docs(adr): ADR-306 records the probe answers
feat(adr): adr new writes adr/v1 through the sheet writer
docs(adr): ask several probes through the choice tool as one batch
scan writes one import sheet per record to docs/architecture/.import/,
which ignores itself. The v0 reader maps status by the ADR-304 §7 table,
carries date, deciders, related and the supersession edges, keeps every
other key in unmapped, and lists verb, capability, basis and the agent
in todo with ranked capability candidates. A v1 record is read as itself.

apply writes each sheet with an empty todo through render_record, over the
source when it sits where the target implies, and adds imported: {from,
format} for a non-v1 source. It skips sheets with open todo items unless
--partial, refuses a source changed since the scan, leaves an unedited v1
record byte-identical, and lints what it wrote.
A v1 record carrying imported gets a missing Summary as a warning on
every status, and imported must be {from, format}. The frozen-decision
check lets an imported record fill what its import left empty and gain
its Summary later: a sheet applied with --partial is committed with
empty fields, and completing it is the migration, not an edit.
Goldens cover a scan of the v0 corpus, a v1 record scanned as itself,
apply with open todo items, --partial, a completed sheet, a source
changed since the scan, and lint of an imported record before and after
it is completed. tests/adr-import-roundtrip.sh scans and applies every
record under docs/architecture in a temporary copy, checks bodies, key
coverage and idempotence, and runs in make test-adr.
An optional, loosely shaped observable list says what someone should see,
run or try when a decision holds. It may change after acceptance, and the
agent demonstrates it in the handover before asking. Code follows once #595
merges, since both touch the v1 integrity rules.
…bservable when drafting

The operator may name one, decline, or hand the observing to the agent,
which iterates on the work until it can show the outcome.
docs(adr): ADR-307 proposes observable outcomes on decisions
apply carries unmapped source keys into the record as imported.unmapped,
so no value depends on a todo item being honoured. Text above the H1 moves
into the body just below it. A sheet that renumbers or moves an in-tree
record is refused (adr domain move, ADR-306 §6), and every write checks the
number against the domain's range. --partial no longer writes past a todo
item lint cannot find again: a Deprecated record's note, or a number or
domain mismatch.

A rescan keeps a sheet that differs from a fresh scan unless --force.
apply refuses a record with uncommitted changes unless --force, and a
sheet whose body is missing when the source has one. load_sheet checks
the number (quoted sub-parts, no path characters), summary, body, todo
and unmapped types, and one bad sheet no longer stops the batch. A UTF-8
byte order mark is reported as one.
…skip symlinks

Found running the reorganization: ADR-119 and ADR-121 moved to attend/
kept bare links to ADR-123, which stayed, so six links broke; the
conversion check caught it. And docs/scripts/adr, a tracked symlink to
adr-tool, was written through, editing the generated tool.
…list queries frontmatter

Record edits go through a surgical frontmatter editor: only the touched
field's lines change, a new field lands at its place in the v1 key order in
the tool's YAML style, and every edit is read back before it is written.

- consider appends a considered entry, refusing a --covers name that is not
  a probe named in the Summary
- set takes key=value, key+=item and key-=item as YAML, refusing a frozen
  field once the record has left proposed unless --force
- supersede writes both sides of a supersession, or --amends on the new
  record only, checked against the kind's edges
- enact sets a quoted commit hash on an accepted cut or retire
- list gains --field, --kind, --verb, --capability, --group-by and --json
Each edit keeps the file it wrote, so the goldens show that untouched lines
stay as they were. Refusals covered: unknown probe, missing said or via,
frozen field, status with its own command, unknown key, supersession across
kinds or from a closed record, enact on the wrong verb, status or hash.
adr domain move --plan: each record went to the area of its first
capability. No number changed; every path reference was rewritten, and
INDEX.md regenerated. Two links to the archived ADR-112, broken before
the move, now point into the archive.
adr consider, set, supersede, enact, and list --field/--group-by/--json.
Both branches appended goldens and skill text; both kept.
Twelve notes become records in their intent areas, each git-moved so
history follows, with frontmatter, an ADR H1 and the note's first
commit date. Two are specs: the settings.json merge contract
(ADR-500, platform) and the attend envelope fields (ADR-401, attend).
The other ten are accepted evidence: ADR-183 and ADR-189 to ADR-192
in ways, ADR-400 in attend, ADR-600 to ADR-603 in practice.

Every path to docs/design-notes is rewritten, and the moved notes'
relative links are re-based. A link to ADR-112 that was already broken
now points into the archive. docs/design-notes/README.md is removed;
docs/README.md points at the records instead. The historical one-shot
scripts in tools/scripts keep their old paths.

adr cite: 0 warnings.
The help text, list header and generated INDEX say "Agent Decision
Records". Under adr/v1 the INDEX header describes the v1 record: kind,
status set, capability, a decision's verb, basis and Summary, and
permanent numbers. A v0 project keeps its format block under the new
name. Goldens change only in those lines.
- ADR way macro: the v1 command table covers consider, set, supersede,
  enact, list queries, import scan/apply and domain add/rename/move;
  the record format names the evidence kind (ADR-309) and permanent
  numbers in intent folders (ADR-310); enactment goes through adr enact.
- adr-context: query the corpus with adr list --kind/--capability/
  --field, read a decision's Summary first, follow supersedes/amends.
- adr/migration: existing records convert through adr import scan and
  apply; conversion keeps numbers. MADR and adr-tools have no reader
  yet: a hand-written sheet, or park as legacy.
- skills/adr, project-init, project-audit: contract-aware, with v1
  kinds, lifecycle commands, import, and ADR-305 baseline capabilities.
- Agents: system-architect creates records with adr new and hands the
  probes back to the calling session before accept; workspace-curator
  places records by area under docs/architecture; the orchestrator and
  analyst follow the v1 lifecycle.
- The expansion reads "Agent Decision Record" in README, the docs
  skill and the governance policy, whose ADR section is rewritten.
The template declares contract: adr/v1 with the decision, spec and
evidence kinds, one placeholder capability (a v1 corpus with none fails
lint), a commented baseline block (ADR-305) and an empty surfaces map.
Deleting the contract line and the v1 blocks keeps a project on adr/v0.
The adr skill and project-init describe the new default.
- adr set refuses every status change on a v1 record without --force,
  naming supersede and archive as well as accept, reject and abandon.
  status=proposed on an accepted record used to pass and unfreeze it.
- The adr.yaml template's baseline is active, so a freshly vendored
  project lints clean; the skill's vendoring step dates it. A new test
  vendors the template into an empty repo and requires lint --check to
  pass (make test-adr).
- The migration way and project-init restore the preparation steps
  import scan needs: files named ADR-NNN-*.md with YAML frontmatter.
  Sequential 0001-*.md files and inline metadata are renamed and given
  frontmatter first.
- project-init takes the v1 scaffold record through adr consider and
  accept; workspace-curator sends records from outside
  docs/architecture/ through import, gives evidence a capability.
- Docstring and example fixes in header.py, cmd_list.py and the skill.
adr/v1 phase 3: citations, evidence kind, permanent numbers, v1 guidance
…rrections

Both records were edited in place after acceptance. The accepted bodies
are restored, and each record ends with a Corrections section carrying
the edits and why they were made: ADR-306's probe labels, the renamed
example folder, the body formats from the operator's probe answers,
the --partial exceptions and the imported fields as built; ADR-307's
pointer to the consider way. None changes what was decided.

The sentence on text between a source's frontmatter and its title
describes the reader, so it moves to the adr skill's import notes.
…; git keeps the history

Records are a ledger of decisions and who made them. The tool writes them,
checks their shape and that their references resolve, and enforces no
policy between records. Supersedes ADR-305; amends ADR-304 §1, §6, §11 and
§12 and ADR-308 §2.
ADR-311 keeps the checks a reader of one record relies on and removes the
ones that recreate git or enforce how decisions relate:

- the frozen check, its git history reads, mutable_after_accept, and the
  frozen-field refusals in adr set and adr supersede (supersede loses
  --force, which only served them)
- the capability add check and baseline
- the prior-per-change check, for one capability or a list
- the precedent chain; a precedent must still resolve to a record of a
  kind the basis edge accepts
- the vocabulary layer check (the stemmer moves to import, its only user)
- the Summary probe and inversion check; the Summary heading stays required
- cut and retire enactment in adr cite, surface inventories, retire
  targets and surfaces; enacted keeps its shape check
- the considered-on-accept requirement; considered keeps its shape check

adr accept, reject and abandon run the record's own file rules and no
longer re-lint the corpus. adr cite accepts --no-inventory as a no-op so
existing scripts keep working. An adr.yaml that still carries baseline,
surfaces or mutable_after_accept is not an error; the keys are ignored.
… names what it skips

Ported from fix/603-relocate-safety (8de1520), without its frozen-history
work, which ADR-311 removes:

- adr domain move and rename rewrite a relative link, a path from the repo
  root, or a URL into this repository at a branch (raw URLs included) when
  it resolves to what moved. SHA and tag permalinks, fenced code in
  Markdown, and paths touching a backslash stay as written.
- adr.yaml's repository: names this repository for that URL match; without
  it the origin remote is used. Lint checks its shape.
- --dry-run lists each line a rewrite would change, adr.yaml's own key and
  folder lines included.
- import scan names each Markdown file it passes over, names hidden folders
  it does not enter, counts other files by extension, and skips the tool's
  own files only at the top of the folder scanned.
- tests/adr-conversion-check.sh maps moved paths itself instead of calling
  the tool's rewriter; make test-adr runs adr cite --check, and the
  migration way's example number is marked.

The CI checkout keeps its default depth: nothing reads git history now.
…ger checks

The v1 guide in the ADR way's macro, the consider way, the adr skill,
project-init, project-audit and the system-architect agent no longer say
the tool freezes accepted records, requires a baseline or an add per
capability, a prior per change, a precedent chain that leaves the corpus,
or enactment inventories. In their place they teach the ADR-311
conventions: correct an accepted record by appending, record a change as a
new decision that names what it replaces, and quote the operator. Review
catches a record that breaks them, and git keeps every earlier version.

The Summary format stays as guidance, and the adr skill documents
adr.yaml's repository: key.
…finds records in any folder

Setup and check hardcoded docs/architecture/system/, which ADR-310 renamed;
they now find each record where the corpus keeps it, so the scenario runs on
main's layout and the integration branch's. The prompt routes the migration
through adr import scan and apply (#589).
docs(adr): ADR-306 and ADR-307 keep their accepted text and append corrections
test(live): adr-migrate converts through adr import; folder-agnostic paths
- import scan reads a record named ADR-NNN.md with no slug, as the corpus
  glob does.
- The rewriter leaves fenced code alone at any indent, including a fence in
  a list item, and treats an uppercase SHA as a permalink.
- The lifecycle refusal says the command checks the record; the macro says
  accept checks the record's fields and lint checks its references.
- A golden pins cite --no-inventory, kept for doclint.
- ADR-311, not yet merged, names the two removals it left out: cite's
  warnings on citations of a cut capability, and a retire's targets check.
  It amends ADR-304 §5 as well.
adr: the tool checks shape and references; git keeps the history (ADR-311)
Main's 2.0.0 ships the adr/v1 contract. 2.1.0 adds import, the domain and
record-editing commands, and the evidence kind, and removes checks
(ADR-311). A corpus 2.0.0 accepts lints the same: main's records give 0
errors and 0 warnings under both. Keys 2.0.0 read (baseline, surfaces,
mutable_after_accept) are ignored, and cite --no-inventory still parses.
chore(adr): merge main into integration; adr-tool 2.1.0
@aaronsb
aaronsb merged commit 0c59055 into main Sep 28, 2026
35 checks passed
@aaronsb
aaronsb deleted the integration/adr-v1 branch September 28, 2026 14:30
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