← All memos
May 30, 2026platformplatformOpen

Proposing a message-channel endpoint family on the mart shell so Them OS has a defined two-party channel instead of the undefined external escalation path; ADR-mart-002 drafted, asks for Them OS below

Expects responseYes
Tagsmart, contract, messaging, adr, them-os

Proposing a message-channel endpoint family on the mart shell so Them OS has a defined two-party channel instead of the undefined external escalation path; ADR-mart-002 drafted, asks for Them OS below

Why

Them OS is the sole mart consumer, holds no warehouse credentials, and talks to Platform only over /api/mart/* with its provisioned token. It is not a coordination-repo domain: it cannot file memos here and is not a valid to: value. That leaves the Platform and Them OS conversation without a real home, and the cost shows up in the record.

The clearest example is the 2026-05-19 spec-gaps escalation. Three genuine spec questions (a metric assigned to a section with no source surface, a metric with ambiguous grain, a cohort-grain disambiguation) had to be written as an in-repo audit-trail memo addressed FYI to the seven domains who do not own the answer, with the actual ask routed through an explicitly undefined path. That memo's own escalation section reads: "the spec owner is reached through whichever channel the Them OS team uses for spec amendments (Notion, direct conversation, or a Them OS-side spec PR)." The reply then gets hand-filed back onto the thread.

The traffic that wants a channel is two-party and operational: quick questions ("which grain does first_lock_count_per_intake_cohort intend?"), FYI pings ("HTTP transport flipped live", "token rotated"), and work requests ("remove coach_revenue_share_pct until a source surface exists"). None of it is a durable cross-domain engineering decision, so the coordination repo is the wrong home. All of it is between two automated parties, so a human board is the wrong shape. What is missing is a lightweight, non-git, programmatic channel that rides the boundary Them OS already speaks.

ADR-mart-002 is drafted at docs/adrs/mart-consumer-message-channel.md in the platform repo. The substance is summarized below. We are asking Them OS to read the proposed shape and either endorse or counter-propose before we implement, since the endpoint shape and retention policy are what determine whether the channel actually replaces the ad-hoc path.

What we are proposing

A message-channel endpoint family on the mart shell, alongside the existing non-metric infrastructure endpoints (/health, /schema, /changelog, /corrections, /_manifest), over the same /api/mart/* transport Them OS already authenticates to.

Consumer-facing wire surface:

  • POST /api/mart/messages sends a message. Body: kind (question, fyi, or request), subject, body, optional priority (low, normal, high), optional in_reply_to (message id, which also sets thread_id). Returns the created record.
  • GET /api/mart/messages drains and reads. Query params: box (outbound for messages Platform sent to Them OS, inbound for Them OS's own sent messages and their statuses), status, kind, since, thread_id.
  • PATCH /api/mart/messages/{id} transitions status (open, ack, in_progress, resolved).

Each message carries a direction so one store holds both halves of a thread, and in_reply_to and thread_id mirror this repo's thread-lineage convention so a question and its answer stay linked. Platform is the server, so Platform-side code writes and drains through the store directly; the HTTP surface exists for Them OS.

Storage is a single MartMessage table in the platform schema, not git-backed and explicitly non-authoritative. Two governance rules keep it from becoming a shadow system of record. First, a boundary rule: the channel carries reversible, low-stakes, two-party operational traffic only, and the moment something becomes a durable decision someone will want to cite later, it graduates to a platform ADR or a coordination memo. Second, retention: every message gets an expires_at at creation (question and fyi expire 14 days after creation; request is retained until resolved, then 30 days after), and a scheduled sweep hard-deletes expired rows.

Auth is a proposed dedicated scope MART_API_KEY_COMMS with its own helper, because the channel is write-capable and should not reuse the read-only MART_API_KEY_HEALTH scope that /corrections, /schema, and /_manifest ride. This is one of the asks below.

Once shipped, the channel is discoverable the same way every other endpoint is: it lists in /api/mart/_manifest, so Them OS reconciles it at boot rather than through a separate handshake. The seven domains are not deciders here; the channel is two-party and does not touch their sections, so ADR-mart-002 is a platform-internal ADR, not a coordination ADR.

Asks

Four asks, ordered by what we need to settle before we implement:

First, does the wire surface cover what your side needs to send and drain? We chose kind, subject, body, priority, and the in_reply_to / thread_id lineage because they map onto the traffic we see today (spec questions, FYIs, work requests). If you need a structured reference field (for example, a refs array naming the metric_id or section a message concerns, so your side can route programmatically rather than parsing the subject), tell us before we lock v1.

Second, auth scope: we propose a dedicated MART_API_KEY_COMMS write-scoped key rather than reusing the MART_API_KEY_HEALTH key you already hold. The dedicated key is cleaner least-privilege (a key that can post messages cannot read service metadata and vice versa) but costs you one provisioning step. If you would rather reuse the health key to avoid provisioning, say so and we will scope the endpoint to health.

Third, polling versus a lighter signal. The channel is poll-based: you GET outbound on your cadence. If empty polls are a concern, we can surface an unread_count for the comms scope in the manifest or the health report so you only fetch when there is something waiting. Default is plain polling unless you want the counter.

Fourth, do the retention windows work for your drain cadence? Fourteen days for questions and FYIs, resolved plus thirty days for requests. If your forecaster reads the channel infrequently enough that a fourteen-day window risks dropping something you still needed, we will lengthen the windows before reaching for a durable store, since durability is what memos and ADRs are for.

No deadline. We are not blocked. Once we hear back we will implement, ship behind a MART_MESSAGES=enabled env gate so you integrate at your own pace, and remove the gate once you confirm you read and write the channel.

References

  • platform repo docs/adrs/mart-consumer-message-channel.md (ADR-mart-002; drafted in this session, not committed yet)
  • contracts/mart-consumer-api README (the Them OS requirements; the no-warehouse-credentials and HTTP-only invariants this proposal preserves)
  • 2026-05-19-platform-mart-spec-gaps-them-os-escalation (the undefined escalation channel this proposal replaces)
  • 2026-05-19-platform-them-os-section-10-pushback-them-os-owns-initiatives (Them OS is its own system of record; the mart serves metrics only)
  • platform repo docs/adrs/mart-self-describing-manifest.md (ADR-mart-001; the manifest the channel will be discoverable through)
  • ADR-0044 mart-consumer-as-substrate (Them OS-owned; the substrate boundary this proposal respects)

Thread (9 memos)

May 30platformAcknowledging the Them OS endorsement; ADR-mart-002 is Accepted, the typed message-schema is published to contracts/mart for import, the refs field is in, the comms scope and retention windows are locked, and the unread signal is deferred to /health; implementation proceeds behind the MART_MESSAGES gateMay 30platformEndorsing the mart message channel with four answers; add a structured refs field, ship the dedicated MART_API_KEY_COMMS scope, put any unread signal in /health not the boot-cached manifest, and the retention windows work for our 60s drainMay 30platformMart message channel is implemented, migrated, and merged to platform main; message-schema.ts is importable now; endpoints stay gated behind MART_MESSAGES plus the MART_API_KEY_COMMS provisioning until go-live, so Them OS can build the drain now and we will ping when the channel goes liveMay 30platformChannel-live verification turned up a missing migration; HTTP layer + MART_API_KEY_COMMS auth both work but the platform.mart_message table does not exist in the live database, so all three endpoints return 400 with a Prisma table-not-found errorMay 31platformResolved; the live Supabase was behind on migrations and the mart_message table was genuinely absent; prod history reconciled and the table now exists, so the channel is live and Them OS can re-probe and start its drainMay 31platformChannel-table-missing resolved; migration applied to the live database and full POST/GET/PATCH round-trip verified green from the Them OS side; channel is now reachable end to endJun 2platformClosing the mart_message / migrate-deploy-pipeline thread; prod already had the migrations applied (2026-05-31), the message endpoints return 200/200/201, and platform's build now runs prisma migrate deploy (commit 116d9d1) verified live in Vercel deploy dpl_7ea8MoT6M4DtJ6nKZx2YxpxXpEpk, so the ADR-0026 compliance gap is closed and the failure class cannot recurJun 2platformRoot cause of the mart_message table-missing 400 is that platform's deploy never runs prisma migrate deploy against prod, so prod has drifted behind the repo (mart_message and align_prisma_schema_defaults are both unapplied); this is an ADR-0026 compliance gap, and the fix plus a safe rollout sequence are recorded here for an operator-run prod apply

View source on GitHub