→ shipped an interface change whose contract document still described the old one
- cost when it happens
- high
- how often
- common
A response type gains a field. The implementation is correct, the tests pass, and the
published specification still describes last month's shape — so every consumer generated
from it is wrong in a way that will surface in somebody else's codebase.
The specification is a second, hand-maintained representation of something the code already
states precisely. Two representations of one fact require an act of synchronisation, and
that act is performed by memory. It is skipped the first time somebody is in a hurry, and
after that the document is untrustworthy, so people stop reading it, so it stops being
updated at all.
A worker asked to change the implementation changes the implementation. Asking it to also
update the specification is asking for the synchronisation to be performed by something else
that can forget; the fix is to have one representation.
Broken consumers, whose breakage is discovered by the consumer rather than by the producer.
And the slow loss of the document itself: once a specification has been wrong twice, it is
treated as documentation rather than as a contract.
An operation is declared once, as a typed descriptor beside its implementation, with its
input and output types. Everything external is derived from that declaration: the MCP tool,
the HTTP route, the OpenAPI operation with its schemas and examples, the browsable
reference, the benchmark target and the generated documentation. Adding an operation by
editing a transport registry or a specification file is not possible, because those are
outputs. generate --check names every generated file that differs from what the
declaration produces, and CI refuses the tree.
before handler.rs (new field) + openapi.yaml (hand-written, unchanged)
after $ majordomus generate --check
stale: docs/generated/openapi.json (differs)
[reproduce: majordomus generate]
It does not review an interface design, and it does not decide whether a change is
breaking. It removes the possibility of the description and the behaviour being maintained
separately.
What this looks like
Concrete situations, one per audience. Each is declared in the moment's front matter, so the before and the after are data rather than prose a page could drift from.
-
The specification written twice
platform-team
- before
- A field is added to a response type and the specification is not updated, so every generated client is wrong.
- after
- The specification is derived from the same typed declaration the implementation uses, and CI refuses a tree in which it is stale.
-
Consumers generated from a stale document
open-source-maintainer
- before
- Downstream clients are generated from a published contract that drifted two releases ago.
- after
- The published document is a build output of the declaration; there is no second place for it to drift from.
-
A boundary that was reviewed once
enterprise
- before
- The reviewed interface document and the served interface are two artefacts with no enforced relationship.
- after
- One declaration, many projections; the reviewed artefact is regenerated and diffed rather than trusted.
How you would know
The observable symptoms this moment declares. They are the questionnaire on the index and the input of majordomus why diagnose; nothing else defines them.
-
◻
An interface specification is maintained by hand alongside the code that implements it.
hand-written-spec
-
◻
A published contract describes a version of the interface that no longer exists.
spec-behind
-
◻
Adding an operation means editing both the implementation and a separate registry or document.
two-places-to-edit
Where this lives in the tool
Everything below is read out of this moment's own front matter and resolved against the repository. A name here that did not exist would fail validation.
the commands that answer it
the capabilities of the executable that answer it
the claims that back this page, and the evidence behind each
-
guaranteed
MCP, HTTP, OpenAPI, Swagger UI, the capabilities commands and the generated reference are derived from the registry, and a change to one definition reaches every one of them
-
guaranteed
The OpenAPI document is inferred, the tags are the modules, the examples are the benchmark cases, the responses are the router's statuses by kind, the prose is the one text every projection shares, and the site's API reference is rendered from the committed document
-
guaranteed
The committed projections, the OpenAPI document, the capability reference and the allow-lists, are regenerated from the registry and the schemas, and generate --check refuses a stale one
-
guaranteed
Every capability of the Rust executable is defined once, and the registry refuses a duplicate id or a colliding projection name, naming both parties
-
guaranteed
How each declarative kind is read and which keys it may carry is data read at run time, and a repository adds a kind with its JSON Schema without a code change
the rules that govern it
- project.interfaces-are-projections
- project.rust-canonical-declaration
- project.derived-files-regenerated
- majordomus.projection-integrity
the use cases that show the way out
If this one is familiar, so is the next
What this moment names, what names it, and what shares its area, audience or tags. The second and third are derived; only the first is written down.
All 38, and how they connect to the tool →