Problem
Three surfaces are about to describe the same ingest contract, and today none of them shares a source:
docs/CUSTOM_RUNTIME_INGEST.md — hand-written, covers the run/event API only.
- The setup prompts (paired issue) — read by agents, where a wrong header is a silent failure.
- The public endpoint reference on the landing site (paired issue) — where drift-bot now enforces AC-WEB-001.3 truthfulness: it greps repo docs and fails a landing claim the repo denies.
Meanwhile the OTLP receiver's real capabilities are undocumented anywhere. dashboard.py::_otlp_decode has accepted protobuf, OTLP/JSON and gzip for some time. Nothing tells a user that, so functionally it does not exist.
Scope
A generated docs/INGEST.md, on the model of scripts/gen_query_contract_doc.py (which already generates docs/QUERY_CONTRACT.md from the contract, with a drift test). One module declares the ingest contract; the doc, the setup prompts, the landing page and the server's own validation all read it.
Contents, all derived:
- Both surfaces:
/v1/{logs,metrics,traces} (OTLP) and /api/v1/runs* (run/event).
- Accepted content types and encodings —
application/x-protobuf, application/json, Content-Encoding: gzip.
- Every header, spelled in the case the server actually matches.
- Size caps and batch caps, as numbers read from the constants that enforce them.
- Every response code and what it means, including what happens to a partially-bad batch (state the real behaviour, whatever it is — an all-or-nothing promise we don't keep is worse than an honest "valid events are kept").
- Which
gen_ai.* attributes are read, and — explicitly — which are not yet.
A drift test in the shape of tests/test_query_contract_drift.py: regenerate, diff, fail if the committed doc is stale.
Why a whole issue for a doc
Because it is the anti-drift mechanism for the other three, not the doc itself. We have shipped a wrong number to users from exactly this shape of gap before — ARCHITECTURE.md and CLAUDE.md drifted into describing a different product, and the fix was to generate docs/MODULE_MAP.md rather than to correct the prose. Same move.
Acceptance
Part of the ingest-gap plan (phase 1, item 5 of 5).
Problem
Three surfaces are about to describe the same ingest contract, and today none of them shares a source:
docs/CUSTOM_RUNTIME_INGEST.md— hand-written, covers the run/event API only.Meanwhile the OTLP receiver's real capabilities are undocumented anywhere.
dashboard.py::_otlp_decodehas accepted protobuf, OTLP/JSON and gzip for some time. Nothing tells a user that, so functionally it does not exist.Scope
A generated
docs/INGEST.md, on the model ofscripts/gen_query_contract_doc.py(which already generatesdocs/QUERY_CONTRACT.mdfrom the contract, with a drift test). One module declares the ingest contract; the doc, the setup prompts, the landing page and the server's own validation all read it.Contents, all derived:
/v1/{logs,metrics,traces}(OTLP) and/api/v1/runs*(run/event).application/x-protobuf,application/json,Content-Encoding: gzip.gen_ai.*attributes are read, and — explicitly — which are not yet.A drift test in the shape of
tests/test_query_contract_drift.py: regenerate, diff, fail if the committed doc is stale.Why a whole issue for a doc
Because it is the anti-drift mechanism for the other three, not the doc itself. We have shipped a wrong number to users from exactly this shape of gap before —
ARCHITECTURE.mdandCLAUDE.mddrifted into describing a different product, and the fix was to generatedocs/MODULE_MAP.mdrather than to correct the prose. Same move.Acceptance
docs/INGEST.mdis generated, and editing it by hand fails CI.Part of the ingest-gap plan (phase 1, item 5 of 5).