Skip to content

System architecture

Purpose

This document describes how Sea Suite is put together: the components, the boundaries between them, and the reasons those boundaries sit where they do. It is the map the rest of the build docs assume. The one-sentence architecture is: the Harness is the product; the model is a swappable dependency; every guarantee we make to a customer is enforced in a deterministic component, never in a prompt. If a design choice elsewhere in the docs contradicts that sentence, this document wins.

Scope / Non-scope

In scope

  • Component inventory: Harness, Org Model store, manager agents (Chonks), function services, memory, policy engine, integrations, content engine, surfaces, audit.
  • End-to-end data flow for the three canonical flows: a morning brief, a blocker chase across two teams, an over-threshold approval.
  • Multi-tenancy and isolation model (cells, row-level isolation, keys, portfolio reads).
  • Model-provider abstraction (the model gateway).
  • Scaling units, failure domains, and degradation modes.
  • The "why" behind each boundary.

Non-scope

1. Architectural stance

Five decisions shape everything below. They are restated as ADRs in Glossary and ADRs.

# Decision Consequence
A1 Guarantees live in the Harness, not the prompt. Autonomy classes, outreach caps, human-only blocks, escalation rights, and quiet hours are enforced by deterministic services that sit between the agent and the world. An agent can be wrong, jailbroken, or replaced by a worse model and the product still cannot fire someone, spam an IC, or suppress an escalation.
A2 Event-sourced Harness. Every trigger, step, tool call, model call, and outcome is an immutable event. Runs are replayable from the event log plus a pinned Org Model version. Debuggability, audit, and offline evaluation come for free; state is a projection.
A3 One door to humans. Every message to a person — Slack, Teams, SMS, voice, email, console notification — goes through outreach-service. There is no other write path to a human. Caps, tone dial, symmetry ("if it wrote about you, you can read it"), and the uncancellable escalation right are enforced in one place.
A4 Math is deterministic. Planning, capacity, scheduling, calibration, ledger arithmetic, and escalation routing are function services with typed inputs and explainable outputs. The model narrates and decides between options; it does not compute them. Cost per managed person stays bounded; outputs are testable; receipts contain rule evaluations rather than vibes.
A5 Tenant is the unit of everything. Isolation, scheduling fairness, budgets, keys, retention, graduation, and failure containment are all keyed by tenant first. A noisy or compromised tenant cannot affect another; portfolio views are built by reading across tenants through a grant layer, never by joining them.

2. Component map

flowchart TB
  subgraph Surfaces["Surfaces (thread-first)"]
    SL[Slack / Teams adapter]
    SMS[SMS + voice adapter]
    APP[Sea Suite app]
    OC[Owner Console + Admin]
  end

  subgraph Harness["Harness"]
    ING[ingress<br/>webhook + message intake]
    SCH[harness-scheduler]
    EXE[harness-executor<br/>run workers]
    OUT[outreach-service<br/>the one door to humans]
    ESC[escalation-router]
  end

  subgraph Agents["Manager agents (Chonks)"]
    CH[Chonk runtime<br/>planner · analyst · chaser · writer]
  end

  subgraph Determ["Function services"]
    PLN[planning-solver]
    CAP[capacity-model]
    SCD[scheduling-solver atoms]
    CAL[calibration-engine]
    LED[budget-ledger]
  end

  subgraph State["State"]
    OM[(Org Model store<br/>versioned graph)]
    MEM[(memory-service<br/>people memory · decision log · narrative)]
    EVT[(event log<br/>append-only, per tenant)]
    RM[(read models<br/>integration mirrors)]
  end

  POL[policy-engine]
  MG[model-gateway]
  CE[content-engine]
  AUD[(audit-service<br/>hash-chained)]
  INT[integration connectors<br/>work · comms · people · finance · atoms]

  SL & SMS & APP --> ING
  OC --> ING
  INT --> ING
  ING --> SCH
  SCH --> EXE
  EXE --> CH
  CH --> MG
  CH --> PLN & CAP & SCD & CAL & LED
  CH --> OM & MEM & RM
  EXE --> POL
  OUT --> POL
  CH --> OUT
  ESC --> OUT
  OUT --> SL & SMS & APP & OC
  CH --> CE
  CE --> OUT
  INT --> RM
  EXE --> INT
  EXE --> EVT
  EXE --> AUD
  OUT --> AUD
  OM --> AUD
  MEM --> AUD

2.1 Component inventory

