Skip to content
GitHub
Decisions

ADR-076: Platform pillar as decision-module host

Context

ADR-001 established the three-context topology — spectral.core (substrate), spectral.worlds (world modeling), spectral.platform (customer-facing product surface). The topology decision is anchored on bounded-context vocabulary divergence between worlds and platform; that divergence is preserved by the in-band decision-support shift and the three-context split continues to hold.

What changes is what spectral.platform is. ADR-001 described the platform pillar as “customer-facing platform bounded context — workspaces, evaluation frameworks, change sets, scans, optimizations, verdicts, and their application + infrastructure layers.” Every customer-visible artifact in that list either retires (per ADR-074, ADR-075) or pivots away from the customer surface. Without a fresh statement of what the pillar does, the platform context’s responsibilities under the shift are read by inference from individual retirement ADRs — fragile, ambiguous, and a missing positive grounding for the downstream Codex rewrites + Linear scope changes.

This ADR governs the platform pillar as the decision-module host. The surrounding topology (three contexts, inter-context isolation, Clean Architecture inside each context, no direct imports between worlds and platform, contract-surface mediation per ADR-065) is set by ADR-001 and applies unchanged.

Decision

The platform pillar’s role under the in-band decision-support shift is decision-module hosting and the surrounding infrastructure that makes deployed decision modules a viable product. Five sub-responsibilities scope it:

D1 — Decision-module hosting

spectral.platform hosts the multi-tenant decision-module-hosting fleet that serves decisions. Modules deploy per (org, domain, action); each module is the compiled, deterministic, deployable artifact produced by the world agent under the authoring-time path (per ADR-081). The fleet is the replicas of the existing api deployable (per ADR-109 D1) — module hosting is one of api’s responsibilities under the shift, alongside the surviving non-decide route groups from ADR-006 sections 1–5; no new deployable is added. Pods load modules from the module store on first request for an (org, domain, action), cache in-process, and serve subsequent requests from cache. Horizontal scaling is one knob (HPA on aggregate load), not per-module. The “fleet of pods behind one Service” framing refers to the architectural surface; the deployment-level Service is api.

D2 — Routing

The platform routes incoming POST /api/decide (and the MCP equivalent — see ADR-077) calls to the correct deployed module. The routing key is (org, domain, action) at the world model’s active version; callers can optionally pin to a specific version via the world_model_version request field. Routing decisions are deterministic and do not invoke the LLM.

D3 — Audit

Every decision produces an audit-grade record: status, work_frame, decision_metadata (which rules matched, suppression chain, aggregation outcome, predicate runtime traces, errored predicates, derivation outcomes), caller identity (authenticated_caller), world-model version pinned at decision time, request ID. Audit records are retained and queryable per the data-retention policies governed by ADR-042; the audit chain integrity model (event substrate per ADR-044, contract surfaces per ADR-065) carries forward unchanged.

D4 — Auth

Caller identity is resolved from signed auth tokens (JWT per ADR-039) at request entry and surfaced as the system-generated authenticated_caller context attribute. Delegation chains (a service acting on behalf of an end customer or operator) ride in the token’s act claim per RFC 8693. Caller-supplied identity fields are not accepted — identity is forgery-resistant by construction (per ADR-087). Domain-level access control, scope model, and roles carry forward per ADR-006 (sections 4–5, which the API-surface supersession ADR-077 leaves authoritative).

D5 — Billing

Per-decision usage metering, tier accounting, and reservation of customer-tier observability surfaces. Specifics of billing model (per-decision unit price, per-org subscription tiers, free-tier limits) are commercial decisions tracked separately and not specified here; this ADR commits the platform pillar to owning the billing surface, not to its commercial shape.

What carries forward unchanged

Inter-context isolation between spectral.worlds and spectral.platform per ADR-001 (and the contract-surface admission rules per ADR-065). Clean Architecture inside spectral.platform. The library structure under src/spectral/platform/ per ADR-031. The platform pillar’s database schema (platform.*) per ADR-032. The platform pillar’s deployment slot in the deployment topology per ADR-046 and ADR-109 D1 (the decision-module-hosting fleet runs as replicas of the single api container, which hosts D1).

