diff --git a/.github/workflows/agent-guidance.yml b/.github/workflows/agent-guidance.yml index 5a5b056331..1728d75acb 100644 --- a/.github/workflows/agent-guidance.yml +++ b/.github/workflows/agent-guidance.yml @@ -5,6 +5,7 @@ on: paths: - 'AGENTS.md' - 'CONTEXT.md' + - 'docs/adr/FORMAT.md' - 'docs/agents/**' - 'scripts/__tests__/agent-guidance-contract.test.ts' - 'scripts/check-affected/**' @@ -18,6 +19,7 @@ on: paths: - 'AGENTS.md' - 'CONTEXT.md' + - 'docs/adr/FORMAT.md' - 'docs/agents/**' - 'scripts/__tests__/agent-guidance-contract.test.ts' - 'scripts/check-affected/**' diff --git a/AGENTS.md b/AGENTS.md index b830dd8b1e..c8749ee632 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,12 +5,13 @@ The daemon owns sessions. Registry declarations route commands to platform runti ## Task routing -Read only the row for your current task. Follow other links only when they apply. +Read only your task's row. Follow other links only when they apply. | When the task involves | Read | | --- | --- | | Domain vocabulary | `CONTEXT.md` | -| Architecture decisions | `docs/adr/README.md` | +| Read architecture decisions | `docs/adr/README.md` | +| Write or amend an ADR | `docs/adr/FORMAT.md` | | Source changes, package boundaries, routing, diagnostics | `docs/agents/code-ownership.md` | | Checks or a failed gate | `docs/agents/testing.md` | | New or changed tests | `docs/agents/test-authoring.md` | @@ -51,7 +52,7 @@ The versioned CLI help owns command semantics. To drive an app, start with the s - The layering scan reads only tracked files. Stage new modules before you trust its result. - When you rename a file, move its Fallow baseline entry. Do not regenerate unrelated baselines. -The validation lock automatically reclaims owners that are provably dead. An unknown owner continues to block. +The validation lock automatically reclaims provably dead owners. An unknown owner continues to block. If the wait times out, report the printed owner and the lock state. Do not remove the lock manually. ## Agent docs diff --git a/docs/adr/FORMAT.md b/docs/adr/FORMAT.md new file mode 100644 index 0000000000..5d23376f01 --- /dev/null +++ b/docs/adr/FORMAT.md @@ -0,0 +1,68 @@ +# ADR format — read this before you write or amend an ADR + +An ADR records one accepted decision. It states: what was decided, why, what was rejected, and what follows. + +## When to write one + +Write an ADR only when all three conditions are true: + +1. The decision is hard to reverse. Changing it later costs real work. +2. The decision is surprising without context. A future reader would ask "why this way?" +3. The decision is the result of a real trade-off. Genuine alternatives existed. + +If one condition is missing, do not write an ADR. + +Keep a proposal in an umbrella issue. Write the ADR when the decision is accepted. An ADR records accepted decisions only. + +Write a new ADR only when the problem or the scope changes. Amend the existing ADR otherwise. + +## What an ADR contains + +Every ADR states four things: + +1. The decision: the API, data flow, invariants, constraints, or boundaries. State only what explains the decision. +2. The reason for the decision. +3. The rejected alternatives, each with its reason. +4. The consequences and the open gaps. + +Keep exact technical names (commands, flags, wire fields, typed reasons) where they explain the decision. + +Sections are optional. A short paragraph that holds the four things is a complete ADR. Do not add empty sections. Do not put an audit report in an ADR. + +## What an ADR excludes + +Do not record these in an ADR. Send each to its owner: + +- Implementation inventories and file maps: the registries, types, and gates that own them. +- Measurements: `docs/evidence/`. +- Rollout and migration plans: the package docs, the help, or the umbrella issue. +- PR logs and status change-logs: the umbrella issue. + +Do not repeat general repo guidance that `AGENTS.md` already gives. + +## The record stays current + +An ADR states the current truth. It is not a log of the discussion around the decision. An optional dated "Decision history" line records a real change of direction. If the direction really changed, you may add one such line with its reason and a link. + +## Before you amend + +1. Check the current code against the decision the record intends to keep. +2. Check the code comments and issues that cite the record. +3. If the code changed on purpose: update the record to the new truth. Record the reason. When the direction changed, you may add the optional "Decision history" line. +4. If the code drifted by mistake: record the drift in an issue. Keep the constraint in the record. +5. Preserve constraints that the code cannot show. +6. If you change a section name or a decision number, update the code citations that point at it. + +An amendment that changes the modality, scope, or exceptions of an accepted decision must say so explicitly. Keep proposed wording as proposed. + +## How to restructure an existing record + +- Keep a migration ledger. Map each old section to one outcome: kept, moved to a named owner, or deleted with a reason. +- Before a deletion, move content that is the only home of a still-true contract to its owner. +- Keep one PR to one to three related ADRs. + +## Size and naming + +Keep ADRs short. Aim below about 8 KB. + +Name files `NNNN-slug.md` in `docs/adr/`. Scan the directory and use the next number. Add a row to the index in `docs/adr/README.md`. diff --git a/scripts/__tests__/agent-guidance-contract.test.ts b/scripts/__tests__/agent-guidance-contract.test.ts index 6e0df3cc32..0eeb9dbbb8 100644 --- a/scripts/__tests__/agent-guidance-contract.test.ts +++ b/scripts/__tests__/agent-guidance-contract.test.ts @@ -8,6 +8,7 @@ const ROOT = path.resolve(import.meta.dirname, '../..'); const BYTE_BUDGETS = { 'AGENTS.md': 4_000, 'CONTEXT.md': 11_000, + 'docs/adr/FORMAT.md': 4_000, } as const; const FOCUSED_DOC_BUDGET = 5_000; const AGENT_DOCS_TOTAL_BUDGET = 40_000; diff --git a/scripts/check-affected/model.test.ts b/scripts/check-affected/model.test.ts index aa642d18b4..04d16c3f6f 100644 --- a/scripts/check-affected/model.test.ts +++ b/scripts/check-affected/model.test.ts @@ -228,7 +228,7 @@ test('user docs pages and README select the docs-links and docs-prose checks', ( }); test('agent guidance owns its focused contract instead of disappearing as docs-only', () => { - for (const file of ['AGENTS.md', 'CONTEXT.md', 'docs/agents/testing.md']) { + for (const file of ['AGENTS.md', 'CONTEXT.md', 'docs/agents/testing.md', 'docs/adr/FORMAT.md']) { const result = plan([file]); assert.deepEqual(result.checks, ['agent-guidance']); assert.deepEqual(result.docsOnlyPaths, []); diff --git a/scripts/check-affected/model.ts b/scripts/check-affected/model.ts index e1ec8d0d57..9fa45e8ac7 100644 --- a/scripts/check-affected/model.ts +++ b/scripts/check-affected/model.ts @@ -539,7 +539,7 @@ const buildOwnership: OwnershipRule = ({ file }, input) => { // carry executable contracts and must reach their focused workflow even when the // main CI workflow ignores documentation. const COMMAND_DOCS = 'website/docs/docs/commands.md'; -const AGENT_GUIDANCE = new Set(['AGENTS.md', 'CONTEXT.md']); +const AGENT_GUIDANCE = new Set(['AGENTS.md', 'CONTEXT.md', 'docs/adr/FORMAT.md']); function isAgentGuidance(file: string): boolean { return AGENT_GUIDANCE.has(file) || file.startsWith('docs/agents/');