Aller au contenu

Maia Swarm architecture

Status: Planned (R1–R10, S17, S18, MN0–MN5 in the roadmap); no Swarm service exists. It builds on source candidates: vendor executor Sessions and maia mcp (external executors), the S19 Run ledger (coordination), and the registered session.spawn and Session mailbox (peer-agent protocol).

Outcome

Maia Swarm is the one orchestration layer above Maia Node across Projects. It is not another agent harness and runs no second model loop: native work runs through the Maia Agent kernel (decision), and Maia Node owns durable state, events, leases, traces and receipts. Maia is the coordination hub through which any agent or harness reaches another (decision).

operator / CLI / MCP / voice / A2A
              |
       Maia Swarm service
  Projects, Routines, admission,
  Session trees, queues, budgets
        /                 \
Maia Node + Agent       external executor
native Sessions         Sessions

Swarm runs as Effect services inside the one Control Node process (decision); moving it out later must not change operation IDs or Agent definitions. Deployment and failure model: operations. Schemas: contracts. Nouns (Agent, Agent Session, Routine, Workspace, Project): product vocabulary.

Coordinator ownership and visibility

Maia, each Workspace coordinator and each Project coordinator are distinct principals and durable owners (decision). They reuse the Maia Agent kernel but share no model Session, memory, credentials or mutable context. One trusted Control Node enforces every owner (Node enforcement).

OwnerOwns and may discoverExcluded from its discovery surface
Maia / globalIts global conversation, preferences, requests and delegation records; authorized Workspace directory and permitted summariesOther owners' scopes; an unrestricted Project/worker directory or private child context
WorkspaceIts conversation, shared knowledge, objectives and delegation records; its own Project directory and permitted summariesOther Workspaces, their names, Projects or activity
ProjectIts conversation, knowledge, work, artifacts and authorized Sessions/workersOther Projects, including siblings in the same Workspace
WorkerIts assignment, resources and explicitly supplied or granted Project contextSibling workers and wider Project records unless the assignment grants those reads; all other Projects
  • Membership is not content permission: discovering a child permits only its admitted summary, never its history, tools or credentials.
  • Bounded Session discovery and peer messaging follow D-166: a Session sees the Session ID, Project, role and state of the Sessions it may message. That merges no owners and grants no other owner's history, memory, tools or credentials.
  • A coordinator never inherits the human client's portfolio grants. Every activation binds an exact owner kind and identity, principal, Session and policy revision; role labels grant nothing.
  • Global and Workspace owners need an explicit protocol and storage extension (R3) before activation; never fake one with a caller Project ID, a synthetic operator Project or a wildcard. Existing IDs and history are migrated, or activation is refused.
  • A Workspace may own several ordinary Sessions, even with no Project, repository or Execution Environment, without creating or waking a coordinator (Session ownership contract). Such a Session gains no coordinator authority, and Project execution is delegated, never obtained by reparenting.
  • The human client may address any exact Session in its portfolio; that gives the receiving Project no lateral visibility. Node can deliver an exact admitted request through a Workspace boundary without waking its model. Idle coordinators hold no model stream.
  • Maia mediates cross-Workspace work; a Workspace mediates cross-Project work inside it. Siblings never discover each other beyond the peer directory of D-166.
flowchart TD
    Maia["Maia: authorized Workspace directory"]
    Maia --> WA["Workspace A: own Project directory"]
    Maia --> WB["Workspace B: own Project directory"]
    WA --> PA1["Project A1: own work only"]
    WA --> PA2["Project A2: own work only"]
    WB --> PB1["Project B1: own work only"]
    PA1 --> Workers["Assignment-scoped workers"]

The global coordinator, Maia, owns a distinct Session and principal. It routes requests through Workspace boundaries, receives permitted results, asks for previews and never consumes a human-only confirmation. Every request it sends binds source message, principal, owner scope and policy revision, exact destination, input digest, delegated rights and receipt lineage; an ambiguous scope needs clarification. Human portfolio reads and exact controls stay available through CLI, API and SDK when any coordinator is down, and there is no all mutation selector or inferred fallback coordinator.

Parent-mediated delegation

The parent owns the request and its delegation record; the recipient owns the accepted work and its execution resources. Node links the exact identities instead of giving two owners one mutable task. Cross-owner delegation is not a Session-tree child and never inherits grants. It composes the existing message, task and Session owners; no task-graph product, workflow language or scheduler is added.

Before delivery the contract freezes sender and recipient owner and Session, source-message references, objective, constraints, acceptance criteria, permitted input and output classes, policy and revision fences, expiry, budgets, idempotency and receipt lineage. References and reply capabilities never reveal hidden sibling IDs. Node keeps full provenance and projects only what the recipient may see.

  • A Project returns a result or an out-of-scope need to its bound parent. The parent may pass an explicitly permitted input or result to another child, never private text, titles, paths or citations merely because both children are its own; read access and model redaction are not disclosure permission.
  • Replies are data, not grants. Steering or cancellation needs the rights recorded for that delegation and is settled only when observed.
  • Retries reconcile the original identities. A late result after revocation, expiry or recipient replacement admits no new work.

