Skip to content
Merged
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
46 changes: 3 additions & 43 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,7 @@ it: a GitHub Actions composite (`action.yml`) and a GitLab CI Catalog component
## Architecture

A human at a shell, the GitHub Action, and the GitLab CI component all invoke the same
`semvertag tag`, and `semvertag/` is short enough to read. What reading it will not tell you is why
four of its shapes are load-bearing rather than incidental: the two forge adapters are independent
copies sharing only the RFC 8288 pagination loop
([ADR-0001](docs/adr/0001-forge-providers-not-unified.md)) and their status-to-error ladders stay
duplicated for the same reason ([ADR-0003](docs/adr/0003-error-translators-not-tabled.md)); there is
one verb and no `doctor` preflight ([ADR-0006](docs/adr/0006-no-doctor-preflight-command.md)); and
the composite action deliberately does not check the repository out
([ADR-0007](docs/adr/0007-composite-action-does-not-check-out.md)).
`semvertag tag`, and `semvertag/` is short enough to read.

## Cutting a release (maintainers)

Expand All @@ -39,41 +32,8 @@ Release and move `v0` by hand, or cut a new patch tag.

## Workflow

**The spec for a change is its PR body**, not a committed file: why, design, non-goals, verification,
reviewed with the diff. There is no change file and no lane to choose. A trivial PR (typo, dep bump,
formatter, CI tweak) ships a conventional-commit title with no body ceremony.

Two things outlive the PR, and there are exactly two places to put them: an alternative **rejected**
with reasoning becomes an ADR in [`docs/adr/`](docs/adr/) (`NNNN-slug.md`, sequential, with a revisit
trigger), and real work **not scheduled** becomes a GitHub issue. There is no third state, and no
separate truth-home directory — a behaviour change is reviewed with the diff, not promoted to a page.

### Where a fact goes

Four homes, one owner each:

| Home | Holds |
|---|---|
| `semvertag/` | anything readable from the source — the default |
| a named test | an **invariant**: must stay true, and a change could silently break it |
| `docs/adr/` | a rejected alternative, with the reasoning that would otherwise be re-litigated |
| `README.md` and `docs/` | anything a user needs |

Before writing a line anywhere:

> Can an agent get this by reading `semvertag/`? → **don't write it.**
> Would a wrong change here fail a test? → it belongs **in the test**, not in prose.
> Does a user need it? → **`README.md` / `docs/`**.
> Otherwise it does not get written.

**Prose about mechanism has no home. There is no file to add a paragraph to.** This file included:
it is always loaded, so a line that restates a docstring, a justfile comment, or `pyproject.toml`
costs every turn and rots in two places at once. The workflow files here carry dense explanatory
comments; re-telling them in this file is the failure mode to watch for.
Real work **not scheduled** becomes a GitHub issue.

An invariant is a test whose name is the claim, with a docstring opening `INVARIANT:` and a second
paragraph naming **what breaks it** — design rationale, not a report of what this one test catches.
Nothing enforces that docstring shape; it is read at review time. A relative link to an ADR *is*
checked — CI runs lychee `--offline` over every `.md` — but a path named in a docstring or a comment
is not. Both ADRs and `INVARIANT:` docstrings ratchet: nothing prunes a record once its call is
settled. Keeping them lean is a standing habit.
Nothing enforces that docstring shape; it is read at review time.
Loading