Skip to content

feat(adr): agent-ways adopts adr/v1 and migrates a first sample - #581

Merged
aaronsb merged 9 commits into
mainfrom
feat/adr-adopt-v1
Sep 27, 2026
Merged

aaronsb merged 9 commits into
mainfrom
feat/adr-adopt-v1

Conversation

@aaronsb

@aaronsb aaronsb commented Sep 27, 2026

Copy link
Copy Markdown
Owner

Stacked on #580 → #579 → #578 → #576 → #575 → #574 → #573. Closes #566. Human-gated: this sets direction for this repo.

Summary

  • Decided: agent-ways' adr.yaml declares contract: adr/v1. That covers:

    • the decision and spec kinds
    • a capability vocabulary of 13 entries
    • four surface namespaces
    • cite.exclude for fixtures and the adr tool's own sources
    • no hard-coded default deciders, as you asked through the kg session

    A first sample migrates:

    • ADR-179: retire on install, targeting cli:migrate, enacted by b4f63aa6
    • ADR-186: add on testing
    • ADR-123: change on disclosure, superseding records still on v0

    ADR-304 gains a second considered entry with your words after reading it in full.

  • Trades away: every new decision in this repo now needs the v1 frontmatter and a Summary, as ADR-304 intends. The 91 unmigrated records keep linting as v0.

  • One-way? No. Removing contract: from adr.yaml returns the repo to v0 rules, and the migrated records' extra fields are ignored under v0.

  • State after this PR:

    • adr lint: 0 errors. There are 12 warnings, 11 of them capabilities with no add decision yet, expected while records are on v0.
    • adr cite: 0 errors and 128 warnings, all citations of superseded records.
  • Probes:

    • Not confident: the capability list is yours to write. I seeded 13 from the components and the ADR history: adr, docs, matching, disclosure, authoring, attend, install, config, introspection, governance, loop, guards, testing. Split, merge or rename freely; each one-liner is the capability's only description.
    • Not confident: the migrated records record agent: {name: Claude, model: unrecorded}, because pre-v1 records never named a model. ADR-304 didn't anticipate historical records. Is unrecorded the right convention, or should agent be optional for migrated records?
    • Confident: no migrated record invents an operator basis. Each is grounded in its own text: evidence plus precedent.
    • Not confident: the Summaries on migrated records are written after the fact from the record's own text. They're honest but retrospective. The probes read as questions about a past decision.
  • Inversion: one end migrates nothing and leaves every record v0 until touched, per §7. The other end migrates all 91 now. This PR adopts the contract and migrates a sample that exercises each shape: an enacted retire, an add, a change over v0 priors. Is the sample right, or should adoption wait until more of the corpus moves?

Rehearsal (tier 2 container)

tests/fixtures/docker/scenarios/adr-migrate had a real agent (claude-sonnet-5) migrate ADR-179 and ADR-186 on a copy of this corpus. It cost $1.10 over 40 turns. It made the same choices as this PR:

  • Kind and verb: retire on install for ADR-179, add on testing for ADR-186.
  • Basis: evidence plus precedent.
  • No invention: no operator entry, and model: unspecified rather than a guess.

Its check.sh never ran, because I switched branches during the run and the fixture mounts live from the working tree. A rerun is in progress, and its result will follow as a comment.

Not done

  • The Deprecated mapping (ADR-101/102): it needs a call on whether anything replaced them (§7).
  • A split of a mixed decision-plus-spec record: ADR-186 would be the candidate; it stays whole in this PR.

- ADR-179 is a retire on install, targeting cli:migrate, enacted by
  b4f63aa, the commit that removed the migrator.
- ADR-186 is an add on testing.
- ADR-123 is a change on disclosure. It supersedes records still on v0, so
  lint warns until they migrate.

Each keeps every original field and section, and gains a Summary drawn
only from the record's own text. Each basis is grounded in the record's own
evidence and precedents. None gets an operator entry, since none of these
records quotes the operator. The agent model is recorded as 'unrecorded',
because these records never named one. The tier 2 migration rehearsal made
the same choices.

Example ADR numbers in two agent prompts and the migration way carry an
adr-cite-ignore marker. adr cite on this repo is now 0 errors and 128
warnings, which are citations of superseded records. Lint under adr/v1 is 0
errors; 91 records remain on v0.
@aaronsb

aaronsb commented Sep 27, 2026

Copy link
Copy Markdown
Owner Author

Review: adr/v1 adoption and the first migration sample

What this changes: declares contract: adr/v1 in adr.yaml with 13 capabilities, 4 surfaces and cite.exclude. Migrates ADR-179, ADR-186 and ADR-123. Adds a second considered entry to ADR-304. Marks example ADR numbers with adr-cite-ignore. Adds the adr-migrate tier 2 scenario and per-scenario max_turns.