Router-first execution and attention

This section owns routing, parallelism, work and feedback policy and role boundaries. Node owns context, journals and evaluation; presentation owns client signals and delivery; operations owns outage recovery; contracts lists the protocol obligations; R10b sequences delivery. No other scheduler, graph database, queue service or router catalog.

Roles and readiness

Maia, Workspace and Project coordinators use one scoped coordinator implementation: the same routing, context preparation, queues, journals, delegation and feedback, with different owner scope, grants, configuration, model and effort policy and budgets. Each keeps its own Session, history and context graph. An exact authorized Project request goes straight to that Project without ancestor model hops.

  • Coordinators and autonomous bots combine a reasoning LLM with the shared Jev evaluation capability. Bot is a role, not a runtime or CLI resource.
  • Execution workers use LLMs and their admitted tools only, with no Jev access or routing policy; central services prepare their context and model setup.
  • Node validates and executes decisions. Jev supplies judgments, never grants, lifecycle truth or verification. Follow-up work uses the same admission path; replies transfer no ownership.
  • Automation needs both its reasoning model and Jev. If either fails, affected new AI decisions are blocked with an explicit unavailable state; cached scores, FIFO or a fallback LLM never stand in. Manual operations, observations, safety controls and factual acknowledgements stay available, and admitted effects are reconciled, not killed or replayed.

Routing plans and bounded parallelism

  • The Jev router judges eligible intents, destinations and setup without a coordinator pass per known request; complex or ambiguous requests go to a coordinator or ask for clarification.
  • Code supplies candidates; the LLM may propose bounded plans referencing the original input. Keep the whole request and shared constraints ("test both; do not deploy"). Multiple intents get independent judgments.
  • A plan is versioned data over registry operations, admitted Session and delegation primitives and explicit dependencies; never code, a plugin or a workflow language. Node validates targets, rights, schemas, expiry, spend bounds and confirmation rules before admission and again at execution where required. Persistent policy changes need their own preview, confirmation and receipt; model suggestions never register tools or widen authority.
  • Independent branches run concurrently; a join waits only for its named inputs. Journal order records observations; it does not serialize work. Sequential gates stay: authorization before export, approval before consequential work, context compilation before a model call, observed handoff before its acknowledgement.
  • Node bounds each request's branches, depth, time, candidates, retries and spend, and reconciles unknown effects instead of dispatching again. Do not mix private owners' histories to increase batching, and never claim unlimited routes, requests or parallel workers.

Work priority and separate message lanes

Human requests, coordinator task requests, worker events and user feedback are separate lanes over the same SDK, journal and delivery. Node derives authenticated origin: a task or worker report never becomes a human instruction or approval, and cancellation, receipts and confirmations are protected from background traffic.

One Jev scorer uses separate versioned rubrics for work priority and for attention. Node measures deadlines, costs, queue age and dependencies and applies deterministic eligibility, reserved control classes, fair shares, aging and capacity limits first; scores rank only inside an eligible class and keep their model, rubric, revision and expiry. No score bypasses a dependency or confirmation, starves a scope, cancels work, hides a failure or certifies completion. Without a scorer, the default until R10b, the deterministic scheduler orders work FIFO within its class. Where a Project turns ranking on, a scoring failure blocks the autonomous choices that needed it (the readiness rule) instead of falling back to FIFO; manual controls and the reconciliation of admitted effects go on.

Journal-driven acknowledgements and feedback

Work dispatch and feedback use separate bounded queues; the linked journal views stay the record of truth and feedback is a projection. State, resource and mandatory-visibility updates bypass model latency; delivered, seen and acknowledged stay separate observations; an unknown handoff keeps its original identity.

  • Each accepted or queued branch and each confirmed handoff is recorded at once, and the acknowledgement fast lane reports that evidence ("two dispatched; one queued"). Received, queued, dispatched, running, completed and dispatch-unconfirmed are distinct; transport receipt does not prove executor acceptance, and an acknowledgement never replaces confirmation.
  • The feedback router wakes on meaningful events, a bounded deferral deadline or a relevant context change. Its input is the original request, the originating view, feedback already delivered or seen, pending age and notification preferences. Idle is an expiring client signal, not proof of anything.
  • Jev recommends relevance, grouping, timing and an allowed channel; the LLM writes an explanation only when useful. Channels: view update, attention card, grouped summary, conversation reply, opt-in speech on the originating device, bounded deferral, or no interruption. No interruption never deletes evidence or hides owed answers, approvals, failures or unknown outcomes.
  • Deliveries are deduplicated and revalidated against scope, expiry and view generation; only replaceable progress is coalesced; deferral has a finite timer. Slow speech never blocks visual updates, speech is never broadcast and focus is never stolen. Under AI outage, essential status, acknowledgements and manual recovery remain.