What no longer belongs in the platform pillar’s role

Customer-facing scan execution (retired per ADR-074), customer-facing eval-set generation and delivery (retired per ADR-075), changeset lifecycle as customer-consumable workflow (retired per ADR-074’s downstream Linear scope), tournament + verdict customer surfaces (retired per ADR-074), customer-directed parameterization (retired per ADR-075), OTEL ingestion as a delivery mechanism (retired per ADR-074). Each of these had a customer-facing surface in the prior platform pillar; under the shift, that surface is POST /api/decide (and MCP), and these prior surfaces are gone.

The world agent’s internal eval framework (covered in ADR-075’s principle-migration tracking) lives in spectral.worlds, not spectral.platform. Generated decision modules originate from the world agent and are uploaded to the module store by the worlds context; the platform context consumes from the store at request time. The producer-consumer direction across the context boundary preserves inter-context isolation per ADR-001 + ADR-065.

Alternatives considered

Leave ADR-001’s platform-pillar content unchanged; rely on individual retirement ADRs to communicate the shift. Rejected. ADR-001 is a top-level architectural anchor that downstream ADRs and Codex pages cite as the source of truth for what each context is. Without a fresh positive statement, future readers who land on ADR-001 see the prior product framing as authoritative, contradicting every retirement ADR they have to discover by hunting through the supersession graph. The amendment makes the platform pillar’s current responsibilities legible at the anchor point.

Fully supersede ADR-001 with a new three-context-architecture ADR. Rejected. The topology decision in ADR-001 (three bounded contexts, inter-context isolation, Clean Architecture inside each) is unchanged by the shift. Full supersession would require restating the topology rationale and the worlds-vs-platform vocabulary argument, neither of which has new content. Per discipline (whole-ADR supersession only when the whole decision is replaced), this is a partial supersession of the platform-pillar segment only.

Fold the platform-pillar amendment into ADR-077 (the new decision-API-surface ADR). Rejected. The platform pillar’s responsibilities are an architectural anchor (what does the context do); the API surface is one of several mechanisms expressing that anchor (alongside the decision-server fleet topology, the audit chain, the billing surface). Combining them would conflate “what is the platform context” with “what is the customer-facing surface” and force a future reader to extract platform-pillar responsibilities from an API-shape ADR.

Define a sixth sub-responsibility (e.g., observability) as a peer to D1–D5. Rejected. Observability is substrate (ADR-036) consumed by every responsibility above; it is not a top-level platform-pillar role distinct from D1–D5. Calling it out as a peer would over-decompose and dilute the focus of this amendment.

Consequences

  • ADR-001’s platform-pillar content segment is replaced with a one-line pointer to this ADR. The topology decision in ADR-001 stands; the platform-pillar content moves here.
  • Downstream Codex rewrites can ground their platform-pillar references on this ADR rather than reading “platform” through every retired customer-facing primitive.
  • ADR-077 (decision API surface) is the next anchor decision; it builds on D1 (hosting) and D2 (routing) here and inherits the auth + audit + billing surfaces.
  • The decision-module-hosting fleet runs as replicas of the single api app container per ADR-109 D1; the single-container collapse eliminated the earlier service-slot question. Horizontal scaling is one knob (HPA on aggregate load), not a per-service topology choice.
  • Module integrity / signing model — the discipline governing how modules are signed, verified at load, and rotated — is not specified here. It is settled separately by ADR-080, which blocks the decision-module-hosting build-out (not this anchor).
  • Execution-sandbox specifics (restricted exec context, AST-level static analysis at code-gen, OS-level sandbox on decision-server pods) are noted as the safety posture but not specified at the ADR level here; the actual sandbox decisions are settled by ADR-083.
  • Noisy-neighbor handling at runtime (per-tenant rate limits, CPU-time budgets, optional dedicated-pod escape hatch) is an operational concern for D1 once the fleet hosts modules from many orgs; settled separately by ADR-084.
  • The retirements in ADR-074 and ADR-075 each note “code remains until Phase 4 build-plan removal” for the implementations of the retired primitives. The platform pillar’s content under this ADR is the target state; the codebase migrates toward it over Phase 4 build-plan work, not in a single change.