Verdict: revise before merge. Two findings matter most. The adr-migrate scenario now passes vacuously. ADR-123's Summary misstates its own decision. ADR-179 is a faithful migration.

Gates run

  • adr lint in check mode: 0 errors, 12 warnings (11 capabilities with no add; ADR-123's priors still v0). rc 0.
  • adr cite with no inventory: 0 errors, 128 warnings. rc 0.
  • The test-adr make target: pass.
  • The adr macro test script: 31 passed, 0 failed.
  • The stat of commit b4f63aa confirms it is the removal commit: it deletes migrate.rs (267 lines) and migrate_exec.rs (734 lines), plus Commands::Migrate, cache_root_canonical and allow_in_place, and it is on origin/main. enacted: "b4f63aa6" is correct.

High

1. adr-migrate passes vacuously once this PR lands. tests/fixtures/docker/scenarios/adr-migrate/setup.sh:6,11
run-tier1.sh:134 clones /src into $APP_DIR, and setup.sh copies $APP/docs/architecture from that clone. On this branch, ADR-179 and ADR-186 are already v1 with a Summary and a basis. So the "before" snapshot is already migrated. The agent has nothing to do, and every assertion in check.sh passes: it declares v1, it lints clean, and it has no operator entry. The rerun this PR says is in progress will measure nothing. The earlier rehearsal only worked because the tree it mounted predated d3e941a.
Fix: keep v0 copies of the two records under scenarios/adr-migrate/v0/ and have setup.sh copy them over the corpus. Also add a precondition that fails setup when a snapshot already has ^contract:.

2. ADR-123's Summary does not match its body. docs/architecture/system/ADR-123-firing-dynamics-progression-axis-unification.md:32-36

  • Decided: "one progression axis drives firing for both" is wrong. The body decides on one engine that takes an axis from each caller: wall clock for attend and token position for ways (§1, lines 77-81). §4 (lines 141-161) argues the two axes must differ.
  • Inversion: "the other end drives everything from one global clock. This shares an axis" frames the decision as a shared axis, which is the framing the body rejects (Context line 45, and the Clock trait alternative at line 276).
  • Trades away: "the per-subsystem tuning that the superseded models allowed" is backwards. Curves become per-way parameters (§2), which adds tuning. The costs the body names are different: a refactor across three crates, removing redisclose with no shim, and recomputing attend's parameters (lines 260-264, 268).
  • One-way? No conflicts with "no graceful rollout — the old and new parsers cannot coexist" (line 263). At minimum, name the frontmatter break.
  • The Summary leaves out reactive firing (§5, postcheck), ways tune (§8) and the 2026-07-29 amendment. They are a large part of what was decided.

Suggested Decided line: "one firing engine with pluggable curves serves attend and ways; each caller supplies its own monotonic axis (seconds for attend, token position for ways). Ways also gains reactive firing through postcheck, and redisclose is replaced by curve: with no shim."

Medium

3. ADR-123 omits the operator direction its body quotes. Line 280 says "the code reuse Aaron surfaced in this conversation". Line 304 says "Rejected on Aaron's direction: shims are carryovers…". ADR-304 §7 says an operator basis migrates "where the record already quotes operator direction". This is under-reporting, not invention. The PR's Confident probe ("each is grounded in its own text: evidence plus precedent") is therefore incomplete for ADR-123. Fix: add operator: aaronsb, level: guided, said: "<the line-304 wording>", paraphrase: true, via: ADR-123 Alternatives, session 2026-04.

4. The evidence bases point back into the record itself. ADR-179:10, ADR-186:8, ADR-123:8. "The deferral audit in this record's Context" meets the lint rule, but it has the self-referencing shape that §11 is meant to break. The facts each record rests on sit outside the corpus, so cite those directly:

ADR-123's "divergent decay models" also contradicts its Context: "The math is identical across both tools … Only the units differ" (line 45). The evidence is duplicated math, not divergent models.

5. ADR-123 spans more than disclosure, and the contract can't express that yet. The record supersedes ADR-119 and ADR-121 (attend), ADR-112 (the session ledger, i.e. introspection) and ADR-104 (disclosure). It also adds a reactive trigger path (matching) and ways tune (introspection). When 119, 121 and 112 migrate, the rule that a change supersedes a decision on the same capability will fail for 3 of the 4 priors. Only constrain may carry a list. This is contract friction of the kind Rollout step 3 wants recorded. Open it as a change on capability: adr: either let change carry a list, or scope the same-capability check per superseded edge.

6. ADR-186 as add on testing is shaped by the ledger rule rather than by the record. Its Context (line 36) lists tests that already existed: Rust unit tests, session_sim and the reconcile tests. So testing was already active, and ADR-186 extends it. §3 defines add as bringing a capability into being. The testing one-liner seems written to fit ("…and the live install fixture"). The general gap is that a capability which predates the corpus has no honest add. That will recur for all 11 capabilities that warn. Options: a baseline add per capability grounded in evidence: repo at adoption commit, or a contract rule that capabilities declared at adoption start out active. Also a change on adr.

7. check.sh checks too little. scenarios/adr-migrate/check.sh:15-28

  • It inspects only basis[].operator.said. A fabricated considered: or concern: goes through unchecked.
  • The fixed-string match against the old text passes when the agent relabels any sentence of the record's prose as operator speech. That relabelling is the fabrication §11 describes. Neither record quotes operator direction, so the honest assertion is simpler: no operator key anywhere in the frontmatter.
  • ADR-179's body is hard-wrapped. A faithful quote that spans a wrapped line fails the fixed-string match, which produces a false failure.
  • Nothing checks that the "decisions and history" stayed intact. The prompt demands it, but an agent that deletes the body still lints clean. Diff the body against the snapshot and allow only the inserted ## Summary block.
  • The rubric is nearly free: kind matches any mention, and add matches "address" or "added". A threshold of 2 of 3 gives little signal. Tighten to word-bounded retire and add, or check the chosen verb in the frontmatter directly (ADR-179 should be retire with a cli: target).

Low

8. adr-cite-ignore sits inside fenced examples in subagent prompts. At agents/workflow-orchestrator.md:153,158 (a markdown-fenced status template) and agents/workspace-curator.md:91 (a fenced dialog), the marker becomes part of text the subagent imitates, so it may emit <!-- adr-cite-ignore --> in its own reports. Fix: list the two agent files in cite.exclude, or use ADR-NNN placeholders, which CITE_RE doesn't match. workspace-curator.md:35 is prose, and the comment is invisible when rendered. migration.md:47 is a trailing shell # comment, so the rename command still runs as written.

9. cite.exclude hides real citations. hooks/ways/documentation/adr/src and hooks/ways/documentation/linting hold about 45 real citations (29× ADR-304, 9× ADR-303, ADR-177, ADR-167, ADR-302). The exclusion drops them to hide about a dozen example numbers (ADR-038, ADR-051, ADR-101, ADR-005, ADR-104#2, ADR-411, ADR-603). Line markers on those dozen lines would keep the tool's own citations under the maintenance loop.

10. An empty max_turns file breaks the run. At tests/fixtures/docker/run-tier2.sh:107, an empty or non-numeric file gives an empty --max-turns value. Fall back with turns=${turns:-$MAX_TURNS}.

11. ADR-186's Summary is slightly loose. Tier 1 runs on PRs that touch the install path and on pushes to main, not on every PR. Trades away also leaves out the network dependency named in Negative (line 77).

12. The new considered entry on ADR-304 comes after acceptance, but §12 defines considered as weighing "before acceptance". mutable_after_accept allows the edit, and via says honestly that it came after the merge. I can't check the quote against any repo artifact, so the operator should confirm it is verbatim. covers: [] is honest.

13. The capability vocabulary has overlaps and gaps.

  • Overlaps: config (permissions) and guards (security baseline) both fit ADR-152 and ADR-175. matching (scoring) and introspection (calibration data) both fit ADR-134, ADR-156 and ADR-158. adr and docs share doclint's citation checks.
  • Gaps: the method the ways teach has no home: ADR-128, ADR-132, ADR-138, ADR-176, ADR-178 and ADR-180. authoring covers files and CLI, not the substance of the guidance. There is also no clear home for ADR-139 (localization), ADR-111/ADR-151 (binary structure) or ADR-185 (the CLI output contract; that one may be a constrain on *).

14. Smaller points.

  • ADR-179's cli:migrate is ambiguous across the repo's four CLIs. Consider cli:ways-migrate before any cli inventory lands.
  • adr.yaml does not declare basis_sources, so the tool default applies. §4 shows it in the contract, and declaring it keeps the file self-describing.

What is solid

  • ADR-179 is a faithful migration. retire on install with targets: [cli:migrate] matches a record that removes a surface and keeps the capability. The Summary's trade, reversibility (the tag escape hatch) and inversion (keep the guards, drop the command) all come straight from the body. The enactment SHA is correct.
  • The precedents (ADR-144, ADR-184, ADR-113) are all Accepted.
  • agent: {model: unrecorded} is an honest convention for historical records, better than guessing.
  • No migrated record invents an operator quote.
  • Setting defaults.deciders: [] follows the §7 rule against a hard-coded decider.

AI-assisted review via Claude

…re records land

- ADR-123 and ADR-186 go back to v0.
  - ADR-123's Summary misread its own body, and the record spans several
    capabilities, which no single-capability change expresses.
  - ADR-186 is an add on a capability, testing, that existed before any
    record, so no add is honest. That is a contract gap, filed for the
    operator.
- ADR-179 keeps its migration, with two fixes.
  - Its evidence points outside the record, to the release tags.
  - Its target is cli:ways-migrate, since agent-ways ships four CLIs.
- The two subagent prompts move into cite.exclude instead of carrying
  markers a subagent could copy. adr.yaml declares basis_sources
  explicitly.
- The frozen snapshot reads the default branch's history (origin/HEAD)
  when there is one. A decision still in review on a feature branch can be
  revised, and it freezes where it lands. Without a remote default, HEAD's
  history counts, so the goldens are unchanged.
…on relative paths

Found by the tier 2 migration rehearsal: the agent migrating ADR-179 and
ADR-186 reported that adr lint on a relative path falsely failed precedent
grounding. The grounding map keys on corpus paths, which are absolute, and
a record parsed from a relative argument never matched them. Arguments are
now resolved. A regression golden lints ADR-109, which is grounded only
through precedent, by relative path.

The adr-migrate scenario, per the #581 review:
- setup refuses records that are already v1
- check fails on any invented operator basis, considered or concern entry
- check confirms every original heading survives
- the rubric regexes are word-bounded, with a threshold of 3
An empty max_turns falls back to the default. The fixture's CLAUDE.md warns
that /fixture is mounted live, so the branch must not change during a run.
@aaronsb

aaronsb commented Sep 27, 2026

Copy link
Copy Markdown
Owner Author

Review addressed in ce184a6 and b360990.

The sample is now narrower and honest. ADR-179 stays migrated, with its evidence pointing outside the record (the release tags) and its target cli:ways-migrate. ADR-304 keeps the new considered entry; it is quoted verbatim from the operator's message after merging #559. ADR-123 and ADR-186 go back to v0:

  • ADR-123: its Summary misread the body, and it spans capabilities (item 5).
  • ADR-186: an add on a capability that predates the corpus isn't honest (item 6).

Items 5 and 6 are a real contract gap, filed as #582 for the operator: a baseline add, capabilities that start active, or a multi-capability change.

Other items:

  • 8: the subagent prompts moved to cite.exclude, and their markers are gone.
  • 10: an empty max_turns now falls back to the default.
  • 14: basis_sources is declared explicitly.
  • 9: cite.exclude stays broad. Marking the ~12 example lines in the tool's own source is a follow-up, and the trade-off is noted.

Item 1 and item 7 (the rehearsal):

  • setup restores both records from main's merge-base and refuses to run if they are already v1
  • check fails on any invented operator basis, considered or concern entry, and confirms every original heading survives
  • the rubric is word-bounded, with a threshold of 3

The first real rehearsal (both records starting from v0) turned up a tool bug. The migrating agent reported that adr lint on a relative path falsely failed precedent grounding, because the grounding map keyed on absolute paths. Fixed, with a regression golden. A rerun is in progress.

New design fix: the frozen check caught my own mid-review edit to ADR-179. A record now freezes where it lands: the snapshot reads the default branch's history (origin/HEAD), so a decision still in review can be revised.

For the operator (item 13): the capability list's overlaps and gaps are yours to call. Candidates: config/guards, matching/introspection, and no home yet for the method, localization, binary structure or the CLI output contract.

@aaronsb

aaronsb commented Sep 27, 2026

Copy link
Copy Markdown
Owner Author

Rehearsal rerun: adr-migrate passes 10/10, starting from v0 with the hardened check, in 31 turns for $0.75 on claude-sonnet-5. Both records declare v1, lint clean, invent no operator statement or considered/concern, and keep every original section.

guards folds into config, introspection into matching. method and cli are
new; localization joins disclosure and binary structure joins install.
Operator's call on the #581 review.
@aaronsb

aaronsb commented Sep 27, 2026

Copy link
Copy Markdown
Owner Author

Operator decisions from this morning's review:

  • Capability list: 82aef7e drops guards (folded into config) and introspection (folded into matching), and adds method and cli. Localization is now under disclosure, and binary structure is under install. That leaves 13 capabilities.
  • adr/v1: capabilities that predate the record corpus have no honest add decision #582: declared capabilities start active. That lands as a separate PR after this one: a change decision on capability: adr that amends ADR-304, the lint update, and the migration of ADR-123 and ADR-186.
  • Decisions 3–8: accepted as built. The same amendment widens ADR-304 §6's stale-citation wording to cover deprecated, rejected and abandoned targets.
  • Decision 9 (the verbatim quote in ADR-304's second considered entry) is still open for the operator to confirm.

Base automatically changed from feat/adr-consider-way to main September 27, 2026 15:15
@aaronsb
aaronsb merged commit cec7a37 into main Sep 27, 2026
6 checks passed
@aaronsb
aaronsb deleted the feat/adr-adopt-v1 branch September 27, 2026 15:25
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.

adr v1 (7/7): agent-ways adopts adr/v1 and migrates a sample of records

1 participant