Skip to content
GitHub
Decisions

ADR-101: Alpha agent topology — one agent (the World Agent), serving operators directly

Context

The agent-stack audit (2026-06-04; planning/0.3.0/agent-stack-audit/findings.md) established that the executable-agent stack had never made a real model call on the production path, and prompted a re-examination of the Operations Agent.

Examined on its merits, the Operations Agent earns no place at alpha:

  • It is inertbuild_ops_agent is never called in a booted process; its API routes are unwired; no agent-task consumer runs it.
  • Its operational tooling — DLQ inspection / event replay, cluster triage — is plain operator capability that needs no LLM agent (per ADR-054, Supabase Studio / direct SQL is the sufficient operator path at alpha).
  • Its one genuinely agent-shaped behavior — conversational World-Model co-authoring — is subsumed by the World Agent’s operator-direct role (ADR-078 D2, ADR-081). The World Agent already serves operators directly; a second operator-facing agent would fragment the operator surface without a distinct responsibility (the same reasoning ADR-078 used to reject a third agent now applies to the second).

This ADR sets the alpha agent topology: a single agent, the World Agent.

Decision

D1 — Alpha topology is one agent: the World Agent

The architecture runs a single production agent, the World Agent (spectral.worlds, per ADR-058 / ADR-081). The Operations Agent is removed: its runtime, its memory schema (platform.operations_agent_memory), and its agent-specific tool surface leave the architecture. No second agent runtime exists.

D2 — The World Agent serves operators directly

Operator needs are met by the World Agent and by non-agent operator surfaces:

  • Authoring / co-authoring (rule curation, distillation, code-generation orchestration, conformity review, evolution proposals) — the World Agent’s operator-direct surface (ADR-081; the authoring cockpit, SPEC-370). This is the primary alpha authoring flow.
  • Operational tooling (DLQ inspection / replay, system health, triage) — plain operator endpoints / Supabase Studio (ADR-054). Not an LLM agent.

Auth + scope on the single World Agent runtime determine what each caller (operator, and post-release customer per ADR-081 D5) may see and do.

D3 — Identity-not-capability session vars

Session vars name identity, not capability. The principal concept for per-user access is the authenticated user: operators are entries in core.users carrying SCOPE_*_OPERATIONS; there is no separate operator identity layer. The reusable identity contract is SESSION_VAR_USER_ID = "app.user_id" (src/spectral/core/db/session_vars.py) — not app.operator_id, which would burn the var on a single capability class.

Any per-user-keyed RLS — including the World Agent’s operator-direct surface and any per-user-owned data — uses this var: the RLS predicate is <owner_col> = current_setting('app.user_id')::uuid, and request middleware sets SET LOCAL app.user_id = <jwt_subject> in the request transaction. This principle outlives the Operations Agent that first motivated it; the identity-session-var contract is governed here.

D4 — What carries forward as substrate; what is removed

Carries forward, governing the single World Agent: the LangGraph framework (ADR-007 surviving sections), conversation persistence (ADR-043), the human-in-the-loop interrupt pattern and framework-layer tool composition (ADR-060). Shared transport the World Agent’s operator-direct chat already uses — the agent-task dispatch seam and the Realtime→SSE proxy — is retained (it is not Operations-Agent-specific).

Removed with the Operations Agent: its composition (build_ops_agent), its tool families (DLQ tool bridge, the substrate-tool factory, the ask_world_agent bridge), its memory schema and repositories, its API routes, and its operations-app workspace surface. The ContentClass.OPERATIONS content class and the SCOPE_*_OPERATIONS roles are not Operations-Agent-specific and are retained. The mechanical code/frontend/migration removal is tracked as SPEC-562 (W4); this ADR is the doctrine.

D5 — The operator-facing surface is the conversational, ingestion-driven authoring cockpit

The single World Agent serves operators through a conversational authoring cockpit: the operator creates a world, chats with the World Agent, and supplies source material as web links + document attachments; the agent consumes, evaluates, and proposes the rules that define the world, driving distillation / web-research / code-generation / proposal as tools within the session. This is the primary alpha authoring flow.

This amends ADR-090 D1/D3, which framed batch source-material distillation as the primary path and conversational authoring as secondary: the distillation / web-research / code-gen / proposal capabilities are the tools the chat agent drives, not a fire-and-forget batch run reviewed afterward. ADR-090’s two-phase workflow shape, gates, and publication cadence stand; only the authoring surface (conversational, operator-driven, incremental) and executor (LangChain per ADR-102, not DSPy) change. It also extends ADR-081’s operator-direct surface into this named cockpit. The chat is a tool-using LangGraph agent; the per-node executor is LangChain with_structured_output + tool-calling.

Alternatives considered

Keep the Operations Agent at alpha. Rejected. It is inert dead code; its operational tooling needs no LLM agent (ADR-054); its co-authoring behavior is subsumed by the operator-direct World Agent (ADR-078 D2 / ADR-081). Carrying it forward is runtime + memory + doctrine surface for no distinct responsibility.

Keep ADR-078 D3’s two-agent topology. Rejected. D3’s two-agent split reflected the pre-pivot stance where the world and World Agent were operator-internal and a separate operator agent (Ops) covered operational concerns. Under the in-band decision-support shift, the World Agent spans operator + customer audiences through one runtime (ADR-078 D2), and operational concerns are plain surfaces — the second agent slot no longer has a distinct subject.

Consequences

  • The alpha agent topology is one agent, governed here; ADR-078’s D1/D2/D4 (Spectral Agent retirement, World Agent owning customer-facing surfaces, ADR-007 substrate) stand.
  • The app.user_id identity-session-var contract is governed by D3 above.
  • Corpus reference-scrub: the ~20 ADRs that name the Operations Agent as a live agent (heaviest: ADR-060 D7/D8, ADR-064, ADR-035, ADR-036, ADR-047, ADR-054, ADR-058, ADR-070) are reconciled to the one-agent topology (SPEC-562 W0).
  • Code, frontend, and migration removal of the Operations Agent is tracked as SPEC-562 (W4); the World Agent’s WorldAgentRunner and the generic LangGraph interrupt substrate are retained even where the Ops Agent was their only consumer.
  • Codex agent-architecture collapses from two-agent (World / Ops) to one-agent (World); the operations-agent page retires (SPEC-562 W0 Codex sweep).
  • ADR-090 D1/D3 are amended and ADR-081 is extended (D5): the primary authoring surface is the conversational ingestion-driven cockpit; both carry pointers to this ADR. ADR-090’s two-phase workflow, gates, and publication cadence stand.

References

  • ADR-078 — retire Spectral Agent; the World Agent owns the surviving customer-facing surfaces
  • ADR-081 — World Agent role + operator-direct surface (amended by D5)
  • ADR-090 — World Agent workflow (D1/D3 authoring-surface framing amended by D5)
  • ADR-058 — World Agent memory
  • ADR-102 — LLM execution stack
  • ADR-054 — DLQ retry semantics (operator path = Studio at alpha)
  • src/spectral/core/db/session_vars.pySESSION_VAR_USER_ID identity contract (D3)
  • planning/0.3.0/agent-stack-audit/replan.md — agent-stack re-plan + decision record