Component Responsibility Owns (data) Interface Why it is a separate boundary
ingress Receives webhooks and surface messages, verifies signatures, normalizes to IntegrationEvent / InboundMessage, writes to event log, hands to scheduler. Nothing durable except the inbound event record. HTTPS webhooks; surface SDK callbacks. External input is untrusted. One verification and normalization point; everything downstream sees typed events, never raw payloads.
harness-scheduler Turns cadences, events, IC messages, escalations, and handoffs into RunRequests; admission control per tenant; missed-run detection. Cadence fire index; per-tenant queues; leases. Internal gRPC; event log. Fairness and "cadences never silently skip" must not depend on any worker being healthy.
harness-executor Executes runs: assembles context, invokes the Chonk runtime, enforces budgets and run-level policy gates, persists every step as events. Run state (projection of events). Internal. Isolates the non-deterministic part (agent) inside a deterministic envelope with budgets and gates.
Chonk runtime One process type parameterized by charter. Runs planner → analyst/chaser → writer sub-agents with a tool allowlist. Nothing durable. Stateless between runs. Called by executor; calls tools. Agents must be replaceable, evaluable, and unable to persist state outside memory-service.
policy-engine Classifies every proposed action into an autonomy class, applies graduation state and listening period, evaluates hard lines, thresholds, outreach caps, quiet hours. Pure function over (action, Org Model version, policy set). Policy definitions live in the Org Model; the engine is stateless. classify(action) → Verdict. Guardrails are a product commitment (PRD §11). A pure, testable evaluator is auditable and cannot be argued with by a model.
outreach-service The single write path to humans. Applies caps, quiet hours, tone dial, symmetry rules, delivery retries, fallback surfaces; guarantees IC → human escalation delivery. Outreach ledger (per person per day), delivery state. send(OutreachRequest) → OutreachResult. See A3. The trust cliff (PRD §16) is mostly about messages.
escalation-router Deterministic routing of escalations to the correct human: manager of record, owner, designated safety contact. Cannot be bypassed by agents. Escalation records. route(Escalation) → Route. The escalation right is uncancellable; routing must not depend on a model choosing to do it.
Org Model store Versioned graph of Person, Role, Org, Goal, Project, Cadence, Policy, Decision and typed edges. Every mutation carries reason and actor. The graph, change log, snapshots. orgmodel-api (gRPC internal, REST/GraphQL external). Shared truth across every Chonk; owner-correctable; versioned for replay. Different lifecycle from memory.
memory-service People memory (preferences, history, growth), decision log, org narrative. Every fact carries provenance, visibility, retention class. Serves the "what my manager knows" page. Memory facts. read(subject, scope), write(fact), redact. Privacy semantics (visibility, retention, symmetry) are different from the Org Model's. Keeping them apart makes minimization enforceable.
Function services planning-solver, capacity-model, scheduling-solver (atoms), calibration-engine, budget-ledger. Deterministic, typed, explainable. budget-ledger owns ledger entries; others are stateless over Org Model + read models. gRPC. See A4.
read models / integration mirrors Per-tenant mirrors of work, comms, people, finance, and atoms systems, refreshed by webhooks and polling. Agents read mirrors, not live APIs. Mirror tables per connector. SQL via mirror-api. Latency (<5 s thread replies), rate limits, and snapshot semantics for receipts.
integration connectors Read/write adapters per provider with least-privilege scopes. Writes are executed only by the executor after a policy verdict. Connector config, tokens (encrypted per tenant). Provider APIs. Each connector is a security surface (PRD §16). Depth over breadth; MCP-first.
model-gateway Provider-agnostic completion API with tiers, structured output, caching, per-tenant provider config, cost metering, no-training enforcement. Call ledger, cache. complete(ModelRequest) → ModelResponse. Portability and cost. Agents never see a provider SDK.
content-engine Renders grounded scripts into Chonk-voiced text, video, comics, cards. Consumes writer output with citations; never raw data. Rendered assets (object store). render(ContentJob). Expensive, brand-sensitive, and asynchronous. Must not be able to invent claims.
surface adapters Slack, Teams, SMS/voice (Twilio), the Sea Suite app, Owner Console, admin. Translate OutreachRequests and inbound messages. Thread ↔ person mapping. Provider SDKs. Thread-first product; adapters are thin so the product logic stays surface-agnostic.
audit-service Append-only, hash-chained log of decisions, messages, data access, and policy verdicts with a "why" per action. Exportable. Audit entries. append, export, verify. Integrity must be independent of application bugs. Owned by the Chief Security Officer.
console-api / admin-api BFFs for Owner Console and admin. Direction editor, health, approvals inbox, ask-anything, graduation, integrations. Nothing durable. REST + GraphQL, see Data Model and API Reference. Owner and admin surfaces have different auth and different read patterns from the IC thread.

