ADR-023: Multi-Agent Team Durability — Checkpoint, Interrupt, Per-Member Streaming¶
Metadata¶
- Status: Accepted
- Date: 2026-07-29 (accepted 2026-08-01)
- Decision Makers: Vijaykumar Singh
- Related ADRs: 003 (workflow engine / StateGraph — the primitives this propagates), 021
(terminal-native HITL — where team interrupts surface) - Work tracked by: TD-25
- Benchmark: competitive-benchmark-2026-07.md §4
Context¶
Victor's multi-agent model is a genuine strength: UnifiedTeamCoordinator is used directly as a
StateGraph node (teams are formations — SEQUENTIAL/PARALLEL/HIERARCHICAL/PIPELINE — not a separate
graph abstraction), with WorkspaceIsolation per member. This is arguably cleaner than CrewAI's
role model or AutoGen's conversation model, and CLAUDE.md deliberately forbids a wrapper node type.
The StateGraph engine (ADR-003) already has checkpointing and HITL-interrupt primitives. The gap
is that a team node does not propagate them to its members' execution:
- No team-run checkpoint/resume. A long multi-member run cannot be durably checkpointed and
replayed at member granularity; a crash mid-formation loses in-flight member state. LangGraph — the
category bar — makes checkpoint + time-travel replay first-class. - HITL mid-run is web-only. A member hitting an approval gate defers to Chainlit (see ADR-021);
there is no terminal-native interrupt/resume for a paused team member. - Per-member streaming is incomplete. The user sees aggregate progress, not each member's live
stream — hard to follow a HIERARCHICAL or PARALLEL formation.
Decision¶
Give team nodes a durability contract that propagates the StateGraph primitives to member
execution, without introducing a new multi-agent graph abstraction:
- Member-granular checkpoint/resume.
UnifiedTeamCoordinatorcheckpoints per-member state to
the StateGraph checkpointer so a formation resumes at the last completed member (or mid-member
step where the member itself checkpoints), not from the top. - Interruptible members. A member's approval/
ASKgate raises a StateGraphinterrupt, so the
team run pauses durably and resumes on approval — surfaced terminal-natively via ADR-021 (not
browser-bound). - Per-member streaming. Each member's
RenderActionstream is tagged with member identity and
fanned out so the TUI (ADR-020) can show per-member progress lanes.
This is a contract on the team node — the public shape of how a team participates in a graph —
so it is FEP-gated (below).
Rationale¶
- First principles. Durability is the property that separates a demo multi-agent run from a
production one: it must survive a crash, pause for a human, and be observable per participant.
LangGraph set that bar; formations without it are behind regardless of how clean the topology model
is. - Reuse, not reinvent. The checkpointer and
interruptalready exist at the graph layer; this
ADR threads them through the team node rather than building a parallel mechanism — honoring the
"teams are formations, not graphs" rule. - Co-design. Depends on ADR-021 for the terminal interrupt surface and ADR-020 for per-member
streaming lanes; the three specify one coherent multi-agent UX.
Consequences¶
- Positive: crash-safe, pausable, observable team runs; parity with LangGraph on the durability
contract while keeping the cleaner formation model. - Negative: checkpoint volume grows (member-granular snapshots); the team-node contract change is
a public surface ⇒ FEP + migration for existing team definitions. - Neutral: single-agent runs and the formation taxonomy are unchanged.
Implementation¶
- Companion FEP — the team-node durability contract (checkpoint identity, interrupt semantics,
member-tagged streaming) is avictor.frameworkpublic surface: ratified in
FEP-0028. Increments:
1. member-granular checkpoint/resume via the existing StateGraph checkpointer — done across all six
formations: SEQUENTIAL and PIPELINE (per-member), PARALLEL (lock-protected concurrent
completed-set), HIERARCHICAL (phase-granular — plan/specialists/synthesis in
shared_state["__hier__"]; the specialist wave runs through the shared concurrent runner, so
mid-wave cumulative checkpoints give per-specialist partial resume — a mid-wave crash re-runs only
the unfinished specialists under the restored plan, no replan), and CONSENSUS/REFLECTION
(round/iteration-granular — the loop state inshared_state["__consensus__"]/["__reflection__"],
resume continues at the next unfinished round/iteration). Iterative mid-loop partial resume
deferred;
2. memberinterrupt— slice 2a (terminal-native member approval) done: a member's policy
ASK-gated tool surfaces to the shared terminal approval modal (ADR-021), tagged withmember_id,
via a member-tagging wrapper published on aContextVarduring member-orchestrator construction
(read by the policy-engine builder). Slice 2b-infra (durable pause/resume mechanism) done:
a member reportingmetadata["awaiting_approval"]stops the SEQUENTIAL formation (opt-in
pause_hook), persists a pause checkpoint (paused member re-runs on resume), and returns a paused
aggregate; resume re-runs the paused member via the pillar-1 checkpoint path. The real mid-member
ASK→pause trigger is now done: a durable team run armscurrent_member_durable_pause_enabled,
a member'sASKraisesMemberApprovalPause(aBaseExceptionthat survives the runtime-core
except Exceptionpath), caught atSubAgent.executeand surfaced as the awaiting result.
Concurrent durable pause is now done for PARALLEL and HIERARCHICAL: a wave collects every
awaiting member into a multi-pause aggregate (__awaiting_approvals__+ one batch pause
checkpoint) and a resumed run re-runs exactly the paused set; HIERARCHICAL additionally pauses on
an awaiting supervisor plan or synthesis via the singular__awaiting_approval__aggregate
(the paused phase is not snapshotted, so resume re-executes exactly it — all three phases handle
an awaiting result, makingsupports_durable_pause()safe to arm). The no-graph chat continuation
(non-team single agent) is deferred;
3. member-taggedRenderActionfan-out for ADR-020's per-member lanes — done across all six
formations (FEP-0028 increment 4): aMemberEventSink/ContextVarteams→stream bridge in
stream_with_eventsemitsmember_start/member_completed/member_error/
member_awaiting_approvallanes; SEQUENTIAL/PIPELINE and the concurrent formations route
through sharedBaseFormationStrategyhelpers, CONSENSUS/REFLECTION emit via the same
member_event_hook. Member tool/token streaming is a follow-up.
FEP-0028 was Accepted on 2026-08-01 with the contract fully landed (PRs #733–#752); this ADR's
status advanced with it and TD-25 is closed. Remaining deferred items are recorded in the FEP as
Non-Goals/Follow-ups: iterative-formation (CONSENSUS/REFLECTION) durable pause, iterative mid-loop
partial resume, member tool/token streaming, the project.db-backed checkpointer, and the non-team
single-agent chat continuation.
Alternatives Considered¶
- A dedicated multi-agent graph abstraction. Rejected — explicitly forbidden (CLAUDE.md; teams
are formations used directly as nodes). Durability must ride the existing StateGraph primitives. - Checkpoint only at formation boundaries (not per member). Rejected: loses in-flight member
work on crash; the whole point is member-granular resume. - Keep HITL web-only for teams. Rejected: same terminal-first argument as ADR-021.
References¶
- ADR-003, ADR-020,
ADR-021 victor/teams/unified_coordinator.py,victor/workflows/unified_compiler.py,
TD-10 (workspace isolation rename)
Revision History¶
| Date | Version | Changes | Author |
|---|---|---|---|
| 2026-07-29 | 1.0 | Initial ADR — team durability contract (checkpoint/interrupt/per-member stream) | Vijaykumar Singh |
| 2026-07-30 | 1.1 | Increment 1 (member checkpoint/resume) and increment 4 (per-member streaming lanes) landed for SEQUENTIAL via FEP-0028 | Vijaykumar Singh |
| 2026-07-30 | 1.2 | Increment 3 slice 2a (terminal-native member approval: member ASK → shared modal, member_id-tagged) landed; durable pause/resume (2b) deferred | Vijaykumar Singh |
| 2026-07-31 | 1.3 | Increment 3 slice 2b-infra (durable member pause checkpoint + resume re-run at the teams layer) landed for SEQUENTIAL; real ASK trigger + chat continuation deferred | Vijaykumar Singh |
| 2026-07-31 | 1.4 | Increment 3 real mid-member ASK→durable-pause trigger landed (MemberApprovalPause BaseException, armed for durable team runs); non-team chat continuation still deferred | Vijaykumar Singh |
| 2026-07-31 | 1.5 | Concurrent-formation per-member streaming lanes (PARALLEL/HIERARCHICAL via a shared helper) + durable-pause arming gated to supports_durable_pause() (fixes a latent #740 abort); concurrent checkpoint/pause still deferred | Vijaykumar Singh |
| 2026-07-31 | 1.6 | PIPELINE full durability (checkpoint/resume/pause/lanes) via the shared sequential machinery — it's a sequential formation; CONSENSUS/REFLECTION + concurrent checkpoint/pause remain deferred | Vijaykumar Singh |
| 2026-07-31 | 1.7 | CONSENSUS + REFLECTION streaming lanes landed — per-member lanes now cover all six formations; concurrent durable checkpoint/pause is the last deferred item | Vijaykumar Singh |
| 2026-07-31 | 1.8 | Concurrent durable checkpoint/resume for PARALLEL landed (lock-protected cumulative-completed-set checkpoint; execution stays concurrent) — resolves the FEP's concurrent-resume open question; concurrent pause + HIERARCHICAL checkpoint deferred | Vijaykumar Singh |
| 2026-07-31 | 1.9 | Concurrent durable pause/resume for PARALLEL landed — a wave collects every awaiting member into a multi-pause aggregate (__awaiting_approvals__ + one batch pause checkpoint); resume re-runs exactly the paused set; PARALLEL.supports_durable_pause() now True. HIERARCHICAL concurrent pause + non-team chat continuation deferred |
Vijaykumar Singh |
| 2026-08-01 | 1.10 | HIERARCHICAL phase-granular checkpoint/resume landed — plan/specialists/synthesis each snapshotted into shared_state["__hier__"] (persisted by the existing member hook; pure formation-layer change); resume restores up to the last completed phase (completed plan restored not re-run, drives phase 2), fallback path ends at phase 2. Mid-wave partial resume + HIERARCHICAL pause deferred |
Vijaykumar Singh |
| 2026-08-01 | 1.11 | CONSENSUS + REFLECTION round/iteration-granular checkpoint/resume landed — loop state snapshotted into shared_state["__consensus__"]/["__reflection__"] after each round/iteration (pure formation-layer change); resume continues at the next unfinished round/iteration, completed ones restored. Checkpoint/resume now covers all six formations. Iterative-formation durable pause + mid-loop partial resume + non-team chat continuation deferred |
Vijaykumar Singh |
| 2026-08-01 | 1.12 | HIERARCHICAL durable pause + per-specialist partial resume landed — the specialist wave now runs through the shared concurrent runner (_execute_members_concurrently extended with additive tasks/indices/resume_override params; PARALLEL defaults unchanged), giving mid-wave cumulative checkpoints (crash mid-wave re-runs only unfinished specialists under the restored plan — no replan) and the multi-pause aggregate for awaiting specialists; supervisor plan/synthesis pause via the singular aggregate and re-run on resume; HIERARCHICAL.supports_durable_pause() now True (all three phases handle an awaiting result). Non-team chat continuation deferred |
Vijaykumar Singh |
| 2026-08-01 | 1.13 | Status Proposed → Accepted. FEP-0028 accepted with the contract fully landed (PRs #733–#752): checkpoint/resume + streaming lanes across all six formations (member / member-concurrent / phase+per-specialist / round / iteration granularity), durable pause for SEQUENTIAL/PIPELINE/PARALLEL/HIERARCHICAL. TD-25 closed. Deferred remainder recorded as FEP Non-Goals/Follow-ups (iterative-formation pause, iterative mid-loop partial resume, tool/token member streaming, project.db checkpointer, non-team chat continuation) | Vijaykumar Singh |