ADR-077: Decision API surface — `/decide` and MCP
Context
ADR-006 specified Spectral’s customer-facing API around a twenty-module router taxonomy organized by domain resource: scans, changesets, changesets/clusters, changesets/experiments, changesets/validation, evaluations/rubrics, evaluations/objectives, traces, samples, dashboard, supervisor, inference, otel, and adjacent admin surfaces. The taxonomy reflected the sidecar measurement product: customers submitted scans, browsed clusters, accepted changesets, ran holdout validations.
Under the in-band decision-support shift, Spectral’s customer-facing job is a single thing: at decision time, the caller invokes Spectral with the action they are about to take, and Spectral returns a binding { status, work_frame }. Every primitive the prior router taxonomy organized around — scans, changesets, evaluations, traces, samples, optimization rubrics, OTEL ingestion — is retired per ADR-074 / ADR-075. The router taxonomy is therefore not a partial mismatch; it is structurally wrong for the new product.
What ADR-006 settled outside the router taxonomy — domain-scoping conventions, the response envelope shape (collections / single items / errors), the scope model, the role model, deprecation policy — is independent of the prior product framing and applies unchanged; API versioning is date-based and carried out-of-band per ADR-091. This ADR governs the router-taxonomy segment of the customer-facing surface.
A new customer-integration shape replaces it: one primary HTTP endpoint plus an MCP equivalent plus an OpenAPI-style discoverability surface. The decision contract is identical across HTTP and MCP; consumers choose. This ADR specifies the shape.
Decision
Note on URL paths. The decision endpoint path is
/decide— unversioned, with the API version carried out-of-band (no version token in the path, ADR-091). There is no/api/segment: it would be redundant with the dedicatedapi.runspectral.comhost (ADR-052). The remaining path segments (discoverability, MCP) are still illustrative working language; their literal naming is settled when those specifics land. The substantive decisions in D1–D7 are the request/response shape, the routing model, the MCP equivalence, the discoverability direction, and credential-carried tenancy.
D1 — Primary endpoint: POST /decide
Spectral’s customer-facing decision surface is a single HTTP endpoint (POST /decide). The endpoint accepts the action the caller is about to take, the context the caller has, and the world-model coordinates, and returns the binding decision contract.
The endpoint is not domain-scoped in the URL path. ADR-006 D2’s /api/domains/{domain_id}/... pattern applied to customer-data CRUD; /decide is a routing endpoint whose action + context scoping coordinates ride in the request body (D2). The caller’s (org_id, domain) tenancy is resolved from the credential, not the body (D7); the org/domain tenancy model in ADR-033 (app-layer primary, RLS backstop) continues to apply at the auth + caller-identity layer.
D2 — Request body shape
{ "action": "wire_transfer.release", "context": { /* values for supplied context attributes only */ }, "world_model_version": 7, "correlation_id": "wf_482"}Tenancy is credential-carried, not in the body.
org_idanddomainare resolved from the credential — a domain-bound API key carries both (see D7).org_idis account identity (stable across requests);domainis the key’s bound domain, reserved as an optional body field with key-as-ceiling precedence for the future org-level key.action,context, andworld_model_versionare the body’s semantic decision inputs — the contract MCP mirrors as tool arguments.
actionis required; with the credential’s(org, domain)it forms the routing key(org, domain, action)that resolves to a deployed decision module per ADR-076 D2.contextcarries values forsupplied-source attributes per the active world-model context schema. System-generated attributes (request_time,request_id,authenticated_caller) are captured at the platform edge and not accepted from the caller; computed attributes are derived at composition-root entry.world_model_versionis optional; production calls hit the active version by default. Callers can pin to a specific version for testing non-live rule sets against synthetic cases.correlation_idis optional passthrough; echoed in the response and never relied on or stored as a correlation key by Spectral — Spectral does not internally correlate decisions across calls.
D3 — Response shape: { status, work_frame, decision_metadata }
{ "status": "YELLOW", "work_frame": { "mode": "review", "next_action": "...", "allowed_actions": [...], "forbidden_actions": [...], "required_output": "...", "missing_evidence": [...], "next_human_owner": "..." }, "decision_metadata": { "world_model_version": 7, "module_content_hash": "sha256:...", "matched_rules": [...], "suppression_chain": [...], "aggregation_outcome": {...}, "predicate_traces": {...}, "errored_predicates": [...], "request_id": "req_9f1a82de", "request_time": "2026-05-17T14:23:41.082Z" }}statusis the audit-grade headline, one ofGREEN | GREEN-SKIP | YELLOW | RED. It is easy to scan in volume; the four states are exhaustive.work_frameis the binding contract returned alongsidestatus. It bounds the caller’s behavior with explicit allowed and forbidden actions, required output shape, evidence-gathering instructions, and human-owner routing. The contract names anti-patterns explicitly (llm_policy_decision: false,forbidden_actionsincludes"reinterpret_policy"and"override_spectral") so a downstream LLM cannot talk past Spectral’s decision.decision_metadatais the audit substrate: which world-model version was pinned, the module content hash, which rules matched, which suppressions ran, aggregation outcome, per-predicate traces, errored predicates, request ID, request time. Sufficient detail for the caller to reconstruct the decision path and for Spectral’s audit chain to record an immutable record.
Errors (validation failures, missing auth, system errors) follow ADR-006 D3’s RFC 9457 Problem Details shape unchanged. The four-state status taxonomy covers decision outcomes, not transport errors.
D4 — MCP as a first-class equivalent
Spectral exposes the decision contract through MCP (Model Context Protocol) as a first-class equivalent surface to HTTP. The MCP tool’s request schema mirrors D2; the MCP tool’s response schema mirrors D3. Caller frameworks that prefer MCP for tool / resource integration use the MCP surface; caller frameworks that prefer HTTP use D1. Both paths route to the same decision modules through the same platform pillar (ADR-076).
Authentication on the MCP surface uses the same signed-JWT model as the HTTP surface (ADR-076 D4, ADR-039). Delegation via RFC 8693 act claim applies identically. Identity is forgery-resistant across both surfaces.
D5 — Action discoverability — OpenAPI-style
Callers’ hosts need a programmatic way to know which actions exist for a given (org, domain), what each action’s request-body shape is (the world-model context schema’s supplied-source subset relevant to that action), and what the response shape is (the standard { status, work_frame, decision_metadata }).
Direction: discoverability is modeled after OpenAPI JSON specs. Each action’s request-body schema is the action’s context-schema subset; response schema is the standard contract. Caching contract, version negotiation, and the choice between a separate discoverability endpoint and per-action URL paths that are inherently OpenAPI-spec’d are deferred specifics — they need design work but the direction is settled.
D6 — Endpoints retained from the prior router taxonomy
Not every router in ADR-006 D6 retires. The following keep their routes on the new surface, governed by ADR-006 sections 4–5 (scope model, roles) and adjacent ADRs:
auth— login, register, OAuth, password reset, profile. Per ADR-039.orgs— org details, member management, invites.domains— domain CRUD, member management. Per ADR-033.keys— API key lifecycle. Per ADR-039.operations— operator-facing admin surfaces. Per ADR-047 (operations as separate deployable). Specifics of operator surfaces under the shift (decision-module review, world-model authoring administration, audit-record query) are downstream design work, not specified here.
These routes are unversioned in the path per ADR-091, and continue to use the response envelope from ADR-006 D3, the scope model from D4, and the roles from D5.
D7 — Credential-carried tenancy; no /api prefix
Two rules govern how the decision surface resolves tenancy and forms its paths.
Credential, not body, carries tenancy. The decision surface authenticates with a domain-bound API key. platform.api_keys carries (org_id, domain_id, scopes), so both tenancy coordinates resolve from the credential server-side:
org_idis account identity — stable across a caller’s requests, so it belongs on the credential, not in every request body.domainis the key’s bound domain. A customer has many domains; a domain-bound key targets one.domainis reserved as an optional body field with key-as-ceiling precedence: a domain-bound key with a present, mismatcheddomainis rejected (403) — a key can never widen its own scope; absent, the key’s domain is used. This keeps a future org-level key (one key, many domains,domainsupplied per call) an additive change per ADR-091 D4, not a breaking migration.- The JWT remains the customer-dashboard credential (the
/orgs/{org}/domains/{domain}/...read routes); only the programmatic decision surface (/decide, action discovery) is key-authenticated. The key lookup runs under a shortplatform_roleread before theapp_rolerequest_scope, mirroring the ADR-087 act-as admission (resolve tenancy under platform_role, then bind request_scope under app_role); RLS backstops as before.
No /api path prefix. The customer + auth surface carries no /api/ segment — it would be redundant with the dedicated, Cloudflare-proxied host api.runspectral.com (ADR-052). The surface is /decide, /actions, /auth/*, /orgs/*. Versioning stays date-based/out-of-band (ADR-091); routing stays body-based (D1/D2).
What retires from the prior router taxonomy
Customer-facing routers tied to retired primitives: scans, changesets (+ clusters, experiments, validation), evaluations/rubrics, evaluations/objectives, traces, samples, dashboard, supervisor, inference, otel, and customer-facing agents (the route shape that managed scanned customer agents). The assistant route is paused pending the Session 3c Spectral Agent reframe.
Alternatives considered
Path-based routing for the decision endpoint — e.g., POST /api/orgs/{org_id}/domains/{domain}/actions/{action}/decide. Rejected. The body-based shape mirrors the MCP surface (where there is no URL path) and keeps the HTTP and MCP surfaces structurally identical. Path-based routing would also force discoverability to navigate two parallel address spaces.
Multiple endpoints per decision class — e.g., POST /api/decide/payment, POST /api/decide/access-control. Rejected. The action identifier in the request body is itself the discriminator; adding URL-path discriminators forces customers to update endpoints when they add actions and duplicates routing logic across HTTP layer and request body.
Versioned per-action endpoints — POST /api/decide/wire_transfer.release@v7. Rejected. Version pinning is a request-level concern (the world_model_version field) not a URL-level concern. Per-action URL paths would either ignore the active-version pointer or force callers to know module versions a priori, defeating the active-version-pointer affordance.
Skip MCP for now; HTTP-only at launch. Rejected. MCP is a first-class equivalent (D4); MCP is increasingly the customer-integration surface AI agent frameworks prefer, and deferring it would force a second supersession ADR when added. The decision contract is identical to HTTP, so the MCP surface is a thin wrapper.
Keep ADR-006’s router taxonomy as-is and add /api/decide alongside it. Rejected. Every customer-facing router in the prior taxonomy organizes around a retired primitive (per ADR-074, ADR-075). Keeping them would imply they are still available, contradicting their retirement. The surviving routes (auth, orgs, domains, keys, operations) are listed explicitly in D6.
Full supersession of ADR-006. Rejected. URL versioning (since superseded by ADR-091), the response envelope conventions (D3), the scope model (D4), the roles (D5), the deprecation / Sunset header policy, and the legacy-endpoint-removal posture (D7) are all independent of the router taxonomy and have no dependency on the retired primitives. Full supersession would force restating these decisions without new content. Per discipline (whole-ADR supersession only when the whole decision is replaced), this is a partial supersession of ADR-006 D6 only.
Consequences
- The router structure for the customer-facing surface is governed here; ADR-006’s other sections (D1–D5, D7) remain authoritative.
- ADR-076’s D1 (decision-module hosting) and D2 (routing) are the upstream architectural anchors for this ADR’s D1 (the endpoint) and D2 (request shape).
- The discoverability endpoint’s specifics (separate vs integrated, caching contract, version-negotiation mechanics) are open design space. Tracked as a deferred Phase 3 item.
- The MCP surface’s auth specifics — handshake mechanics, tool-namespace conventions, resource shape if applicable — are open design space. Tracked as a deferred Phase 3 item.
- The routing coordinates are
(org_id, domain_id)directly, per ADR-086; the org/domain tenancy model in ADR-033 usesorg_id+domain_id. The customer surface and the internal RLS + access-control layer are aligned on the same identifiers. - The
dashboard,supervisor,inference,assistantrouter-area names appear in the platform codebase as application + infrastructure modules; their code removal is build-plan work per Phase 4 (per the retirement ADRs’ “code remains until Phase 4” framing), not part of this ADR’s scope. - Operator-facing admin surfaces under
operationscontinue to live inspectral.platformper ADR-076; the specific operator API shape under the shift (audit-record query, world-model administration on behalf of operators, decision-module review) is downstream design work. - The HTTP and MCP surfaces both surface the same decision contract; bilateral contract tests per ADR-066 (snapshot-based contract testing) are the verification mechanism for cross-surface parity.