Make the API usable by an agent, and stop the contract going stale
Someone writing a script against ShopDB had two options: read a 705 KB spec, or guess. The spec is the reference, not an index, and handing an agent the whole thing means either truncating it or spending a context window on 479 operations nobody asked about. Three additions, each feeding the next. /api/docs/operations.txt is the index: every operation on one line with its method, path, tag, auth and summary, at around 70 KB for all 486. A caller can hold the entire API at once, find the four operations it needs, and only then fetch their schemas. /api/docs/openapi.json?tag=... narrows the spec to one of the 41 tag groups. Components are kept intact deliberately: pruning the schema graph to match would leave references resolving to nothing, and a generator that fails confusingly is worse than a file that is larger than strictly necessary. tools/mcp/ is an MCP server built on both. Five tools rather than 483, because a tool per operation is unusable - clients degrade past a few dozen and models start picking the wrong one. It reads everything from the live instance at call time, so enabling a plugin adds operations it can already reach with no change here. The token is the security boundary and the README says so plainly: every operation the credential permits is reachable, so exploratory work wants a read-scoped token. The rest of this is about the contract not drifting, which is what makes any of the above trustworthy. Two CI jobs: - api-contract runs the parity gates and then regenerates the spec and fails if anything changed. Those gates already existed and already worked - they caught both routes added here, unprompted - but nothing ran them except by hand, and the committed spec had silently drifted by three operations, including two setup endpoints added the day before. - migrations-mysql is the job the header comment has been claiming for some time while not existing. It described "the REAL multi-site deploy path" and "what catches an Alembic regression on MySQL before it ships", and there was no such job. That is worse than no comment: it is the false confidence that let a migration ship which could not build a schema on a fresh MySQL database. The suite runs on SQLite, and SQLite accepts a foreign key onto a table that does not exist. CONTRIBUTING.md now says to run gen_openapi.py --refresh after changing a route, and that a route the sweep cannot classify belongs in PUBLIC_ENDPOINTS with a reason rather than hand-edited into the inventory, which the next refresh would overwrite. The new index route is declared there alongside the spec it indexes.
This commit is contained in:
@@ -169,6 +169,32 @@ Tests are required for:
|
||||
- New plugin hooks (contract test)
|
||||
- Schema migrations (before/after data integrity)
|
||||
|
||||
## Adding or changing an API route
|
||||
|
||||
The published contract is generated from the code, so it goes stale the moment
|
||||
a route changes and nobody regenerates it. A stale spec is worse than no spec:
|
||||
it is what a client generator, an agent, or the MCP server in `tools/mcp/` will
|
||||
believe.
|
||||
|
||||
After adding or changing a route:
|
||||
|
||||
python scripts/gen_openapi.py --refresh
|
||||
|
||||
That rewrites `docs/api-inventory.json` and `docs/openapi.json` from the live
|
||||
app. It fills in what is derived and leaves prose you have written alone, so run
|
||||
it freely and then replace any seeded summary with the reason the route exists.
|
||||
|
||||
If it prints `review` against your route, it could not find a gate on the view.
|
||||
Do not hand-edit the inventory - the next refresh overwrites it. Either gate the
|
||||
view, or, if the route is genuinely public, name it in `PUBLIC_ENDPOINTS` in
|
||||
`tests/test_core/test_public_read_surface.py` with the reason. The generator
|
||||
reads that list, so the published contract and the enforced gate cannot
|
||||
disagree.
|
||||
|
||||
The `api-contract` CI job runs the parity gates and then regenerates and fails
|
||||
if anything changed, so a stale spec fails the pull request rather than being
|
||||
discovered months later.
|
||||
|
||||
## Branches: what belongs on main, and what does not
|
||||
|
||||
`main` is the curated product other sites adopt, and what the publication
|
||||
|
||||
Reference in New Issue
Block a user