3. Component details

3.1 Harness

The Harness is three services (ingress, harness-scheduler, harness-executor) plus the two chokepoints (outreach-service, escalation-router) and the event log. Internals are in Harness and Orchestration; the architectural contracts are:

  • Everything is a Run. A run has exactly one trigger (CADENCE_TICK, INTEGRATION_EVENT, IC_MESSAGE, ESCALATION, HANDOFF, HUMAN_DECISION, REPLAY, MANUAL), one Chonk, one tenant, one idempotency key, one budget, and one pinned Org Model version.
  • Runs are pure over their inputs. Context is assembled from the pinned Org Model version, memory as of run start, and read-model snapshots. Model calls are recorded with request hashes and responses so replay can either re-execute or substitute the recorded response.
  • Actions are proposed, never executed, by the agent. The Chonk runtime returns ProposedAction[]; the executor asks the policy engine for a verdict and then executes, proposes, packets, or shadow-logs. A HUMAN_ONLY action has no execution code path at all — there is no function the executor could call to, for example, terminate employment.
  • Missed runs are escalations. If a cadence's fire window passes without a run reaching SUCCEEDED, the scheduler emits cadence.missed and the escalation-router notifies the manager of record and Sea Suite ops. This is the reliability requirement in PRD §12 made concrete.

3.2 Org Model store

Postgres, one logical database per cell, tenant_id on every row with row-level security, a nodes table and an edges table with JSONB attributes, plus a changes table (the change log) and periodic snapshots. Graph traversals (org tree, escalation chain, dependency closure) use recursive CTEs; depth is bounded by invariants (no cycles). Full schema and invariants: Org Model Spec.

Why not a graph database: the graph is small per tenant (hundreds to low thousands of nodes), the queries are shallow, and the versioning/RLS/backup story in Postgres is worth more than traversal speed we do not need.

3.3 Manager agents

One runtime, nine charters. A Chonk is charter + voice + tool allowlist + sub-agents. The runtime is stateless; identity is data injected at run start. Which Chonks exist for a tenant is a roster configuration; in Org mode the default crew is the org's primary Chonk plus Chonksworth for the weekly update. Details: Manager Agents.

3.4 Function services

Service Input Output Explainability contract
planning-solver Goals, projects, dependencies, capacity, constraints (hard lines) Ranked plan with assignments and dates; diff versus previous plan Every moved item carries because: RuleEvaluation[]
capacity-model People, allocations, PTO, historical throughput Capacity per org per period with confidence Inputs and formula version recorded in receipt
scheduling-solver (atoms) Demand forecast, availability, labor rules, roles per shift Shift schedule; swap feasibility Constraint list with satisfied/violated per shift
calibration-engine Feedback items across the org, rubric Normalized ratings, drift flags Shows the comparable set and the adjustment applied
budget-ledger Plan lines, actuals from finance mirrors, requests Variance, remaining envelope, threshold check Threshold rule and ledger lines cited

Each returns a ServiceResult<T> with evaluations: RuleEvaluation[], inputsHash, and serviceVersion, which the analyst sub-agent copies into receipts unchanged.

3.5 Memory

Three stores with different semantics under one service:

  • People memory: facts about a person, each with source, visibility, retentionClass, writtenByRun. Readable by the subject on the "what my manager knows" page. Write rules in Manager Agents §6.
  • Decision log: every Decision with its receipt, the human's response, and the outcome. Immutable.
  • Org narrative: dated, cited summaries per org ("what happened this week") that seed briefs and updates. Regenerated, never edited.

Memory is never the system of record for anything in the Org Model. If a fact belongs in the graph (a person changed teams), the agent proposes an Org Model change; it does not write it to memory.

3.6 Policy engine

Pure evaluation: classify(action, ctx) → Verdict { mode, reasons, gates, thresholds } where mode ∈ {SHADOW, PROPOSE_CONFIRM, AUTONOMOUS, HUMAN_ONLY, BLOCKED}. Inputs are the action, the tenant phase (listening or active), the per-function graduation state, applicable Policy nodes (hard lines, thresholds, outreach caps, labor rules), and the actor Chonk's charter. The evaluator is invoked three times per action lifecycle: at admission (may this run exist?), at action time (how may this action proceed?), and at send time inside outreach-service (may this message go, now, to this person?). Rule language: Policy Engine.

