docs/api-contract/ — the seam, written down
docs/api-contract/ — the seam, written down
The backend and the renderer talk only over HTTP on 127.0.0.1. That boundary is the one place
where work on either side can break the other, so it is the one place worth freezing. This folder
is what “frozen” means in practice.
What is here
| File | What it is | How it changes |
|---|---|---|
openapi.json |
app.openapi() from resmon_scripts/resmon.py, keys sorted and info.version removed. Every route’s method, path, parameters and request body. |
Generated. Never edited by hand. |
http.md |
The index: every route against the parity-register row it serves, with its auth class, whether it streams, and a one-line purpose; then the closed vocabularies and the admission 429. | Generated. Never edited by hand. |
mcp.md |
The MCP tool contract (v2, at 2.3). What an external harness may call and what it gets back. | Hand-written, frozen. |
assistant-conversations.md · assistant-choices.md · library.md · evidence.md · selected-evidence.md |
The five feature contracts, each written before both harnesses started on a slice that needed them to agree. | Hand-written, frozen. |
openapi.json and http.md are generated by implementation_scripts/api_contract.py and guarded by
verification_scripts/test_api_contract.py, which regenerates both in-process and fails on any
difference. The other six are prose, written once and left alone.
The security model the auth classes come from is ../local-api-security.md:
what a request must carry, where the token lives, and — just as plainly — what none of it defends.
How to change it
A change to the surface takes its own pull request. That is the whole mechanism:
- Change the route. The guard goes red, naming the routes that were added, removed or reshaped.
-
Regenerate, from
resmon_scripts/:python -m implementation_scripts.api_contract --write - Commit the regenerated files in that pull request, and say in the body which routes changed
and why. A reviewer reading
git diff docs/api-contract/should be able to see the whole of what moved without reading the implementation.
A new route also needs a line in api_contract.ROUTE_JOURNEYS saying which register row it serves.
Until it has one the guard stays red: a route nobody can name a journey for is a route the renderer
has no reason to call.
Running the generator to make a red test go green, without looking at what changed, defeats the point of having it. The failure message is the review.
How the renderer rebuild uses it
A screen may call what is in here and nothing else. The 3.0 renderer is measured against this
file: for each of the 44 parity-register rows, http.md lists the routes that carry it, so a screen
that has been rebuilt can be checked against the routes its journey actually needs rather than
against somebody’s memory of the old page. A screen that needs something not in here is asking for
a backend change, which is a pull request of its own and arrives before the screen does.
Three things the index is careful about, because a contract that overclaims is worse than none:
- No response schemas. Not one route declares a
response_model, soopenapi.jsonsays every 200 body is the empty schema{}. Requests are fully described; responses are described in prose in the rootREADME.mdand in the five feature contracts. Nothing here guesses. - Purposes are quoted, not written. A route’s one-line purpose is the first line of its
docstring. The routes that have no docstring show FastAPI’s generated summary in italics — the
function name with the underscores taken out — rather than a sentence somebody invented for them.
http.mdgives the count both ways. - Streaming is marked. Three routes answer
text/event-stream. A client that fetches one of them and waits for a JSON body waits for ever.