End-to-end flow and extension boundaries

flowchart TD
    Clients["Desktop and mobile input with separate focus snapshots"] --> SDK["Public SDK"]
    SDK --> Gate["Node validates origin, scope and input binding"]
    Gate --> Input["Accepted input journal view"]
    Context["Authorized Session graph and ephemeral client context"] -.-> Router
    Input --> Router["Initial Jev routing and setup judgments"]
    Plans["Coordinator-proposed bounded plans"] --> ValidatePlan["Node registry and policy validation"]
    ValidatePlan -.-> Router
    Router --> Admit["Node approval, admission and priority policy"]
    Admit --> Queue["Bounded work queue"]
    Queue --> Dispatch["Dispatch independent eligible branches"]
    Dispatch --> Workers["Parallel LLM-only execution workers"]
    Dispatch --> Reads["Direct admitted operations"]
    Dispatch --> Coordinator["Coordinator LLM when needed"]
    Coordinator --> Plans
    Dispatch -->|"Confirmed handoffs"| Journal["Node journal and event queues"]
    Workers --> Journal
    Reads --> Journal
    Coordinator --> Journal
    Journal -->|"Authorized follow-up evidence"| Coordinator
    Journal --> Ack["Immediate factual acknowledgements and resource updates"]
    Journal --> Feedback["Feedback queue: event, time and context triggers"]
    Context -.-> Feedback
    Feedback --> Analyze["Jev attention judgments and LLM composition when needed"]
    Analyze --> Delivery["Node validates scoped feedback delivery"]
    Ack --> Delivery
    Delivery --> View["Originating visual, text or opt-in voice channel"]

Extend existing owners only: the protocol registry, context projection, shared evaluation rubrics, Node and Swarm admission, and client-core and platform ports. No package extraction, ambient plugins or arbitrary graph properties. Each addition declares inputs, results, errors, ownership, expiry, bounds and contract tests; freeze numeric limits before any capacity claim. PF/DF-29 owns end-to-end acceptance.

Policy boundary

coordinator, specialist, maintenance and administrator are Agent roles only; they add no runtime type or authority. A child is a normal Agent Session linked by parentSessionId. Swarm dispatches only Agent Sessions and Routine ticks; Session-local task items are not scheduling truth.

Swarm decidesSwarm does not persist or enforce directly
Project admission and hold policyauthoritative Project rows and revisions
Agent/Routine snapshot selectionsnapshots, credentials, or provider refresh
Session-tree and peer-delivery policySession rows, events, or message delivery
queue fairness, quotas, and reservation plansleases, reservations, or worker processes
Node/executor placement and handoff intentassignment fencing or external state truth
cross-Project operator projectionsJSONL evidence or repository source changes
voice routing to the exact attached Sessionrealtime audio transport or confirmation proof

Every accepted Swarm decision invokes one registered Node operation; Node owns the row, revision, event, idempotency record and receipt. Operation schemas live only in packages/protocol; the grammar is in the CLI reference.

Project inventory and admission

~/.maia/projects.jsonc and Project-local .maia follow the configuration contracts. Both are desired state, never authority. The operation names here are planned.

  • project.inventory.sync applies only a confirmed inventory digest at the expected registry revision. It may add or relocate exact Projects; omission never deletes, archives, holds or stops one. Relocation fails closed while the Project has a live assignment, unresolved effect, active handoff or root-dependent credential grant. project.add uses the same path. Swarm never hot-applies editor changes.
  • Repository configuration is untrusted until an operator admits an exact snapshot digest (discover, parse, resolve, Doctor, policy validate, candidate, preview, CAS admission, receipt). A repository change yields a candidate that cannot silently broaden tools, Connections, executors, budgets, schedules or cross-Project access; only fields on an immutable protocol allowlist may be admitted automatically.
candidate -> admitted -> active <-> held
active|held -> draining -> held|archived
admitted|active|held -> archived

held blocks new activations and Routine ticks but kills nothing. draining blocks admission and asks running work to settle. Archive needs zero live assignments, reservations, unresolved effects, open handoffs and pending ticks; project.unarchive restores only a Project whose root identity and snapshot still validate, otherwise it returns to candidate.

Project teams and home Sessions

An Agent definition may request homeSession: true: Node atomically obtains or creates the one Session keyed by the exact owner and Agent definition (Projects first; global and Workspace owners wait for R3). Idle, it holds no model stream or worker lease. It wakes on a user message, a Routine tick, an authorized peer message, an A2A task or a recovery event; concurrent wakeups coalesce by Session revision and inbox sequence, and v1 runs one activation at a time.

Coordinator behavior comes from an Agent definition and Skills; the role grants no tools or budget. A Project coordinator's worker children stay in that Project under depth, fan-out, capacity, Connection, executor and cost policy.

One peer-agent protocol

