Skip to content

Inter-Layer Contracts (registry)

The hub owns the inter-layer surface. Layers integrate contract-first — never by importing each other's private code. This page is the single registry of every contract between layers: who produces it, who consumes it, whether it's shipped, the ADR that governs it, and the test that enforces it.

Rule: a producer layer publishes a versioned contract; consumers bind only to that. A cross-cutting decision about a contract becomes an ADR. The contract is enforced by a provider/consumer test, and listed here.

Registry

Contract Producer Consumers Status Governing ADR Test
Data API (contracts/backend-core.openapi.json, agentarmy-platform.openapi.yaml) backend-core frontend-core (copy + generated src/lib/api/client.ts); middle-core (CopilotKit tools, UDA) shipped — Postman mocks live: backend-core (37e883b2…), platform (918365a6…) ADR-005 contract-provider.yml (BE) ↔ contract-consumer.yml (FE) — Pact-style
Model contracts (ProjectionContracts.g.cs, StateMachineContracts.g.cs, WorkflowContracts.g.cs) middle-core (factory) middle-core runtime shipped (generated) drift gate (check_drift.py)
Data-platform contract (data-platform-contract.g.jsonschema_version + *Data fields/types + state enums; DataPlatformContracts.g.cs, MCR-F4) middle-core backend-core UDA (RT6) producer shipped — middle-core #47 (versioned artifact + drift gate + 11 provider conformance tests). Consumer side dormant until backend-core supplies pact expectations (#40/#44) RT7 + ADR-005, ADR-009 provider conformance tests + check_drift.py --strict schema-version gate (MC); consumer-pact hook pending backend #40
JWT-forwarding auth contract (FE → MC → BE, verified once in BE) cross-cutting frontend-core, middle-core, backend-core ADR accepted — contract to wire ADR-002 to wire (auth integration test)
Agent endpoint (/copilotkit) middle-core (#22) frontend-core (#13 runtime route) ADRs accepted — code draft ADR-003, ADR-004 to wire
MCR-F4 projection-read API (mcr-f4-projection.openapi.yaml) middle-core (contracts/proposed/, #62) backend-core UDA ProjectionCapable (Phase 2) producer shipped — spec + mock (de0aa40f…); consumer pending backend-core #76 ADR-005, ADR-009, ADR-016 to wire (connector, #76)
Frontend BFF (browser↔UI, frontend-bff.openapi.json) frontend-core (contract/, #47) the browser (generated openapi-fetch client) shipped — vendored + spec + mock (f1ecc977…); 3 feature-flagged auth schemes (AUTH_MODE); client-gen + /api/cockpit/* route handlers pending frontend-core #48 ADR-002 (cookie-in → JWT-out) to wire (#48)
AG-UI agent stream (SSE, agui-stream.asyncapi.yaml) middle-core (CopilotKit/LangGraph) frontend-core (contract/, #47) contract drafted (AsyncAPI 3.0 — not Postman-mockable); producer vendor + event-name verify pending middle-core #66 ADR-007 doc/governance (SSE)
LLM gateway (OpenAI-compatible, contracts/llm-gateway.openapi.yaml) backend-core (gateway + guardrails) middle-core, frontend-core (browser → BFF → backend; no key in browser) proposed — spec + Postman mock published (173bda37…, non-stream shape); backend-core impl + guardrail middleware pending ADR-021, ADR-003 to wire (provider verification; SSE not mockable)
Agent gateway (A2A + MCP, backend-core/contracts/agent-gateway.openapi.yaml) backend-core (gateway, extracted from generated contract) middle-core (CopilotKit agent picker), frontend-core (agent listing UI) producer shipped — vendored as dedicated artifact so consumers can codegen/contract-test against the gateway as its own surface; consumer codegen + tests pending ADR-028 (spec's stale info.description cites "ARC-ADR-025" — backend-core follow-up to re-link in the YAML) to wire (consumer pact in MC + FE)
Event bus (NATS JetStream + CloudEvents v1.0) the fleet (middle-core hosts the broker per its ARC-ADR-001 / PR #73; bridges per ARC-ADR-022) any subscriber across spokes (consumer per subject) broker live locally (nats:2.10-alpine -js, localhost:4222); compose impl in middle-core #74 (copilot-task); HTTP↔NATS bridges in proposed templates/event-bridge-image/ ADR-022 to wire (push-consumer + GH-Actions publisher + DLQ)
Webhook receiver (event-bridge inbound, contracts/webhook-receiver.openapi.yaml) hub templates/event-bridge-image/ (per ARC-ADR-022) GitHub webhooks → HMAC verify → CloudEvents → NATS fleet.gh.* proposed — spec + Postman mock published (27f2561e…); image doctor 3/3 PASS on a running stack ADR-022 doctor (HMAC verify + JetStream replay)
Runbook orchestrator control API (contracts/runbook-orchestrator.openapi.yaml) hub templates/runbook-orchestrator-image/ (per ARC-ADR-031) operators / agents triggering runbooks; also consumes fleet.* CloudEvents (subject per runbook start trigger) and emits fleet.runbook.command / fleet.runbook.completed proposed — spec shipped; image doctor proves a bus event fires a runbook end-to-end ADR-031, ADR-022 doctor (validate both formats + event-fires-runbook + rejects-invalid)
Fleet interface vocabulary (contracts/fleet-interface/api-interface.ttl + FLEET-INTERFACE-MAP.md) backend-core (API-interface abstraction tool) vendored into backend-core, middle-core, frontend-core — the shared lexicon all layers align to shipped — discovered from the 6 fleet surfaces (335 concepts → 16 shared canonical: Agent/Tool/Invocation/Task/Message/…); re-run to maintain ADR-032, ADR-034 POST /api/v1/ontology/abstract + backend-core/app/ontology/schema_abstract.py
Temporal envelope (tools/temporal/temporal-envelope.schema.json) cross-cutting (hub) every layer — rides CloudEvents (extension attrs), PinnedElements (RT5), DBOS workflow steps proposed — reference HLC + envelope + self-test shipped (tools/temporal/hlc.py); per-layer wiring pending ADR-038 python tools/temporal/hlc.py (self-test); per-layer pact to wire

Upstream contracts (external producers we depend on)

External SaaS we call from inside the fleet earn the same vendoring rigor as internal contracts: we capture the slice we actually consume, mock it in Postman so contract-tests don't bill the vendor, and detect upstream drift via provider verification. Producer here is the vendor; consumer is our spoke.

Contract Producer Consumers Status Governing ADR Test
Tavily Search & Extract (backend-core/contracts/tavily-upstream.openapi.yaml) Tavily (external SaaS) backend-core llm-gateway (app/llm/providers.py _atavily_search / _atavily_extract) vendored — spec captures the slice consumed (POST /search, POST /extract); Postman mock + provider-verification test pending ADR-021 to wire (provider verification against vendored schema)

Backlog (anticipated contracts)

Contracts the fleet logically needs but hasn't shipped yet. Each row becomes a dispatched issue against the producing spoke (agent-army-task label) so the armies pick them up over time. Type = OpenAPI / AsyncAPI / GraphQL / JSON-Schema / TypeScript types / RDF/OWL / SKOS. Horizon = near (this PI), mid (next PI), later (research-grade, no commit).

Design system & frontend-core

# Contract Producer → Consumers Type Horizon Notes
FE-1 Design tokens (frontend-core/contract/design-tokens.json) frontend-core → every component + iOS mirror JSON-Schema (W3C DTCG) near Skeleton + JSON-Schema validation in this PR; populated by FE design-system team
FE-2 Component API contracts registry (frontend-core/contract/components/) frontend-core → Figma Code Connect + consumers TypeScript declarations near One <Component>.api.ts per shipped component. Operationalizes issue #71
FE-3 Theme contract (frontend-core/contract/theme.json) frontend-core → all UIs (web + iOS) JSON-Schema mid Light / dark / branded variants. Builds on FE-1
FE-4 Icon set contract (frontend-core/contract/icons.json) frontend-core → web + iOS + design system JSON mid SVG sprite + name registry
FE-5 i18n message keys (frontend-core/contract/i18n.json) frontend-core → consumers + translation pipeline JSON-Schema mid Key registry + locale fallback policy
FE-6 A11y contract (ADR-only, no API spec) cross-cutting (FE primary) ADR mid Keyboard nav, ARIA roles, contrast targets. Governance, not codegen
FE-7 iOS-shared models (frontend-core/contract/ios-shared.openapi.yaml) frontend-core → SwiftUI iOS app (EPIC #62) OpenAPI later Type defs consumed by web and SwiftUI together
FE-8 Figma Code Connect mappings registry (frontend-core/.figma/) frontend-core (#71) → Figma Dev Mode *.figma.tsx files near Operationalizes #71; this is the registry row for what the pilot creates

Middle-core

# Contract Producer → Consumers Type Horizon Notes
MC-1 Tool catalog schema (middle-core/contracts/tool-catalog.json) middle-core → cloud agents (MCP-style discovery) JSON-Schema mid Today lives in _meta.tools ad-hoc; formalize for cross-agent discovery
MC-2 Agent capability/health (middle-core/contracts/agent-capability.openapi.yaml) middle-core → frontend agent picker OpenAPI mid What an agent advertises (capabilities, readiness, model identity)
MC-3 NATS subject taxonomy (middle-core/contracts/nats-subjects.json) the fleet → any subscriber JSON near Naming convention + payload schemas for fleet.* subjects (per ADR-022)
MC-4 Model-spec contract (middle-core/contracts/model-spec.schema.json) middle-core (factory) → factory consumers JSON-Schema mid Schema of the source-of-truth model file the generator reads
MC-5 Generator-output schema (middle-core/contracts/generator-output.schema.json) middle-core (factory) → consumer codegen tools JSON-Schema mid Shape of every *.g.* artifact the factory emits

Backend-core

# Contract Producer → Consumers Type Horizon Notes
BE-1 LLM gateway SSE stream (extend contracts/llm-gateway.openapi.yaml) backend-core → middle-core + frontend BFF OpenAPI (text/event-stream) near Add SSE event shape; provider-verification only (SSE isn't Postman-mockable)
BE-2 RBAC policy contract (backend-core/contracts/rbac.json) backend-core → all consumers via JWT claims JSON mid Role → permission mapping; enforced once in BE per ADR-002
BE-3 Audit-event contract (backend-core/contracts/audit-events.asyncapi.yaml) backend-core → NATS subscribers AsyncAPI mid What BE emits to fleet.audit.* subjects
BE-4 Embeddings API (backend-core/contracts/embeddings.openapi.yaml) backend-core (or local-embedder fn-tier) → consumers OpenAPI mid Vector dim, model name, retrieval params. Pairs with local-embedder image (#184)
BE-5 Ingest contract clarification (extend backend-core.openapi.json) backend-core → middle-core agents OpenAPI near Resolves MC#44 (URI-ingest vs multipart). Decision artifact needed first
BE-6 Pagination contract (contracts/pagination.openapi.yaml) cross-cutting OpenAPI mid Cursor vs offset, page-size limits; referenced by every list endpoint
BE-7 Ontology snapshot read API (backend-core/contracts/ontology-snapshot.openapi.yaml) backend-core (Fuseki-backed) → agentarmy-forge OpenAPI near GET /ontology/snapshot?version=… returning deterministic RDF (Turtle / JSON-LD / N-Triples) with etag/version headers so forge can short-circuit re-gen. Reads the canonical graph the ingestion service (ADR-030) populates. Governed by ADR-029; upstream consumer of ADR-019 Fuseki store
BE-8 Ontology ingest API (backend-core/contracts/ontology-ingest.openapi.yaml) backend-core → operators + agents OpenAPI (multipart + JSON) near POST /ontology/ingest — accept RDF file → SHACL/ShEx shape validation → write to Fuseki + emit fleet.ontology.changed event (forge webhook fires off this). The file-drop entry point (phase i0) of the full ingestion service. Input seam decided 2026-05-28: BE-8 accepts BOTH multipart file upload AND JSON {uri} by-reference (resolves #44 / #61). Governed by ADR-029, ADR-019, ADR-030
BE-9 Data-source connector registry (backend-core/contracts/ontology-sources.schema.json) backend-core ingestion service → operators + agents JSON-Schema mid Declares a source (structured: DB/CSV/OpenAPI; unstructured: docs/prose) + its lifting config (mapping spec for structured; extractor settings for unstructured). The input registry for the data→ontology pipeline. Governed by ADR-030
BE-10 Provenance / evidence schema (backend-core/contracts/ontology-evidence.shacl.ttl) backend-core ingestion service → ontology consumers + audit RDF/OWL (PROV-O + SHACL) mid Per-assertion evidence: source, extraction method, model+prompt version, confidence, timestamp — attached via reification/hyperedges (ADR-016). "Evidence as a primitive": an assertion without evidence is inadmissible past the gate. Governed by ADR-030, ADR-016

Cross-cutting

# Contract Producer → Consumers Type Horizon Notes
XC-1 Health / readiness (contracts/health.openapi.yaml) every spoke → orchestrator + LB + probes OpenAPI near Standard /healthz /readyz /livez shape. Authored in this PR
XC-2 OTel trace-context (ADR formalization) every spoke → OTel collector sidecar ADR + W3C trace-context near Formalizes ADR-010/ADR-024; OTel collector function-tier image scaffolded in this PR
XC-3 Error envelope (contracts/problem-details.openapi.yaml) every spoke → every consumer OpenAPI (RFC 7807) near Authored in this PR; referenced by every other spec via $ref
XC-4 Rate-limiting (contracts/rate-limit.openapi.yaml) api-gateway-engineer → every spoke OpenAPI mid X-RateLimit-* headers + 429 problem-details shape
XC-5 CI workflow signatures (contracts/ci-workflows.schema.json) hub .github/workflows/_*.yml → spoke callers JSON-Schema for workflow_call mid What inputs/outputs reusable workflows expect
XC-6 Booster pack (per docs PR #190) hub → spoke installers JSON later Booster-pack manifest format
XC-7 MCP server contract pattern (generalize untool fleet) hub → any spoke MCP server OpenAPI + MCP discovery mid Reusable MCP-server scaffold derived from tools/mcp-local-fleet/
XC-8 Image-standard schema (templates/image-schema.json) hub → every image author JSON-Schema near Already exists; just register it as a contract
XC-9 Spoke-layer manifest (templates/spoke-layer-manifest.example.json) hub → every spoke JSON-Schema mid Spoke-level manifest format; schema not yet published
XC-10 Business-object catalog (templates/business-object-catalog.example.json) producing spoke → consumer spokes JSON later Shared business-object registry (existing example, no schema yet)
XC-11 Forge control API (contracts/forge-control.openapi.yaml) hub templates/forge-image/ → backend-core (webhook source), operators (on-demand) OpenAPI producer-shipped POST /webhook (HMAC X-Hub-Signature-256), POST /generate (on-demand {source, target, out, consumer_repo, branch, if_none_match}), GET /healthz. Endpoints shipped in PR #295; OpenAPI spec landed; spec + Postman mock live (66487fcc…). Governed by ADR-029

Upstream vendored

# Contract Producer → Consumers Type Horizon Notes
UP-1 OpenAI (backend-core/contracts/openai-upstream.openapi.yaml) OpenAI (SaaS) → backend-core llm-gateway OpenAPI (vendored slice) mid Capture only the endpoints used; mock in Postman; provider-verification
UP-2 Anthropic (backend-core/contracts/anthropic-upstream.openapi.yaml) Anthropic (SaaS) → backend-core llm-gateway OpenAPI (vendored slice) mid Same pattern as Tavily/UP-1
UP-3 Cerebras (backend-core/contracts/cerebras-upstream.openapi.yaml) Cerebras (SaaS) → backend-core llm-gateway OpenAPI (vendored slice) mid Per ADR-004
UP-4 GitHub REST/GraphQL (hub/contracts/github-upstream.openapi.yaml) GitHub (SaaS) → claude.yml + hub automations OpenAPI (vendored slice) later Capture only what claude.yml + heartbeat actually use
UP-5 ArcadeDB management API (hub/contracts/arcadedb-upstream.openapi.yaml) ArcadeDB → hub provisioning OpenAPI (vendored slice) later The mgmt surface for the platform-tier ArcadeDB image
UP-6 Cloudflare Zero Trust Access (hub/contracts/cloudflare-access-upstream.openapi.yaml) Cloudflare → hub MCP-fleet provisioning OpenAPI (vendored slice) later The slice used by tools/mcp-local-fleet/AUTOMATION.md for app + service-token provisioning

Who's waiting on whom

  • backend-core UDA (RT6) ← middle-core (MCR-F4): producer contract shipped (middle-core #47 — versioned data-platform-contract.g.json + drift gate + provider conformance tests). The wait is now on backend-core: bind the UDA to it (#40) and supply consumer pact expectations (PACT_FILE/PACT_BROKER_URL) so MC's dormant consumer-pact hook activates.
  • Ingest contract gap (MC agent ↔ backend-core /api/v1/ingest): middle-core #44 found the agent assumed JSON {uri} ingest but backend-core's endpoint is multipart file upload. Open cross-layer decision (URI-ingest endpoint vs agent file-upload flow) — see the governing ADR/issue.
  • CopilotKit middle-core runtime → backend-core OpenAPI: the contract is already shipped, so middle-core's tools aren't blocked on it — only on their own draft code (#18–#22).
  • Auth (ADR-002): settled — accepted 2026-05-25. Forward the user JWT unchanged across all three hops; RBAC enforced once in backend-core. Wire the auth integration test as the FE→MC→BE chain is built.

Drift management

frontend-core holds a copy of backend-core.openapi.json (currently in sync, byte-for-byte). The Pact-style contract-provider/contract-consumer tests catch behavioral drift; the file copy should be re-pulled from the backend-core source on each change (or generated in CI) so it can't silently age. middle-core is not yet in the contract-test loop — wire a consumer test (against backend's OpenAPI) and a provider verification (for MCR-F4) when that code lands.

How to add or change a contract

  1. Design with api-designer (the spec: OpenAPI / GraphQL / AsyncAPI / shared types).
  2. If it's a cross-cutting decision, write an ADR (/ea-adr).
  3. Enforce with contract-test-engineer — a provider verification in the producer repo + a consumer test in each consumer repo.
  4. Register it in the table above (producer, consumers, status, ADR, test).
  5. Evolve it safely with schema-migration-engineer (versioned, backward-compatible).

The hub owns this registry + the governing ADRs; the producing/consuming spokes own their side of each contract and its tests.

Vendoring external upstream contracts

When a spoke calls an external SaaS (Tavily, OpenAI, Anthropic, etc.) treat it as a contract too — same vendoring rigor as internal layers:

  1. Capture the slice you consume in <spoke>/contracts/<vendor>-upstream.openapi.yaml. Do NOT vendor the vendor's entire API — only the endpoints and fields you actually send/read today. A future feature that needs more extends the spec first, then the code.
  2. Mock it in Postman (AgentArmy workspace) so contract tests can run without billing the vendor or needing a live key.
  3. Register it under "Upstream contracts" above — producer is the vendor, consumer is your spoke.
  4. Add a provider-verification test — periodically validate that the live upstream still matches the vendored schema. When it drifts, the test fails before the field starts mattering in production.

Why bother: when a vendor silently changes a response field, the only place that breaks is the live integration — usually noticed by users, not engineers. A vendored contract turns that into a CI signal, and makes the dependency surface visible to anyone reading the registry.