diff --git a/openspec/changes/specify-agent-tool-invocation/design.md b/openspec/changes/specify-agent-tool-invocation/design.md index 33309b9..e37ef99 100644 --- a/openspec/changes/specify-agent-tool-invocation/design.md +++ b/openspec/changes/specify-agent-tool-invocation/design.md @@ -5,19 +5,37 @@ activation puts those versions on the path when a person enters the directory. An agent shelling out has no activation, so `just` resolves to whatever is installed globally. +The versions then differ between the agent, the person, and continuous +integration, on the same commit. + +## Goals / Non-Goals + +**Goals:** + +- An agent runs a repository's checks at the versions that repository declares. +- When versions do differ, the failure is recognisable as a version problem + rather than investigated as a defect in the code. + +**Non-Goals:** + +- Changing how any recipe is written. The problem belongs to one class of + consumer; the recipes are shared by all of them. +- Detecting the mismatch automatically. Nothing here fails when an agent uses + the wrong version — the guidance is what makes it avoidable. + ## Decisions ### Say it in AGENTS.md rather than solving it in tooling -The alternative is to make every recipe invoke tools through the version manager -itself, so the path does not matter. That spreads a workaround for one consumer -across every recipe in every shared module, and it would run `mise exec` on -machines where the shell already resolved correctly. - `AGENTS.md` exists for guidance that applies to agents and not to people. This is exactly that: a person cannot hit the problem, and an agent hits it constantly. +*Alternative considered:* make every recipe invoke tools through the version +manager, so the path does not matter. Rejected — it spreads a workaround for one +consumer across every recipe in every shared module, and runs the version +manager on machines where the shell already resolved correctly. + *Alternative considered:* rely on the agent noticing. It did not — three times in one session, each time diagnosing the repository rather than the invocation, and once writing the wrong diagnosis into a pull request description. @@ -25,17 +43,39 @@ and once writing the wrong diagnosis into a pull request description. ### State the symptom, not just the instruction The requirement names what the failure looks like: a check that fails for the -agent and passes in continuous integration, on a file nobody edited. An -instruction alone is easy to skip; a described symptom is recognisable when it -happens, which is when the instruction is needed. +agent and passes in continuous integration, on a file nobody edited. + +*Alternative considered:* state the instruction alone and keep the file short. +Rejected because an instruction is read before the work, while the problem is +recognised during it — and the failure is convincing enough to send someone +investigating the wrong thing. + +## Risks / Trade-offs + +- **An agent never reads `AGENTS.md`.** → `CLAUDE.md` points at it and Claude + Code loads that file by name, so the guidance is reached without the agent + choosing to look for it. + +- **Guidance cannot be enforced, so the wrong version can still be used.** → The + symptom is stated next to the instruction, so the mistake stays recognisable + after the fact even when the instruction was skipped. + +- **`osapi` has no `AGENTS.md`, so the guidance has nowhere to land there.** → + Its contributing-guide conversion creates one, and this change's task for that + repository waits on it rather than working around it. + +- **A repository adopts a different version manager and the instruction goes + stale.** → The requirement is about invoking tools at the versions a + repository declares, not about one tool, so the instruction can change without + the requirement changing. -### Applying it found two things +## What applying found -`osapi` has no `AGENTS.md` at all. It is the only repository without one, which -is already tracked as part of its contributing-guide conversion. The guidance -lands there when that file exists. +`osapi` has no `AGENTS.md` at all — the only repository without one, already +tracked as part of its contributing-guide conversion. The guidance lands there +when that file exists. `osapi-justfiles` publishes the `md` module and did not consume it. Its eleven markdown files — the root README and every module's — were formatted by nothing, -while every repository that fetches from it had them formatted. It now imports +while every repository fetching from it had them formatted. It now imports `md.just` like the rest. diff --git a/openspec/changes/specify-documentation-homes/design.md b/openspec/changes/specify-documentation-homes/design.md index 219973c..2ac7193 100644 --- a/openspec/changes/specify-documentation-homes/design.md +++ b/openspec/changes/specify-documentation-homes/design.md @@ -99,70 +99,7 @@ opening files. operations and `osapi-orchestrator` has no collectors; a shared structure would mean empty directories in both. -## Risks / Trade-offs - -- **"No new planning documents" is unenforceable.** Nothing stops one being - added. → It becomes a review comment with a rule behind it rather than a - matter of taste. - -- **76 documents are removed from the working tree.** Some may contain reasoning - that was never captured anywhere else. → They remain in git history; nothing - is destroyed. Anything worth keeping can be lifted into a change before the - deletion lands. - -- **An index goes stale.** A `docs/README.md` listing directories will drift as - they change. → It lists what each part covers rather than enumerating files. - -## Migration Plan - -1. Each repository gains a `docs/README.md` index. `osapi` is exempt — its site - navigation is the index. -2. `osapi` and `osapi-orchestrator` remove `docs/plans/`. The documents remain - in git history. - -No document is moved into the corpus. - -## Open Questions - -- Is anything in the 76 planning documents worth lifting into a change before - they are removed? They have not been read as part of this audit. -- `gohai`'s `adding-a-collector.md` and `ocsf-validation.md` are how-to guides - that `CONTRIBUTING.md` points at. Is that the right split, or should short - guides live in `CONTRIBUTING.md` directly? - -### osapi's CLAUDE.md is the largest mixed document - -Its 917 lines contain both kinds in alternating blocks. Seven sections are -labelled MANDATORY and read as requirements: every domain must appear in all the -same places as an existing one; provider mutations must be idempotent with a -stated truth table; every node-targeted operation must support broadcast and -return `hostname` and `error` on each result; mutable domains must separate -`POST` from `PUT`; every endpoint taking user input must carry validation tags, -a handler call, a 400 response, and RBAC wiring tests; SDK methods must not -stutter; all function signatures must be multi-line. - -Interleaved with them is procedure — an eight-step walkthrough for adding a -domain, with file layouts and code samples — and description of what the -packages currently contain. The procedure and the description stay; the rules do -not, because they decide whether a contribution is accepted and nothing outside -this file records them. - -Three of the rules bind repositories that cannot see the file. The SDK -guidelines govern `pkg/sdk`'s public surface, and `osapi-orchestrator` is a -consumer of it — the document even instructs consumers not to import `gen`, -which is an instruction to a different repository. - -The same 917 lines also restate branching, commit messages, linting, and test -conventions that `development.md` and `testing.md` already state, in three -places with no pointer between them. That has to be resolved before the root -`CONTRIBUTING.md` is written, or the conversion picks one of three and silently -drops the others. - -*Alternative considered:* convert `CLAUDE.md` to a pointer first and sort the -content afterwards. The conversion is what forces the decision about each block, -so deferring it means writing a `CONTRIBUTING.md` that has to be rewritten. - -## One rule was about to be deleted with its only record +### One rule was about to be deleted with its only record Reviewing the 76 planning documents before removal, as section 4 requires, found seventy that were implementation choreography for shipped work and six that @@ -188,7 +125,7 @@ rule still in force. document is not where a binding rule belongs, and keeping one file to preserve one sentence reintroduces the directory this change removes. -## An architecture document is not one genre +### An architecture document is not one genre `osapi` holds three documents named for architecture, and they are three different things: @@ -206,7 +143,7 @@ Principles". No other repository has a document like these. This is not an organization-wide category with a naming problem; it is one repository's documents sharing a word. -### The routing rule proves why extraction matters +#### The routing rule proves why extraction matters `job-architecture.md` states that an operation reaches `jobs.query` or `jobs.modify` according to its suffix, and lists ten suffixes. The code uses @@ -231,7 +168,7 @@ that still contain requirements, and these are site routes, so the rename costs external links. It belongs after, when what remains is unambiguously description. -## The corpus promised something it does not hold +### The corpus promised something it does not hold The `specs` README says of `openspec/specs/`: @@ -266,7 +203,7 @@ was the first draft of this task, and it was wrong. A README that describes what a directory currently contains, rather than what it is for, stops being true the moment the next capability lands. -### An entry point, before there are twenty capabilities +#### An entry point, before there are twenty capabilities `openspec/` holds six capability directories and eleven archived changes, and nothing that says where to start. That is survivable at six and is not at @@ -282,10 +219,73 @@ Rejected — the useful part is the sentence explaining what a capability is for and when to reach for it, which is not derivable from a list of `SHALL` statements. -### Purpose sections carry the orientation +#### Purpose sections carry the orientation The second requirement follows from the same problem. A capability opening with its first requirement teaches a reader who already knows the domain and no one else. Asking the `## Purpose` to state what the capability governs, and why that ground needs governing, is what makes the corpus readable rather than only searchable. + +## Risks / Trade-offs + +- **"No new planning documents" is unenforceable.** Nothing stops one being + added. → It becomes a review comment with a rule behind it rather than a + matter of taste. + +- **76 documents are removed from the working tree.** Some may contain reasoning + that was never captured anywhere else. → They remain in git history; nothing + is destroyed. Anything worth keeping can be lifted into a change before the + deletion lands. + +- **An index goes stale.** A `docs/README.md` listing directories will drift as + they change. → It lists what each part covers rather than enumerating files. + +## Migration Plan + +1. Each repository gains a `docs/README.md` index. `osapi` is exempt — its site + navigation is the index. +2. `osapi` and `osapi-orchestrator` remove `docs/plans/`. The documents remain + in git history. + +No document is moved into the corpus. + +## Open Questions + +- Is anything in the 76 planning documents worth lifting into a change before + they are removed? They have not been read as part of this audit. +- `gohai`'s `adding-a-collector.md` and `ocsf-validation.md` are how-to guides + that `CONTRIBUTING.md` points at. Is that the right split, or should short + guides live in `CONTRIBUTING.md` directly? + +### osapi's CLAUDE.md is the largest mixed document + +Its 917 lines contain both kinds in alternating blocks. Seven sections are +labelled MANDATORY and read as requirements: every domain must appear in all the +same places as an existing one; provider mutations must be idempotent with a +stated truth table; every node-targeted operation must support broadcast and +return `hostname` and `error` on each result; mutable domains must separate +`POST` from `PUT`; every endpoint taking user input must carry validation tags, +a handler call, a 400 response, and RBAC wiring tests; SDK methods must not +stutter; all function signatures must be multi-line. + +Interleaved with them is procedure — an eight-step walkthrough for adding a +domain, with file layouts and code samples — and description of what the +packages currently contain. The procedure and the description stay; the rules do +not, because they decide whether a contribution is accepted and nothing outside +this file records them. + +Three of the rules bind repositories that cannot see the file. The SDK +guidelines govern `pkg/sdk`'s public surface, and `osapi-orchestrator` is a +consumer of it — the document even instructs consumers not to import `gen`, +which is an instruction to a different repository. + +The same 917 lines also restate branching, commit messages, linting, and test +conventions that `development.md` and `testing.md` already state, in three +places with no pointer between them. That has to be resolved before the root +`CONTRIBUTING.md` is written, or the conversion picks one of three and silently +drops the others. + +*Alternative considered:* convert `CLAUDE.md` to a pointer first and sort the +content afterwards. The conversion is what forces the decision about each block, +so deferring it means writing a `CONTRIBUTING.md` that has to be rewritten. diff --git a/openspec/changes/specify-go-code-standards/design.md b/openspec/changes/specify-go-code-standards/design.md index d19d0c6..e862805 100644 --- a/openspec/changes/specify-go-code-standards/design.md +++ b/openspec/changes/specify-go-code-standards/design.md @@ -95,11 +95,19 @@ enforced by authority. ## Risks / Trade-offs -- **A repository may need an exception.** The requirements state conditions - rather than absolutes where a legitimate exception exists — mocking is the - worked example. -- **These rules are testable only by reading code.** Nothing here is enforced by - a linter. That is a reason to write them down, not a reason not to. +- **A repository legitimately needs an exception and becomes non-conformant.** → + Requirements state conditions rather than absolutes wherever a legitimate + exception exists; mocking is the worked example, since one repository mocks + nothing and declares no mocking library. + +- **Nothing enforces these rules, so a repository can drift without failing a + check.** → They are stated once where every repository can be held to them, + which is what makes drift reviewable. A rule no linter can check is a reason + to write it down, not a reason to leave it unwritten. + +- **A rule universal in practice today stops being universal.** → Each was + measured against every repository before being written, so a future exception + arrives as a change to the requirement rather than as silent non-conformance. ## Migration Plan diff --git a/openspec/config.yaml b/openspec/config.yaml index b40506a..7f907d7 100644 --- a/openspec/config.yaml +++ b/openspec/config.yaml @@ -48,7 +48,9 @@ rules: lost. A decision with no alternative is an assertion. - Prefer recording the real failure that motivated a decision over an abstract argument for it. - - Risks use the form [Risk] -> Mitigation. + - "Each risk states the risk in bold, then an arrow, then what mitigates + it. **The risk.** → The mitigation. Avoid square brackets; the markdown + formatter escapes them as link references." tasks: - Each task is verifiable — state what would prove it done. - Order by dependency. A task that breaks consumers is immediately