3.7 Integrations

Connectors are split into mirror (read, webhook + poll, writes to read models) and actuator (write, invoked only by the executor after a verdict). A Chonk never holds a provider token. Each connector declares its scopes, its event types, and which ManagementFunctions it feeds. The v1 list is in the PRD §10; contracts are in Integrations Spec and webhook payloads in Data Model and API Reference.

3.8 Content engine

Takes a ContentJob { script, citations, format, voiceProfile, toneDial } where script is the writer sub-agent's output with [n] citation markers already validated against a receipt. Renders to text, video (avatar + voice), comic, or card. The engine can reformat and shorten; it cannot add claims. Every rendered asset embeds the receipt ID and is published only through outreach-service.

3.9 Surfaces

Thread-first. Each managed person has exactly one manager thread on their preferred surface; the Owner Console is a web/mobile app; the voice line is an atoms-mode surface; admin is web. Surface adapters own the mapping (tenant, person) ↔ (provider, channel/thread id) and nothing else.

3.10 Audit

Every event that crosses a trust boundary — policy verdict, outreach sent, data access by a run, Org Model change, memory write, human decision — becomes an audit entry: { tenantId, seq, ts, actor, action, subject, why, receiptId?, prevHash, hash }. Hash-chained per tenant; the chain head is anchored daily to an external write-once store. Exportable in JSONL. The Chief Security Officer's runs verify the chain and report breaks as P0 incidents.

4. Canonical flows

4.1 Morning brief

Trigger: the person's brief cadence fires inside their chosen window (e.g., 08:15–08:45 local). Owner Chonk: Chief Human Officer (or the org's primary Chonk in Org mode).

sequenceDiagram
  autonumber
  participant SCH as harness-scheduler
  participant EXE as harness-executor
  participant POL as policy-engine
  participant OM as Org Model
  participant MEM as memory-service
  participant RM as read models
  participant CH as Chonk runtime (CHO)
  participant MG as model-gateway
  participant OUT as outreach-service
  participant SL as Slack adapter
  participant AUD as audit

  SCH->>EXE: RunRequest(CADENCE_TICK, cad=brief, person=P, key=cad|fireAt)
  EXE->>POL: preAdmission(run)
  POL-->>EXE: ADMIT (phase=ACTIVE, checkins=AUTONOMOUS)
  EXE->>OM: snapshot(P, orgs, goals, projects, cadences) @version
  EXE->>MEM: read(subject=P, scope=brief)
  EXE->>RM: waitingOn(P), waitedOnBy(P), overnightDelta(P)
  EXE->>CH: run(objective=morning_brief, ctx)
  CH->>CH: planner → steps [analyst.delta, analyst.waiting, writer.brief]
  CH->>MG: complete(analyst: rank what matters, structured)
  CH->>MG: complete(writer: brief in CHO voice, tone=0.7, cite [n])
  CH-->>EXE: ProposedAction(SEND_MESSAGE, receipt R1)
  EXE->>POL: classify(SEND_MESSAGE brief)
  POL-->>EXE: AUTONOMOUS
  EXE->>OUT: send(person=P, kind=brief, body, receipt=R1)
  OUT->>POL: sendGate(P): cap ok, window ok, tone ok
  OUT->>SL: postMessage(thread(P))
  OUT->>AUD: append(outreach.sent, why, R1)
  EXE->>MEM: write(fact: brief delivered, waiting-on list) [visibility=self]
  EXE->>AUD: append(run.succeeded)

Notes on the flow:

  • Step 5–6 read mirrors, not live APIs. The receipt records mirror snapshot timestamps, so the brief can say "as of 07:02".
  • The writer never sees raw tickets; it sees the analyst's ranked findings with source IDs. Grounding is validated by the executor: every [n] in the body must resolve to a receipt source.
  • If the brief cannot be produced before the window closes (model outage, backpressure), outreach-service delivers the fallback brief: a deterministic template rendered from read models only, marked as such. A missed brief is a cadence.missed escalation.

4.2 Blocker chase across two teams

Trigger: GitHub webhook pull_request.review_requested aged 20 h without review. PR author Ana is in Platform (managed by the Chief Build Officer); reviewer Ben is in Product Engineering (also Chief Build Officer, but a different org with a different manager of record).

