Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 53 additions & 13 deletions openspec/changes/specify-agent-tool-invocation/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,77 @@ 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.

### 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 allthe 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.
138 changes: 69 additions & 69 deletions openspec/changes/specify-documentation-homes/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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
Expand All @@ -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/`:

Expand Down Expand Up @@ -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
Expand All @@ -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.
18 changes: 13 additions & 5 deletions openspec/changes/specify-go-code-standards/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 3 additions & 1 deletion openspec/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down