The Update-Demand rule
Audience: contributors changing a registered surface.
Aperture has one house rule about documentation: any change to a registered
surface must ship the matching skills/*.md document in the same PR. A surface
change that lands without its doc update is a non-skippable CI failure. This page
is descriptive — it explains the rule and the gates that enforce it. The rule
itself lives in skills/update-demand.md and is self-protecting; this page does
not modify skills/*.md or the gates.
Why the rule exists
A surface's behaviour and its documentation are two halves of one contract. If
they can drift, the docs rot silently and callers get surprised. Update-Demand
removes the option to drift: the same PR that changes a surface changes its doc,
and CI refuses the PR otherwise. The skills/ docs are the human- and
agent-readable description of each surface; keeping them lockstep with the code is
the whole point.
What the rule requires
| If you change… | You must also update… | Enforced by |
|---|---|---|
| An Aperture error code | errors/codes.go — the AllCodes slice and a Registry entry with a Message + Fixups | TestCodesHaveFixups |
A skills/*.md doc | its YAML frontmatter (name matching the file stem + description) | TestEverySkillHasFrontmatter |
| The Update-Demand rule | skills/update-demand.md (it must stay present with frontmatter) | TestUpdateDemandDocPresent |
A table that exists in two places — the rule AST vs. the served rules-serializer.js, or the SQL driver-value type switch vs. mappedDriverTypes | the other half, plus the skills/ doc that restates it | the registry-parity tests, e.g. TestEditorOperatorTablesAgree and TestDriverValueMappingTableMatchesTheTypeSwitch |
CLAUDE.md carries the full table, row by row. As real surfaces land, each adds
a skills/<feature>.md doc and a coverage gate in skills/skills_test.go that
walks that surface's registry, plus a row there.
The enforcing CI gates
These gates are non-skippable and stay green regardless of any single change — they are the mechanical enforcement behind the rule:
TestCodesHaveFixups— everyAPERTURE_*code has aRegistryentry with aMessageand at least oneFixup(orFixupNotApplicable: true). Adding an error code without its remediation metadata fails here. See Adding an error code.TestRegistryHasNoOrphans— theRegistrycontains nothing that is absent fromAllCodes; the two lists cannot diverge.TestCodesAreScreamingSnakeNamespaced— every code isSCREAMING_SNAKEandAPERTURE_-prefixed.TestUpdateDemandDocPresent— the Update-Demand seed doc (skills/update-demand.md) exists with frontmatter. The rule's own documentation cannot be deleted without failing CI — this is what makes the rule self-protecting.TestEverySkillHasFrontmatter— everyskills/*.mdhas aname(matching its file stem) and adescriptionin its YAML frontmatter.- The registry-parity gates — they read the mirror of a Go table (a served JS
file, or the Go source itself) and fail rather than skip when it is
missing.
TestDriverValueMappingTableMatchesTheTypeSwitchis the newest: it parsessqlprovider/values.gowithgo/astand fails if the driver-value type switch andmappedDriverTypesdisagree, so the mapping a developer reads and the mapping the code performs cannot drift.
How this differs from the doc generators
Do not confuse Update-Demand with the mdBook doc generators. The error-code table
(docs/src/reference/error-codes.md) and the CLI reference
(docs/src/reference/cli.md) are regenerated on demand with make docs-gen and
have no CI drift gate — you regenerate and commit them yourself. Update-Demand
is different: it is a hard CI gate on the skills/*.md surface docs and the error
Registry. The generators keep the reference book fresh; Update-Demand keeps
the surface skill docs honest.
The full conventions catalog, including the authoritative Update-Demand table,
lives in
CLAUDE.md.