The planned model-facing agent tools (start_agent and its siblings) and the CLI session operations project one durable protocol; there is no in-memory subagent. Registered today: session.spawn links a child Session to exactly one parentSessionId (the CLI refuses it until runtime qualification), and each Session has a durable mailbox reached through session.message.send and session.inbox.check|ack from an attached Session. A mailbox holds at most 256 unacknowledged deliveries and 1 MiB of their content (mailbox_saturated); acknowledging a delivery frees its share and the acknowledged record stays. Planned (D-168): the acknowledged record keeps its trace, not its subject and body. S17 makes this the agent-to-agent channel (decision) and S18b adds the guards (decision).

A child start atomically records Project, parent, root and child Session IDs; parent and child Agent snapshot digests; instruction digest and classification; narrowed environment and tool grants; budget reservation and deadline; placement requirements and capability evidence; tree revision, idempotency key and receipt.

Authority only narrows down the same-Project tree: a child gets at most the intersection of parent grant, child definition, Project policy and operator policy for executor, Connection, environment, network, profile, tools and budget. No intersection reaches a sibling Project. Admission is atomic against every recursive limit:

maxDepth
maxChildrenPerSession
maxSessionsPerProject
maxConcurrentActivationsPerProject
maxReservedTokens / cost / time
maxWakeupsPerMinute
maxMessageBytes / messagesPerMinute
  • One parent and one root per child; cycles are rejected. Child start validates parent, root, Project and treeRevision in one transaction.
  • Relation lifecycle: active -> stop-requested | detached | terminal. On parent completion the definition chooses wait (until named children are terminal or a deadline), request-stop (cancellation asserted only after child terminal events) or detach-to-project (exact transfer plan, revisions, replacement budget and receipt; refused for unresolved effects or cross-Project moves).
  • After a restart a reconciler finds orphaned relations without inventing a parent.

Durable messages

message:  accepted -> expired
          \-> rejected
delivery: available -> claimed -> processed
                    \-> available (visibility timeout + retry)
                    \-> dead-letter|expired
  • A message records the Node-stamped sender, recipient, behavior (ask_question, assign_work, reply by default, report_progress), thread ID, optional reply-to ID, content digest and classification, idempotency key, wake disposition and recipient revision. Today's session.message.send carries recipient, subject, body and command ID; the other fields arrive with S17.
  • A delivery claim lasts 30 seconds with (deliveryId, attempt) idempotency; at most five retries with exponential backoff capped at five minutes, then dead-letter. Content stays only until acknowledgement and a matching read from the recipient harness's reader, with D-168's exceptions for terminal deliveries and content no harness returns (D-168); the trace is kept 30 days unless Project policy shortens it.
  • Same-Project delivery is the default, whichever checkout runs each Session. A Session may also message an exact Session ID in another Project of its Workspace when both Projects' guards set messageReach: workspace (D-166), within the same discovery and authorization boundaries. Today's source candidate reaches only the sender's checkout. Cross-Workspace delivery and discovery are denied; cross-owner work uses parent-mediated delegation. Unknown and inaccessible targets look the same in lookups, errors, counters and cursors. Any other exception to this denial needs a new decision, never an incidental tool grant. Revocation fences later reads, deliveries and exports but cannot erase delivered context.
  • Peer content is untrusted data at the recipient. It never changes tool grants, confirmation policy, Connection choice or authority. Size, rate, cost and wake limits apply before persistence. Held, archived or terminal recipients accept only exact operator recovery messages.

Swarm live control projections

Swarm decides from projections (queue, capacity, budget, Session, Routine, provider, placement, health) built from Node's post-commit authoritative records, never from a best-effort log stream. Processor partitions and checkpoints belong to operations.

Node committed events/outbox
        -> SwarmObservationProcessor batch
        -> projection rows + checkpoint in one transaction
        -> low-latency in-process wake notification
        -> normal Swarm decision
        -> registered operation + event/receipt
  • After a restart or lag Swarm catches up before claiming a current view. A partition behind its freshness objective makes affected admission blocked or degraded; Swarm never guesses from wall-clock time or a missing heartbeat.
  • One processor subscription covers all Projects, partitioned by a stable owner hash; an exact lease stops two consumers checkpointing one partition.
  • Decisions are loop-safe: each records its input watermark and an idempotency key from kind, target, revision and trigger, so its own output event cannot trigger it again at the same revision.
  • Diagnostic records never trigger placement, delegation, cancellation, budget or provider fallback.
  • Coordinators never see this multi-Project view; each keeps its own discovery ceiling.

Routine model

A Routine is one trigger plus one or more steps, not a workflow language. One step is an Agent, Skills and an instruction, as below; several steps chain with after. "Workflow" is only a product word for a Routine with several steps, never a runtime or CLI root. Steps, owners and event triggers are planned (S49–S52).

