ADR-045: Cross-Tool AGENTS.md as Generated Spine
Status
Section titled “Status”Accepted
Context
Section titled “Context”ADR-036 split CLAUDE.md into a layered constitution (CLAUDE.md + .claude/engineering.md + .claude/workflow.md + .claude/stack.md) so each layer could have its own tone and update cadence. That work shipped. The cross-tool side did not.
The pre-existing parallel pattern — a hand-maintained .windsurfrules mirroring the same content — accumulated drift in measurable ways:
.windsurfrules(179 lines) re-encoded substantially the same material as the three.claude/*.mdfiles combined, but was never updated when the layered files were created. It still carried the pre-ADR-036 organisation.airules.example(131 lines) was stale Chrome Extension / React / Shadcn / Express boilerplate with unfilled[Project Name]placeholders, yet two setup docs (docs/ai-context/ai-rules-setup.mdanddocs/ai-context/INDEX.md) instructed users tocp airules.example .windsurfrules— which would have overwritten the working Windsurf rules with Chrome Extension boilerplate. An active footgun in the published Starlight docs.- The Rules of Engagement section drifted across three files: 10 halt-on-violation rules in
CLAUDE.md(the canonical list, including the ADR-037 failing-test rule), 7 suggestive-tone rules indocs/ai-context/INDEX.md, and 0 explicit rules in.windsurfrules(embedded in prose). Three renderings, three totals. - The “Multi-tool sync” footer in
.claude/stack.mdexplicitly acknowledged the manual-sync requirement as an unsolved problem.
By 2026, the ecosystem solved the cross-tool problem with a single canonical file at the repo root: AGENTS.md, governed by the Agentic AI Foundation under the Linux Foundation. Cursor, Codex CLI, GitHub Copilot, Windsurf, Aider, Devin, Zed, Continue, Amp, and Amazon Q Developer all read it natively. Claude Code reads CLAUDE.md (with @import support), but CLAUDE.md and AGENTS.md compose cleanly: Claude uses the layered files, every other tool reads AGENTS.md.
ADR-036:168 anticipated this work: “.windsurfrules parallel-rules pattern continues but should mirror the new structure when next updated.”
Decision Drivers
Section titled “Decision Drivers”- Single source of truth across tools. The drift measured above happened because there were two hand-maintained files that intended to encode the same rules. Eliminating the manual-sync requirement eliminates the drift.
- Preserve ADR-036’s separation of concerns. Stack facts churn with every dependency bump; halt-on-violation rules change once a year. Collapsing the layered files into a single hand-authored
AGENTS.mdwould undo ADR-036’s tone/cadence reasoning. - CI-enforceable. “Don’t drift” as a rule isn’t enforceable. A build script that fails CI when
AGENTS.mdis stale is enforceable. The project’squality:cichain already runsformat:check,lint,lint:md,check,test:unit,agents:check; adding one more step is consistent with the existing halt-on-violation discipline (ADR-039). - Template demonstrability. This is a template repository. The pattern users see when they fork is the pattern they will keep. A generated-artifact pattern with CI enforcement teaches the right lesson; a hand-maintained mirror teaches the wrong one.
- Forward compatibility. Adding
GEMINI.md(Gemini CLI uses its own filename),.cursor/rules/(Cursor’s glob-scoped overrides), or any other tool-specific file later is a one-line addition to the build script, not a new manual-sync surface.
Considered Options
Section titled “Considered Options”Option 1: Status quo — keep .windsurfrules as a hand-maintained mirror
Section titled “Option 1: Status quo — keep .windsurfrules as a hand-maintained mirror”Description: Leave the current architecture in place. Add a CI lint rule that warns when CLAUDE.md and .windsurfrules diverge. Continue maintaining the “Multi-tool sync” footer.
Pros:
- Zero migration cost.
- No new files in the tree.
Cons:
- The audit above measured concrete drift across three files; convention has demonstrably failed to prevent it.
- Adoption of any new tool (Cursor, Codex CLI, etc.) requires another hand-maintained mirror file — drift surface grows linearly with tool count.
- Doesn’t compose with the AGENTS.md ecosystem standard; the template stays out of sync with the broader 2026 convention.
- Doesn’t address the
airules.examplefootgun.
Option 2: Hand-authored AGENTS.md as new source of truth; delete the layered files
Section titled “Option 2: Hand-authored AGENTS.md as new source of truth; delete the layered files”Description: Make AGENTS.md the canonical hand-edited file. Move all content from .claude/engineering.md, .claude/workflow.md, .claude/stack.md into AGENTS.md sections. CLAUDE.md becomes an @import of AGENTS.md. .windsurfrules becomes a thin overlay pointing to AGENTS.md.
Pros:
- Operationally simple — no build step.
- One file to read, one file to edit.
Cons:
- Reverses ADR-036. The tone/cadence reasoning that justified the layered constitution still applies: stack facts shouldn’t sit in the same file as halt-on-violation rules because the update cadences are an order of magnitude apart, and conflating them makes the rules read advisory while the facts read binding.
- Loses the per-layer file headers that scope each layer’s intent (“Strong defaults with named exceptions” in
engineering.md; “Technology facts” instack.md). - Risks an
AGENTS.mdthat grows monolithically over time — the same failure mode that prompted ADR-036.
Option 3: Generated-artifact AGENTS.md (this ADR)
Section titled “Option 3: Generated-artifact AGENTS.md (this ADR)”Description: AGENTS.md is generated by concatenating CLAUDE.md + .claude/engineering.md + .claude/workflow.md + .claude/stack.md (in that order), with markdown headings demoted by one level so each layer becomes a top-level section under a single AGENTS.md title. A pnpm agents:build script writes the file. A pnpm agents:check script regenerates in-memory and diffs against the on-disk file, exiting non-zero on mismatch. The check runs in pnpm quality:ci. .windsurfrules shrinks to a thin overlay pointing at AGENTS.md plus the one Cascade-specific directive (rule citation).
Pros:
- Preserves ADR-036’s layered constitution intact — each source file keeps its single tone and update cadence.
- Drift becomes structurally impossible. The manual-sync footer disappears.
- Matches the repo’s existing validation-script pattern (
design:validate,budgets:validate,env:validate). - Adding a new tool with its own filename (
GEMINI.md, etc.) is a single additional generation target. - As a template, demonstrates a working anti-drift pattern users can keep when they fork.
Cons:
- One generated file checked into the repo (
AGENTS.md, ~265 lines). PR diffs that touch any source layer also touchAGENTS.md. - One additional build script and CI step to maintain.
- Generated
AGENTS.mdis longer than the AGENTS.md-spec’s informal ~150-line recommendation. Bounded by what the four source layers contain.
Decision
Section titled “Decision”We will go with Option 3 (Generated AGENTS.md) because it is the only option that both eliminates cross-tool drift structurally (via CI) and preserves ADR-036’s layered constitution (via build, not by collapsing). The diff-noise cost of regenerating on every layered-file edit is bounded and visible; the alternative — drift discovered later, in production — is unbounded and invisible.
Implementation
Section titled “Implementation”scripts/src/build-agents-md.tssupports two modes:build— writeAGENTS.mdcheck— fail with exit code 1 ifAGENTS.mddiffers from the regenerated output
package.jsongainsagents:buildandagents:checkscripts;quality:cichainsagents:checkaftertest:unit.AGENTS.mdis committed to the repo so external tools can read it without running a build. The file’s banner names it as generated and points at the regenerate command..windsurfrulesshrinks from 179 lines to ~20: a pointer toAGENTS.mdplus the Cascade-specific “explicitly state the rule” directive.docs/ai-context/INDEX.mdstops duplicating the Rules of Engagement (the 7-vs-10 drift was the motivating example for this ADR); it points atAGENTS.mdinstead.docs/ai-context/ai-rules-setup.mdis rewritten for the AGENTS.md pattern with a 2026 tool-support matrix.airules.exampleis deleted along with its 8 references across the docs/CHANGELOG/README/CONTRIBUTING surface. ADR-035’s Category 1 enumeration loses theairules.examplerow and gains anAGENTS.mdrow..claude/stack.md“Multi-tool sync” footer is replaced with a “Cross-tool spine” note pointing atAGENTS.mdand this ADR.
Constraint
Section titled “Constraint”AGENTS.md is not edited by hand. The pre-commit and CI gates enforce this. To change content that appears in AGENTS.md, edit the source layer in CLAUDE.md or .claude/*.md and run pnpm agents:build.
Consequences
Section titled “Consequences”Positive
Section titled “Positive”- Drift between Claude context and other-tool context becomes a CI failure, not a discoverable-much-later bug.
- New tools that read
AGENTS.mdnatively (Cursor, Codex CLI, etc.) get the full constitution with zero per-tool setup. - ADR-036’s layered structure stays intact —
AGENTS.mdis downstream of the layered files, not a replacement for them. - The
airules.examplefootgun (stale Chrome Extension boilerplate that two setup docs recommended copying) is removed. - The template demonstrates a working cross-tool anti-drift pattern users keep when they fork.
Negative
Section titled “Negative”- One generated file (~265 lines) is checked in. PR diffs that touch any source layer touch
AGENTS.mdtoo. This is intentional; the diff is the change reviewers should be aware of. - One additional build script and one additional CI step (
agents:check) to maintain. - Adding a future tool that uses its own filename (e.g.
GEMINI.md) requires extendingscripts/src/build-agents-md.tswith an additional generation target. Small cost; balances against the cost of hand-maintaining the new mirror.
Neutral
Section titled “Neutral”- The Claude Code experience is unchanged.
CLAUDE.mdstill loads first; the Precedence clause still points at the layered files;.claude/still holds skills, agents, and roles. - The Starlight docs site continues to render the AI-context section without functional change beyond the rewritten
ai-rules-setup.mdpage. - The
AGENTS.mdgenerated file is longer (~265 lines) than the informal AGENTS.md-spec recommendation of ~150 lines. The content all originates from layered files this template explicitly accepts as load-bearing constitution.
Validation
Section titled “Validation”pnpm agents:buildproducesAGENTS.mdcontaining all four source files’ content in the documented order, with markdown headings demoted by one level.pnpm agents:checkexits 0 whenAGENTS.mdis in sync, non-zero when it isn’t, with a regenerate-and-commit message.pnpm quality:ciincludesagents:checkand fails when any source file is edited without regeneration.wc -l .windsurfrulesreturns ≤ 40 (currently 20). No section of.windsurfrulesre-encodes content present inAGENTS.md.grep -r "airules.example"outside historical mentions returns zero matches — no setup doc or config references the deleted file. (amended 2026-08-02: the surviving mentions are theCHANGELOG.mdv0.2.0 and v0.9.0 notes, theai-optimized-means-ai-readyblog post’s retrospective, and this ADR’s own text; the original carve-out named only the v0.2.0 note.)- A trial Cursor / Codex CLI / Copilot session in the repo, asked “what’s our package manager?”, references pnpm 10.x from the Stack section of
AGENTS.md. - A trial Claude Code session asked the same question references pnpm 10.x from
.claude/stack.md. docs/ai-context/INDEX.mddoes not duplicate the halt-on-violation rules list; it points atAGENTS.md.
References
Section titled “References”- AGENTS.md specification — Agentic AI Foundation (Linux Foundation), 2026
- ADR-036: Layered constitution — the layering this ADR extends cross-tool; line 168 anticipates this work
- ADR-039: Halt-on-violation enforcement — the CI-gate pattern this ADR extends to context-file drift
- ADR-035: Template scope boundary — updated inline to swap
airules.exampleforAGENTS.mdin the Category 1 enumeration - ADR-034: Dual-purpose docs strategy —
docs/ai-context/ai-rules-setup.mdcontinues to serve both human and AI audiences after the rewrite
The agents:check script intentionally compares file content byte-for-byte rather than parsing markdown. Any whitespace, ordering, or character change in a source file produces a deterministic change in the generated output; the byte comparison catches every kind of drift, including the failure mode where a source file is edited but AGENTS.md is regenerated against a stale local copy. The check is fast (single read, single regenerate, single diff) and adds < 1s to quality:ci.
The heading-demotion logic in the build script skips fenced code blocks so that comments like # format inside pnpm example snippets in .claude/stack.md are not mistaken for headings. The current source files contain no H6 headings; the demotion is safe.
ADR-041 (Gherkin / BDD-style specs) was declined in the testing-philosophy work and left unrecorded as an ADR file. This ADR uses the next available number (045) following the declined 041 + the 042-044 sequence.
Date: 2026-05-17
Participants: Chris Pezza, Claude
Outcome: Accepted
Enforcement
Section titled “Enforcement”- Testable consequences:
- TC-1:
AGENTS.mdis exactly what the build script generates from the constitution layers. (Amended 2026-08-13:.windsurfrulesis a hand-maintained Windsurf overlay that defers toAGENTS.md— the build script never writes it.) - TC-2: hand-edits to the generated files are caught before merge.
- TC-1:
- Checks:
- TC-1 →
agents:checkinquality:ci(status: block, pre-existing gate) - TC-2 → also
agents:check: a hand-edit diverges the file from its sources and fails the nextquality:cirun. No edit-time (PreToolUse) hook exists — ADR-064 records the sketched hooks as not shipped.
- TC-1 →
- Not machine-checkable: whether new constitution content lands in the correct layer (see ADR-036).
- Graduation log: (empty at creation; entries added when a check changes status)