sequenceDiagram
  autonumber
  participant GH as GitHub
  participant ING as ingress
  participant RM as read models
  participant DET as stall detector (function svc)
  participant SCH as harness-scheduler
  participant EXE as harness-executor
  participant CH as Chonk runtime (CBO)
  participant OM as Org Model
  participant POL as policy-engine
  participant OUT as outreach-service
  participant ESC as escalation-router

  GH->>ING: webhook pull_request (signed)
  ING->>RM: upsert pr#412 (review_requested, 20h)
  RM->>DET: evaluate stall rules for pr#412
  DET->>SCH: signal.stall.detected(subject=pr#412, owner=Ana, dep=Ben, age=20h)
  SCH->>EXE: RunRequest(INTEGRATION_EVENT, chonk=build, key=gh|delivery_id)
  EXE->>OM: who is Ben? org=ProdEng, mor=Dana, chonk=build, escalation chain
  EXE->>CH: run(objective=unblock, signal, ctx)
  CH->>CH: planner → [analyst.impact, chaser.ask_reviewer, chaser.inform_author]
  CH-->>EXE: ProposedAction(SEND_MESSAGE to Ben, receipt R2)
  CH-->>EXE: ProposedAction(SEND_MESSAGE to Ana "chasing", R2)
  EXE->>POL: classify(both)
  POL-->>EXE: AUTONOMOUS (unblocking graduated · Ben cap 1/3 today)
  EXE->>OUT: send(Ben, kind=chase, R2)
  EXE->>OUT: send(Ana, kind=status, R2)
  OUT-->>EXE: delivered · followup scheduled +4h business
  Note over SCH,EXE: +4h, no review, no reply → follow-up run (same subject key)
  CH-->>EXE: ProposedAction(ESCALATE to Dana (MoR ProdEng), R3)
  EXE->>POL: classify(ESCALATE_TO_MOR)
  POL-->>EXE: AUTONOMOUS (router-owned)
  EXE->>ESC: route(escalation, org=ProdEng)
  ESC->>OUT: send(Dana, kind=escalation, R3, uncapped)
  Note over CH: if Ben's non-response is a pattern → handoff to CHO (people problem)

Notes:

  • Cross-team means the chase message to Ben goes to Ben's manager thread. If Ben's org were managed by a different Chonk, the run would issue a HANDOFF to that Chonk rather than messaging Ben directly; a Chonk only speaks in threads it manages.
  • Outreach caps are per recipient, per day, across all Chonks (default 3 chases). The third chase of the day to Ben would be suppressed with outreach.suppressed and the run would escalate instead of nagging.
  • Escalating to a manager of record is routed by escalation-router, not chosen by the model, and is uncapped. Escalating to the owner (into the weekly "three decisions") is Chonksworth's triage and is PROPOSE_CONFIRM.

4.3 Over-threshold approval

Trigger: an IC asks in-thread "can I expense a $4,800 Datadog upgrade?" Policy: engineering tooling envelope is $3,000/month per org without approval. Owner Chonk: Chief Fin Officer.

sequenceDiagram
  autonumber
  participant IC as IC thread (Slack)
  participant ING as ingress
  participant EXE as harness-executor
  participant CH as Chonk runtime (CFO)
  participant LED as budget-ledger
  participant POL as policy-engine
  participant DEC as Decision (Org Model)
  participant OUT as outreach-service
  participant OC as Owner Console
  participant RAMP as Ramp connector (actuator)
  participant AUD as audit

  IC->>ING: message "expense $4,800 Datadog upgrade?"
  ING->>EXE: RunRequest(IC_MESSAGE, chonk=fin, key=slack|ts)
  EXE->>CH: run(objective=handle_request, ctx)
  CH->>LED: check(org=Platform, category=tooling, amount=4800)
  LED-->>CH: over threshold (3000) · envelope remaining 1,250 · vendor history · evaluations
  CH-->>EXE: ProposedAction(APPROVE_SPEND 4800, recommendation=approve, R4)
  EXE->>POL: classify(APPROVE_SPEND, amount=4800)
  POL-->>EXE: PROPOSE_CONFIRM (threshold rule pol_tooling_3k)
  EXE->>DEC: create Decision(kind=spend_approval, status=proposed, receipt=R4)
  EXE->>OUT: send(owner, kind=approval_request, R4)
  OUT->>OC: approvals inbox item
  EXE->>OUT: send(IC, "sent to Cody with a recommendation to approve · ETA today")
  EXE->>AUD: append(decision.proposed)
  Note over EXE: run → WAITING (resume key = decision id)
  OC->>ING: owner taps Approve (decision id, actor=Cody)
  ING->>EXE: RunRequest(HUMAN_DECISION, resume run)
  EXE->>DEC: status=confirmed, decidedBy=Cody
  EXE->>RAMP: approve request (actuator, scoped token)
  EXE->>LED: post ledger line
  EXE->>OUT: send(IC, "approved by Cody · card limit raised")
  EXE->>AUD: append(decision.confirmed, decision.executed)

Notes:

  • The Chonk cannot approve over threshold even if it "decides" to: the executor has no autonomous execution path for a PROPOSE_CONFIRM verdict. The Ramp actuator is only invoked after a decision.confirmed event with a human actor.
  • The decision expires (default 72 h) with a reminder at 24 h; expiry re-notifies the IC and records decision.expired. Nothing silently disappears.
  • The receipt includes the ledger evaluation, the policy rule that fired, the vendor's spend history, and the recommendation rationale; the owner sees the same receipt the IC can see.

5. Multi-tenancy and isolation

5.1 Cells

A cell is { Postgres cluster, Redis, stream topics, executor pool, object-store prefix }. Tenants are pinned to a cell; a global control plane holds tenant → cell routing, billing, and model-gateway configuration. Cells are the blast radius for infrastructure failures and the unit of horizontal growth. Enterprise customers may get a dedicated cell; the code path is identical.

5.2 Isolation layers

Layer Mechanism What it prevents
Data tenant_id on every row; Postgres RLS with the tenant set from the request context; no cross-tenant queries exist in application code Accidental cross-tenant reads by application bugs
Keys Per-tenant KMS data key; PII columns, connector tokens, memory blobs, receipts encrypted with it A dump of one tenant's rows is useless without that tenant's key; offboarding a tenant = key destruction
Compute Run context carries exactly one tenant; a run cannot open a second tenant scope; tool calls are checked against the run's tenant A prompt-injected run cannot read another tenant
Queues Per-tenant partitions and concurrency limits in the scheduler A noisy tenant cannot starve another
Model Per-tenant provider config and budget; prompts carry a tenant-scoped cache namespace Cross-tenant prompt cache leakage; cost overruns
Memory Per-person visibility and retention on top of tenant isolation Over-collection inside a tenant

5.3 Portfolio owners (cross-tenant reads)

An owner of several companies or locations has a PortfolioGrant per tenant ({ granteeId, tenantId, scope: 'health' | 'approvals' | 'full' }). The portfolio service fans out read-only queries to each tenant's console-api under that grant and assembles the view client-side of the data boundary. There is no table that contains two tenants' rows. Approvals in the portfolio view are executed in the target tenant with the grant as actor.

5.4 Locations in Atoms mode

A multi-location operator is one tenant; locations are Org nodes of kind location. Location-level isolation is authorization, not tenancy. This keeps cross-location scheduling (borrowing a line cook) inside one graph.

6. Model-provider abstraction

model-gateway is the only component that knows a provider exists.

interface ModelRequest {
  tenantId: TenantId;
  runId: RunId;
  tier: 'fast' | 'standard' | 'reasoning';   // never a provider model name
  purpose: 'planner' | 'analyst' | 'writer' | 'chaser' | 'ask';
  system: PromptSection[];                      // ordered, cache-stable prefix first
  messages: Message[];
  tools?: ToolSpec[];
  outputSchema?: JsonSchema;                    // structured output required for all non-writer calls
  budget: { maxOutputTokens: number; maxCostUsd: number };
  cacheNamespace: string;                       // tenant-scoped
}

interface ModelResponse {
  content: string | object;
  toolCalls?: ToolCall[];
  usage: { inputTokens: number; outputTokens: number; cachedTokens: number; costUsd: number };
  provider: { name: string; model: string; requestHash: string };  // recorded, not chosen by caller
  latencyMs: number;
}

Rules:

  • Tier → model mapping is per tenant and per cell, editable without deploy. Fallback chain per tier (e.g., standard: [providerA/x, providerB/y]); a fallback is recorded in the run's events so a replay knows which provider answered.
  • No training on customer data is a contract term with every provider and a technical control: zero-retention endpoints only; the gateway refuses providers not flagged zeroRetention: true for tenant data.
  • Structured output is mandatory for planner, analyst, and chaser calls; the gateway validates against outputSchema and retries once with the validation error before failing the call.
  • Prompt caching: the system-prompt prefix (charter, tenant direction, voice) is cache-stable and identical across runs of the same Chonk for the same tenant; per-run context follows it.
  • Replay mode: with replay: { runId, stepId }, the gateway returns the recorded response instead of calling the provider.
  • Cost meter: the gateway is the source of truth for model spend per tenant, per person, per function. The non-functional cost target (model spend well under 10% of price) is enforced here as a soft budget with alerts and, at 150%, a hard budget that downgrades tiers.

7. Scaling units

Unit What scales with it Limits and defaults
Tenant Queues, concurrency, budgets, keys Default 8 concurrent runs; 200 queued; raised per plan
Managed person Cadences (brief, check-in, 1:1), memory volume, outreach caps ~6 scheduled runs/day/person plus signal-driven runs
Run Executor CPU, model calls, tool calls Budgets in Harness and Orchestration §7
Cell Postgres, Redis, executor pool Target 500 tenants or 25k managed people per cell, whichever first
Connector Webhook ingress rate, mirror refresh Per-provider rate limits respected by the mirror; polling backoff
Model tier Provider throughput Per-cell token/min reservation per tier; queue if exhausted

Executors are stateless and autoscale on queue depth per cell. The scheduler runs as a small leader-elected set per cell with shards over tenants. Postgres scales vertically per cell first; sharding a cell is a new cell.

8. Failure domains and degradation

Domain Failure Blast radius Degradation mode Detect / recover
Model provider Outage or latency spike All tenants on that provider in the cell Fallback chain per tier; if all providers fail: briefs use the deterministic fallback template, chases pause (signals persist), IC thread replies with a "delayed" acknowledgement, approvals still route (ledger is deterministic) Gateway health probes; run.failed{class=MODEL} rate; auto-recover on probe success
Connector Provider outage, token revoked, webhook drop One tenant, one connector Mirrors serve stale data with staleSince in receipts; writes queue with deadline; Chonk says "as of" times; stale beyond 6 h → integration.connector.degraded escalation to admin Mirror freshness metric; reconciliation poll
Surface Slack/Teams/Twilio outage Delivery for that surface Outreach retries then falls back to the next surface in the person's list (Slack → email → app); IC → human escalation falls back through all surfaces including SMS and voice Delivery failure rate per surface
Org Model store Postgres primary failure Whole cell Failover to replica (RPO ≈ 0 with sync replication); runs in flight fail and are retried by idempotency key Standard DB monitoring; runbooks in Observability and Runbooks
Scheduler Leader loss Cell-wide cadence firing delayed Leader election < 30 s; cadence index is durable so nothing is lost; missed-window detection still fires escalations Leader heartbeat
Executor pool Crash or OOM In-flight runs Runs are event-sourced; a replacement worker resumes from the last committed step under the same lease Lease expiry
Event log Write failure Cell Ingress returns 503 to webhooks (providers retry); scheduler pauses admission; nothing is processed without being recorded first Write latency, error rate
Audit anchor External anchor unavailable Audit verifiability delay Chain continues; anchor retried; alert after 24 h Anchor lag metric

Degradation is itself an event (system.degraded{domain}), visible to admins and in the Owner Console health header, and the Chonks say so in plain words ("I couldn't read Linear this morning; this brief is from 06:10 data").

9. Risk controls (closed loops)

RC-ARCH-01 — Guardrail bypass by an agent. Owner: Chief Security Officer. Metric: policy.violation_blocked count and any HUMAN_ONLY execution attempt (target: 0 executions, ever).

  • Detect: executor counts every action whose verdict was BLOCKED or HUMAN_ONLY but whose tool call was still attempted; audit-service cross-checks that no actuator write exists without a matching decision.confirmed or AUTONOMOUS verdict.
  • Contain: the run fails closed; the Chonk's graduation for that function regresses one state; the tenant's admin and Sea Suite ops are paged.
  • Correct: root-cause (prompt regression, tool spec drift, model change); patch the prompt contract or tool schema; replay the run in shadow to confirm.
  • Learn: the case is added to the eval suite (Evaluation and Testing) as a red-team fixture; the policy engine gains a rule if the gap was in policy, not in the agent.

RC-ARCH-02 — Cross-tenant data exposure. Owner: Chief Security Officer. Metric: cross-tenant read attempts (target 0), RLS denial count.

  • Detect: RLS denials logged; canary rows per tenant with unique tokens; any appearance of a canary token in another tenant's model request or outreach is an alarm.
  • Contain: freeze the executor pool for the cell; revoke the run's lease; quarantine the audit segment.
  • Correct: identify the code path; hotfix; rotate the affected tenants' data keys if exposure is confirmed; notify per Security, Privacy, Compliance.
  • Learn: add the path to the integration test matrix that runs every query under a mismatched tenant context.

RC-ARCH-03 — Model cost runaway. Owner: Chief Fin Officer. Metric: model spend / price per managed person per month (target < 10%).

  • Detect: model-gateway per-tenant daily spend versus budget; per-run token anomalies (> 3σ for its run kind).
  • Contain: tier downgrade at 150% of daily budget; non-critical run classes (content, reporting) deferred to the next day.
  • Correct: identify the run kind and prompt driving spend; move computation into a function service if the model was doing math; tighten maxTokens for that run kind.
  • Learn: budget defaults per run kind are re-derived monthly from p95 usage; the change is a Policy change with a reason.

10. Edge cases

ID Scenario Signal / detection Rule Action (autonomy class) Escalation Test
EC-ARCH-001 Two Chonks both want to message the same IC in the same minute (CBO chase + CHO check-in) outreach-service sees two OutreachRequests for one recipient within the coalescing window (5 min) One door: messages to a person are serialized and coalesced per thread Coalesce into one message with two sections, attributed to each Chonk (AUTONOMOUS) None sim-lanternfish: fire a stall signal and a check-in cadence for the same person at T; assert one delivered message with two receipts
EC-ARCH-002 Model provider returns a well-formed but wrong provider name in a fallback Gateway provider.name mismatches the configured chain entry Recorded provider is what the gateway observed, never what was requested Log model.provider.mismatch; continue (AUTONOMOUS) Ops alert if > 1% of calls Inject a mismatching stub provider in the gateway test harness
EC-ARCH-003 Owner Console approval arrives for a decision whose run was already cancelled (tenant paused) HUMAN_DECISION trigger finds run in CANCELLED Human decisions are never lost; they re-open a fresh run Create a new run with trigger HUMAN_DECISION and the original decision id; execute if still valid, else record decision.stale and inform the owner (PROPOSE_CONFIRM for re-execution) Owner notified in-thread Cancel a run in WAITING then post the approval; assert decision.executed or decision.stale
EC-ARCH-004 Webhook replay attack: the same signed GitHub delivery posted twice a day apart Idempotency key gh|delivery_id exists Dedup window is 7 days Drop with run.deduplicated (AUTONOMOUS) None Re-post a recorded delivery; assert no second run
EC-ARCH-005 Cell Postgres failover mid-run during an actuator write (Ramp approve) Run resumes from the last committed step; the actuator step has no step.completed Actuator writes are idempotent using the decision id as the provider idempotency key Re-issue the write; provider dedups (AUTONOMOUS) If the provider does not support idempotency keys, the step is marked NEEDS_RECONCILE and escalated to admin Kill the primary during the actuator step in the integration environment
EC-ARCH-006 IC sends "I need to talk to a human" during a total model outage ingress keyword and intent classifier (deterministic list) match Escalation right does not depend on the model escalation-router delivers to the manager of record over every surface, and acknowledges the IC with a deterministic message (AUTONOMOUS, uncapped) MoR; owner if MoR unacknowledged in 2 h Disable the gateway in sim; send the phrase; assert delivery within 60 s

Open questions

  1. Cell sizing. 500 tenants / 25k people per cell is a guess; the first two concierge pilots should give p95 run cost and Postgres load per managed person to re-derive it.
  2. Read-model freshness contract. Should briefs wait for a mirror refresh (adds latency, improves accuracy) or always use the last snapshot? Current answer: use the snapshot and say "as of"; revisit after pilot feedback.
  3. Voice line architecture (atoms). Whether inbound voice is transcribed at the edge (Twilio) or in-cell affects the PII boundary. Decide before Atoms build; see Atoms Mode Spec.
  4. Dedicated cells versus shared for SOC 2 scope. Whether a dedicated cell changes the audit boundary enough to be worth offering before Full Suite GA.
  5. Agent workers as nodes (AG-1). Managed agent workers will call tools too; whether they run inside the same executor with a different charter or as external MCP endpoints is undecided. Leaning external.