{
  "schemaVersion": "maia.routine.v1",
  "id": "morning-triage",
  "enabled": true,
  "trigger": {
    "kind": "cron",
    "expression": "0 8 * * 1-5",
    "timezone": "Europe/Paris",
    "catchUp": "latest-only"
  },
  "target": {
    "mode": "wake",
    "agentRef": "project:self/coordinator"
  },
  "instruction": "Triage newly admitted work and delegate only actionable items.",
  "requestedSkillRefs": ["project:self/issue-triage"],
  "limits": {
    "timeoutMs": 900000,
    "maxPendingTicks": 1
  }
}
scheduled -> admitted -> dispatching -> session-linked -> completed
          -> skipped|coalesced|blocked|failed|cancelled
dispatching -> outcome-unknown -> reconciling -> session-linked|blocked|failed
  • wake needs a canonical home Session; start binds the exact admitted Agent definition digest. Requested Skills are eligibility hints only.
  • A tick is unique on (routineRef, routineRevision, scheduledAt), reuses its command identity on retry and uses the snapshot that planned it. A manual trigger creates a distinct triggerKind: "manual" tick.
  • Cron is the five-field POSIX form with a named IANA timezone (tzdb version recorded). A nonexistent local time is skipped and recorded; a repeated one yields one tick unless policy says both.
  • Catch-up is none, latest-only (default) or bounded all; maxPendingTicks defaults to 1. Optional jitter is 0 to 300,000 ms, derived from the tick digest.
  • Disable stops planning but lets a running tick finish; cancel is separate. Retries default to zero, at most five, backoff capped at one hour.

