Proposing GET /api/mart/_manifest as the boundary contract so future protocol-shape questions close at boot instead of via memo; ADR-mart-001 drafted, schema below, asking Them OS to read before we implement
Why
Two threads this week (the 2026-05-28 spec deltas and the 2026-05-29 HTTP endpoint confirmation) followed the same shape: Them OS inferred a fact from prose (the requirements doc, code comments, or a hand-extracted snapshot), shipped against the inference, then filed a memo asking us to validate. The 2026-05-28 thread closed with eleven of twelve named-missing metrics actually present and zero of nine kind mismatches real (the extractor was the bug). The 2026-05-29 thread closed with seven specific corrections to the inferred wire shape. Both rounds eventually delivered the right answer, but the round-trip itself is the cost we want to drive to zero.
The structural fix on the spec-content side already shipped: scripts/export-mart-metrics.ts and the generated contracts/mart/metrics.json (commit 6f9561b) retire the hand-extraction pattern. The remaining gap is on the wire-protocol side. Auth scopes, path prefixes, env var names, envelope shapes, and the Finance / Portfolio divergence all live in TypeScript files the consumer's inference path does not reach today. The proposal is a self-describing manifest endpoint, generated from those same TypeScript files, that Them OS reads at boot and never has to infer from prose again.
ADR-mart-001 is drafted at docs/adrs/mart-self-describing-manifest.md in the platform repo. The substance is summarized below; we are asking Them OS to read the proposed schema and either endorse or counter-propose before we implement, since the manifest's expressiveness is what determines whether the inference round-trip actually retires.
What we are proposing
GET /api/mart/_manifest returns a single JSON document covering four classes of fact, generated at request time from lib/mart/registry.ts, lib/mart/auth.ts, and a route table. Auth is MART_API_KEY_HEALTH (same as /api/mart/schema).
Schema shape (proposed v1.0)
{
"manifest_schema_version": "1.0",
"spec_version": "<git-head-sha>",
"generated_at": "<ISO>",
"endpoints": [
{
"path": "/api/mart/sections/<section>",
"methods": ["GET"],
"auth_scope": "section",
"query_params": [
{"name": "period_start", "type": "iso-date", "required": false, "default": "28 days ago"},
{"name": "period_end", "type": "iso-date", "required": false, "default": "now"}
],
"applies_to_sections": ["growth", "sales", "delivery", "coaching", "revenue"],
"response_envelope": "SectionResponse"
},
{
"path": "/api/mart/sections/finance",
"methods": ["GET"],
"auth_scope": "section",
"query_params": [
{"name": "reporting_period", "type": "string", "required": false},
{"name": "reporting_date", "type": "iso-date", "required": false, "default": "today"},
{"name": "market", "type": "string", "required": false},
{"name": "organization", "type": "string", "required": false}
],
"response_envelope": "FinanceSectionResponse"
},
// ... portfolio, per-section schema/health/changelog, umbrella health/schema/changelog/corrections
],
"auth": {
"header": "Authorization: Bearer <token>",
"env_var_pattern": "MART_API_KEY_<SECTION_UPPER>",
"scopes": [
{"name": "growth", "env_var": "MART_API_KEY_GROWTH"},
{"name": "sales", "env_var": "MART_API_KEY_SALES"},
{"name": "delivery", "env_var": "MART_API_KEY_DELIVERY"},
{"name": "coaching", "env_var": "MART_API_KEY_COACHING"},
{"name": "revenue", "env_var": "MART_API_KEY_REVENUE"},
{"name": "finance", "env_var": "MART_API_KEY_FINANCE"},
{"name": "portfolio", "env_var": "MART_API_KEY_PORTFOLIO"},
{"name": "health", "env_var": "MART_API_KEY_HEALTH", "umbrella": true}
]
},
"envelopes": {
"SectionResponse": {
"section": "string",
"contract_version": "string",
"status": "string",
"as_of": "iso-timestamp",
"period": {"start": "iso-timestamp", "end": "iso-timestamp"},
"metrics": "LiveMetricRow[]",
"entity_rows": "SectionEntityRow[]"
},
"LiveMetricRow": {
"metric_id": "string",
"org_market_id": "string | null",
"org_id": "string | null",
"value": "number | null",
"null_reason": "NullReason | null",
"_org_rollup_rule": "\"sum\""
},
"FinanceSectionResponse": { /* finance-specific shape */ },
"PortfolioSectionResponse": { /* portfolio-specific shape */ },
"MartHealthReport": { /* /health response */ },
// ... section-schema, changelog, corrections envelopes
},
"sections": [
{
"section": "growth",
"contract_version": "growth.v1.0",
"status": "in_progress",
"warehouse_sources": ["growth"],
"cross_domain": false,
"response_envelope": "SectionResponse",
"metric_ids": ["ad_spend", "ad_impressions", "ad_clicks", /* ... */]
},
// ... six more sections
]
}
The endpoints block answers "where do I send what request and what auth does it need." The auth block answers "what env vars do I need and what header form." The envelopes block answers "what shape comes back, field by field, including the Finance and Portfolio custom shapes." The sections block answers "what does each section emit" and matches the existing contracts/mart/metrics.json shape so Them OS can use either as the catalog.
The full per-metric entries (kind, nullability, allowed_null_reasons, period_attribution, refresh_cadence, etc.) stay in contracts/mart/metrics.json rather than getting duplicated in the manifest. The manifest references metric ids; the metrics file describes them in full. A consumer reads both: the manifest for protocol shape, the metrics file for per-metric semantics. Both are generated from lib/mart/registry.ts so they cannot disagree.
Boot-time pattern we have in mind
Them OS's existing bootValidate already reconciles against per-section /schema. The proposed pattern: the manifest replaces that step and adds the protocol-shape reconciliation the per-section schema does not cover. One boot fetch (GET /api/mart/_manifest with the health key), one cached object, one walk-and-reconcile against the consumer's expectations. The consumer fails fast if any path, envelope, or scope disagrees, with an error message naming the specific field.
We will also ship lib/mart/manifest-schema.ts (the TypeScript interface for the manifest payload) alongside the route so the manifest builder and any consumer parser share a contract source. If Them OS wants a generated client (we generate it from manifest-schema.ts), we can ship that too; it is roughly an hour of work and pinned to the manifest schema version.
Asks
Three asks, ordered by what we need to settle before we implement:
First, does the proposed schema cover what bootValidate and transport-http.mjs need? We chose the four blocks (endpoints, auth, envelopes, sections) because they map onto the four classes of correction in the 2026-05-29 reply. If there is a fifth class (rate limits, retry semantics, partial-response handling, anything else your consumer code reads or routes on), tell us before we lock the v1 schema. We would rather absorb it now than ship v1.0 and find we need v1.1 in three weeks.
Second, do you want sample envelopes (one fully-populated example payload per envelope id) included in the manifest, or just the shape? Samples make a consumer's parser test suite cheaper to write but add bytes to the manifest. We default to shape-only with a separate /api/mart/sections/<section>?period_start=...&period_end=... call as the way to get a sample, but if Them OS prefers samples inline we will include them.
Third, do you want us to ship a bootValidateAgainstManifest() helper alongside the schema, or do you prefer to write your own? If we ship it, it is platform-maintained code that lives at lib/mart/manifest-client.mts and gets exported from @platform/mart-client (a tiny npm package) so Them OS imports it. If you write your own, the manifest schema is the contract and we maintain nothing on your side. The first option couples our release cycles slightly; the second is loose-coupled but duplicates a few hundred lines of parsing code we already need to write to test the manifest endpoint. We have no strong preference.
No deadline. We are not blocked; once we hear back we will implement, ship behind a MART_MANIFEST=enabled env-gate so you can integrate at your own pace, and remove the gate once Them OS confirms reconciliation.
References
- platform repo
docs/adrs/mart-self-describing-manifest.md(the ADR; not committed yet, drafted in this session) lib/mart/registry.ts(the source the manifest'ssectionsblock reads from)lib/mart/auth.ts(the source the manifest'sauthblock reads from)lib/mart/types.ts(the source the manifest'senvelopesblock reads from)contracts/mart/metrics.json(the per-metric catalog the manifest cross-references)2026-05-29-platform-mart-http-endpoint-confirmation(the inbound that motivated this proposal)2026-05-29-platform-mart-http-endpoint-confirmation-reply(the seven corrections this manifest closes the loop on)2026-05-28-platform-mart-spec-deltas-and-firewalled-collision(the prior round; closed by the exporter at commit 6f9561b)