Steps, owners and triggers (planned)

  • Owner and definitions. A Routine belongs to one exact Project or, after R3, Workspace. An Agent's Routines are those whose steps target it. There is no global or all owner. Agent and Routine definitions are Node-admitted snapshots: created through the CLI, API or web, or proposed by a Project's .maia and admitted by digest like the Project inventory. Only a person confirms creating, revising, enabling or disabling an Agent, a Routine or an event source; agents and coordinators get role_denied (S1, S18a).
  • Steps. A single-step Routine keeps the top-level target and instruction. A multi-step Routine replaces them with steps: a bounded, acyclic list (the protocol entry freezes the bound). Each step has a key, a start target for an exact Agent definition (until S49, S30's inline Session template, whose digest the tick snapshot freezes), requested Skills, optional harness, model and effort overrides checked against the Project's grants, an instruction, an optional goal (S69) and after. Swarm records each run as one Run ledger and starts one Session per ready step; steps never wake a home Session, so they never coalesce. A person step (S50) targets an exact person, or a role after ORG1, instead of an Agent and starts no Session: it records a request in the attention inbox (R8), settles done on that person's answer or review, settles failed at its expiry unless its escalation person answers first, and never takes an agent's answer. An answer only passes text to later steps; approving a consequential operation stays that operation's own preview and confirmation, bound to its exact target. The Node settles each agent step's task from its Session's terminal turn outcome; every Routine run, a single-step one included, is a run ledger entry (D-119). A step's retries follow the Routine retry rule, a per-step retry budget that replaces S19's three-dispatch stop for Routine runs (D-119); once they are spent, the step fails and starts none of its dependents. The run settles once, with evidence, after its last task settles.
  • No routing language. Steps have no conditions, loops, variables or data passing. A step's Agent decides through its Skills and may add follow-up ledger tasks to its run, within a per-run task cap that the protocol entry freezes and that counts every step and follow-up task.
  • Triggers. cron (above); manual from the CLI, API or web; and event from an event source, matched on a declared event type and exact field filters. Node-event triggers wait for a causal-depth guard, so a run's own events never retrigger its Routine. An event tick is unique on (routineRef, routineRevision, sourceId, sourceEventId). Each distinct event gets its own tick, bounded by the source's rate cap and the Routine's pending-tick cap; overflow is recorded as blocked, never queued silently. Catch-up applies to cron only; an event that arrives during a hold is recorded as skipped.
  • Gates (D-174). A cron trigger may name a gate, a command a person sets in the Routine's definition, and one exact admitted Agent definition it runs as. After S47b the Node runs it in a sandbox at that Agent's placement, with that Agent's grants and network policy, for at most 60 seconds and 64 KiB of output. Exit 0 with no output records the tick as skipped with reason gate_empty and starts no Session; exit 0 with output runs the Routine, and the steps without after read that output the way they read event data: size-bounded, masked and marked untrusted. Any other exit or a timeout records failed with a typed reason. A gated cron may fire every minute; empty ticks are counted per Routine and hour, not kept one by one. The gate decides only whether the trigger fires; steps still have no conditions.
  • Event data. The Node stores each accepted event by ID. A step reads it only through a run-scoped read that returns the source's schema-declared fields, size-bounded and marked untrusted. Event text is data: it never changes the Routine, its Agents, policy, approvals or credentials.
  • Event sources. Third parties extend Maia by sending events, not by loading code. An event source is a Node record with an owner scope, declared event types with schemas, size and rate caps, and its own ingest-only credential: stored as a digest, shown once, rotated or revoked through preview and confirmation. Events enter through one ingest operation in the SDK and CLI, never a model tool. The Node checks the credential, schema and caps, deduplicates by source event ID within a retention window and records each event. A source grants no authority: only the Routine's confirmed Agents and policy run.
  • Connectors. A connector is an out-of-process adapter, such as a GitHub App. The provider calls a public URL on the connector, never the Node. The connector verifies the signature (GitHub's HMAC), uses the provider's delivery ID (GitHub's X-GitHub-Delivery) as the source event ID, normalizes the event and calls ingest with the source credential. The dedupe window covers the provider's redelivery window. It runs beside the Node on the same host or reaches it over an authenticated tailnet hop (after D-056); the SSH bridge opens per invocation and suits only tests. Each connector is a new service and needs owner approval.

Session goals (planned)

S69 lets a Session, Routine step or task keep working until a check passes. It extends the R11 AG1 session.goal.* contract in the CLI reference with two optional fields, check and budget, rather than defining a second contract; a goal loops only when it carries both. Those operations are not registered today; Maia agent's standalone /goal is local to that agent. A Session goal is a completion condition: it never closes an S48 Project goal.

  • Who changes a goal. A goal without a check stays an R11 AG1 self-goal, which the model may manage as that contract allows. Once a person sets a check or budget, only a person rewrites, updates, pauses, resumes or abandons the goal or raises its budget, by preview and confirmation on a Routine step or task; the model's rewrite, update, done and abandon get role_denied, and it may only read the goal and propose a change. Only a passing check or a person records met; only an evaluator or a person records impossible. Standing approvals (S67) never change a goal, and event data never sets one.
  • Check. A command check is a person-set exact argv (stored as a digest) and working directory. Only a recorded command item that matches it, from the turn that just settled and after that turn's last file change, counts, never model text; a command check works only where the adapter records command items (Codex and Claude Code; Pi streams turn boundaries only). A rubric check is an R10a evaluation over bounded observed items, masked as S65 specifies, never judged by the model doing the work. A worker's session.settle done (S29) on a goal-bearing Session is a claim, not the check.
  • Outcomes. met settles the Session as done and impossible as abandoned. At the budget the goal becomes budget_limited: the Session settles as needs_review (S29), a Routine step counts as failed, and no resume, auto-resume (S27) or move (S66) sends a continuation or sets a native goal. After a turn that failed, was interrupted or has an unknown outcome, the goal becomes paused with a typed reason until a person resumes it.
  • Budget. Required: a Node hard turn cap, which a Workspace ceiling (E5) can lower, plus at least one measured limit (tokens, wall time, or money where the Connection reports usage) through BudgetReservationV1's session scope (S55). Rubric evaluation counts against it.
  • Loop. After a turn settles as completed and the check fails, the Node sends one continuation: an idempotent, receipted turn.send that names the goal and carries the verdict reason as bounded, untrusted data. A Node restart sends it once. No continuation is sent while a question or approval (S4b) is pending. This is stricter than D-023, which allows fallback after a verified pre-dispatch or no-output failure. A person's own turn, steer or interrupt pauses the goal and cancels a queued continuation (S26 turn.cancel). A continuation is a new turn, not a fallback; the S69 decision entry records the Node as its sender.
  • Native goals. Claude Code's /goal is a session-scoped Stop hook, unavailable when hooks are disabled; Maia's launch sets settingSources: [] and registers only its PreToolUse guard, so whether /goal works there is unproven. Codex exposes thread/goal/set, get and clear on the app-server; third-party sources report a goals feature flag and a model tool that marks a goal complete, which the official page does not show. Where a pinned version proves its native goal under Maia's launch, the adapter may set it to the same objective so the harness keeps working inside one turn with its own capabilities. Its verdict is advisory: Maia runs its own check before recording met, and interrupts the native loop at the budget within a bound the protocol entry freezes. The adapter clears the native goal whenever Maia's goal leaves active and before any resume, fallback (S60) or move (S66), which carry only Maia's record. Until that proof holds, the adapter does not set a native goal itself; worker launches leave native goals on, and a task's harness may use them (D-123).
  • Defaults. Goals are opt-in per step or task. Templates (S63) give steps a check and a budget, and a Workspace, Project, Agent or step policy (E5) can require or forbid goals. A step without a goal runs its turn once.

Admission, queueing, and capacity

Scheduling is bounded weighted round robin across active Projects. Inside a Project the order is operator-confirmed recovery, direct user or Maia message, due Routine tick, peer child start or wake, then evaluation or maintenance.

Priority never bypasses budgets or authority. Admission reserves worst-case tokens, cost, concurrency slot, deadline and executor capacity in the same transaction as queueing. Completion releases the exact reservation; an unknown outcome keeps it until reconciliation. Overload returns a typed blocked result naming the limiting resource, never silent queue growth. Fairness also covers per-Connection caps, per-actor and per-Session wake-rate caps and starvation boosting with a ceiling.

Deterministic scheduler algorithm

Each active Project has a weight 1..16, a persisted deficit, a last served sequence and one FIFO queue per priority class. One dequeue transaction:

  1. drops entries of inactive Projects and releases their reservations;
  2. promotes an entry by at most one class after 60 seconds, never above recovery;
  3. visits Projects by ascending (lastServedSequence, projectId), adding weight to each deficit (cap 64);
  4. takes the highest-priority head whose reservation still validates and whose unit cost 1 fits the deficit;
  5. subtracts one, advances the served sequence, creates the assignment intent and marks the entry dispatched in one SQLite transaction;
  6. returns the entry once, with its original sequence, if assignment fails before an external call; blocks it with its reservation if the outcome is uncertain.

Deficits survive restart. A continuously eligible Project is served within (active Projects × per-Project quantum) turns unless its own budget is exhausted. Held, draining and archived Projects are never visited.

Budget reservation contract

interface BudgetReservationV1 {
  readonly reservationId: string;
  readonly projectId: string;
  readonly sessionId: string;
  readonly activationId: string;
  readonly connectionId: string;
  readonly scopes: ReadonlyArray<
    "user" | "project" | "agent" | "session" | "connection"
  >;
  readonly maxInputTokens: number;
  readonly maxOutputTokens: number;
  readonly maxWallTimeMs: number;
  readonly maxCostMicros: number;
  readonly currency: "USD" | "EUR";
  readonly priceCardDigest: `sha256:${string}`;
  readonly concurrencySlots: number;
  readonly createdAt: string;
  readonly expiresAt: string;
  readonly state:
    "reserved" | "partially-settled" | "settled" | "released" | "unknown";
  readonly revision: number;
}

A reservation checks every scope atomically. Usage is normalized to input, output, cached and reasoning tokens and cost micros against the immutable price card and settles monotonically. Missing final usage keeps the worst-case amount until reconciliation or a 24-hour operator-visible expiry; it is never silently refunded. A cost overrun blocks further activations; it never truncates a completed response.

Placement and multi-Node scale

Execution Nodes add worker capacity to the one Control Node through MN0–MN5 (roadmap). The Control Node's SQLite store is the only durable writer and there is no HA in v1 (operations). Execution Nodes use the execution-only composition: no Swarm store, no remote SQLite writes, no PlacementPlanner, no product clients. They accept fenced assignments (Node assignment and fencing) and send bounded authenticated observations. Team invitations, voice and multi-Control UI are not capacity prerequisites.

  • Registration. NodeRegistrationV1 carries Node and device identity, certificate fingerprint, registration incarnation, OS and architecture, worker and protocol versions, a possibly empty inventory of prepared environments with exact workspace_* identity evidence, execution-profile digests, CPU and memory capacity, load, heartbeat sequence and expiry. A Node with no prepared environment starts no worker. Execution Environments keep their workspace_* identity through the naming migration.
  • Heartbeats. Every 5 seconds; four misses stop placement; once the assignment lease expires the Control Node fences the epoch. Clock decisions use Control Node time.
  • Assignment and handoff. An assignment binds activation, Node, worker or executor, revision, lease epoch, capability evidence digest and expiry; stale epochs are rejected. A handoff increments the assignment epoch before another Node may write. Handoff is planned -> draining -> fenced -> preparing -> assigned -> completed (failed and outcome-unknown need reconciliation); the destination starts only when environment provenance and capability evidence match the plan.
  • Environments and profiles. Placement needs an exact admitted working directory or an explicit prepare operation; files are never assumed replicated, and preparation grants the worker no writes. The first remote profile is managed-read-only; remote coding, builds and tests wait for a named per-OS containment adapter, and the unsandboxed operator profile is never eligible.
  • Partial outage. Projections turn stale with observedAt, placement stops, ownership holds until lease reconciliation, and a missing heartbeat never reports work as completed or cancelled. Cross-Node views keep each source (streamOwnerId, eventSequence); there is no global event order.

Staged targets become release gates once the benchmark machine, model fakes, workload mix and baseline are committed:

StageActive SessionsConcurrent activationsProjectsObservation burst/sLive subscribersGate
local10025105,00032no duplicate effect under restart
node250755020,000128bounded p95 queue/event/fan-out latency
swarm50015010050,000512recovery, fairness, replay, and processor SLOs pass

Bursts last 60 seconds of mixed records and prove that viewers do not raise worker emissions, slow consumers are isolated, checkpoints catch up and no message or effect is lost. Load above 70% of the qualified envelope for 15 minutes signals moving optional projection processors out of process, never dropping evidence.

External executor Sessions

The v1 vendor executors (Codex CLI, Claude Code) are specified in external executors. Every external executor, including any later hosted one such as Cursor Cloud (not on the roadmap), follows these rules:

  • Swarm decides admission; Node persists the normalized Session; the adapter owns vendor calls and reconciliation evidence. Guarantees are checked against fresh capability evidence before dispatch.
  • Starting one is a consequential third-party effect bound to an exact repository and ref, environment, model, network and MCP policy, secret references, data and artifact policy and deadline. Remote states (busy, rate-limited, stale, outcome-unknown) stay explicit, and Maia never claims its own tools, sandbox or confirmations run inside the vendor.
  • Request IDs, idempotency keys, cursors, rate-limit state and normalized events are persisted before projection; callbacks authenticate and reject replay. One fenced reconciler polls with Retry-After, backs off from one second to five minutes and opens a circuit after five transport failures. Uncertainty never causes a duplicate create or send.

Voice cockpit orchestration

Text is the default input; voice is optional input and output for the exact attached Session, never a harness mode or extra authority (decision). A coordinator reasons and delegates only when addressed; direct worker or ordinary Session talk never passes through one. Clients own presentation, Providers own realtime transport and the confirmation ceremony, and R7 with PF-18/DF-18 requires text and voice parity.

text composer ------------------------> canonical Session/control operations
voice input -> realtime/tool boundary -> same exact target and command identity
                                               |
                            explicit coordinator OR worker/ordinary Session
  • A selected visual node is a hint until an exact input is resolved; global routing never widens a direct Session request.
  • The voice model gets only ask, status, preview and confirm. confirm asks the authenticated client to show a confirmation; only the client calls confirmation.consume. The one-time confirmation binds actor, device, voice Session, turn, operation and input digest, targets, revisions and expiry, and is voided by reconnect, transcript correction, drift or identity doubt.
  • Context is the current utterance plus a redacted rolling summary for the target, never unbounded traces or credentials.
  • Without voice, text and CLI controls remain; an unavailable coordinator blocks only requests to it.

A2A boundary

A2A v1 is the external boundary for discovery, messages, tasks, artifacts and updates; MCP stays a tool and context protocol (decision). Node authenticates and normalizes ingress (a2a_peers, a2a_tasks in the Node data model); Swarm admits each request to one exact Project. Each accepted task maps to one internal A2A task record and one Agent Session activation, keeping external and internal task, Session and activation identities. Peers never receive worker leases, Connection secrets or mutation authority. A2A ingress follows S17 on the path to an organization's agent hub.

A2A v1 implementation profile

  • Supported: the published A2A v1 HTTP profile (Agent Card discovery, task create, get and cancel, message parts, artifact references, task updates). The exact version lives in the protocol capability manifest and fixtures; unsupported extensions fail explicitly.
  • Peer enrollment records the normalized Agent Card URL and digest, TLS identity, key fingerprint, allowed Project, capabilities, callback origins, content, size and rate limits, expiry and revision. Discovery is HTTPS only, resolves DNS before and after connect, rejects private, link-local and loopback addresses and cross-origin redirects, caps responses at 256 KiB and never treats card content as instructions.
  • Each inbound request authenticates the peer, checks nonce and a five-minute TTL, deduplicates (peerId, externalTaskId, requestDigest), maps the grant to one actor and Project, and stores raw identity apart from normalized content. Retries return the same a2aTaskId.
A2A stateMaia projection
submittedadmitted or blocked before activation
workingqueued/starting/running activation
input-requiredwaiting interaction
completedterminal succeeded plus exported artifacts
failedterminal failed with redacted typed diagnostic
canceledcancellation observed, not merely requested
  • Updates and callbacks use durable cursors, acknowledgements, five retries and dead-letter. Cancellation is a receipted Session operation.
  • Artifact imports never fetch a peer URL without SSRF and digest checks.
  • A disconnected peer does not mean task failure; reconciliation stays operator-visible.

Acceptance gates

  • Concurrent starts never duplicate home Sessions or child relations; limits and budget reservations are atomic and survive restart.
  • Session discovery and messaging stay within the Project by default; a sibling Project becomes reachable only when both guards set messageReach: workspace (D-166), which grants no access to its history, memory or credentials. A Workspace cannot discover or message sibling Workspaces until a later approved tier; human grants never widen coordinator identity.
  • Owner-scoped history, memory, tools, evaluation caches and resources pass the DF-22 denial matrix; a shared kernel or separate processes alone prove nothing.
  • Routine ticks are unique across retries, DST and restart; fairness holds under one noisy Project.
  • Scheduling uses only post-commit observations, catches up from checkpoints and never repeats a decision from its own output.
  • Stale Node or worker epochs cannot append events or receipts, and external uncertainty never duplicates a create or send.
  • Parent-mediated delivery and held-Session recovery require exact separate admission, disclosure rights and current revisions.
  • Hostile peer or A2A content cannot change grants, confirmation policy, Connection choice or authority; peer delegation passes prompt-injection, cycle and quota tests.
  • Speech cannot consume a confirmation; replay and correction fail closed.
  • No Swarm operation bypasses the registry, authorization, CAS, idempotency, events or receipts, and load tests meet the committed staged thresholds.

Imported from Maia OS docs at 3dc3e07