Skip to content

feat(agent-org): add durable pause and resume handoff - #881

Draft
ShiboSheng wants to merge 3 commits into
codex/issue-759-pr4-task-fsmfrom
codex/issue-760-pr5-pause-resume
Draft

feat(agent-org): add durable pause and resume handoff#881
ShiboSheng wants to merge 3 commits into
codex/issue-759-pr4-task-fsmfrom
codex/issue-760-pr5-pause-resume

Conversation

@ShiboSheng

@ShiboSheng ShiboSheng commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Problem

Fixes #760.

PR4 provides formal Task state and ownership, but it does not provide a durable Pause/Resume boundary for in-flight Agent Org work. Without that boundary, a late Turn can cross lifecycle generations, consume work after Pause, clear a replacement runtime, or resume work that is no longer legal.

Real Provider acceptance exposed two additional lifecycle gaps in the initial PR5 implementation:

  • A shell moved to the background was owned by an independent monitor that no longer observed the paused Turn. The Provider Turn stopped and the runtime appeared released, but the parent shell, child process, and process group could remain alive.
  • After restart and Resume, the continuation could leave the original Inbox assignment unread or let the Task and Turn finish before their exact background shell or subagent result was consumed. That produced a false terminal state: the UI could look complete while formal work was still unresolved.

Solution

Add one durable, generation-fenced Pause/Resume protocol for Agent Org formal work:

  • Commit the Paused fence, generation bump, Pause episode, and captured Coordinator/TaskExecution handoff receipts in one immediate SQLite transaction.
  • Give each runtime a lease and bind handoff to the exact Session, Turn intent, runtime lease, and dialog generation. A late callback or old lease cannot release a replacement runtime.
  • Carry that same owner identity into foreground/background shells and subagents using the existing in-memory job registry. Pause cancels only the exact Turn owner, terminates the whole Unix process group, waits for output shutdown and process reaping, and records released only after the owned work is gone.
  • Preserve ordinary SDE behavior: Stop remains Session-wide, Force Send stops foreground work without killing intentionally backgrounded jobs, and Queue does not cancel jobs.
  • Consume a Pause episode once on Resume, re-check Task ownership/status/materialization, and create at most one continuation through the existing Member FIFO. Cancelled, terminal, reassigned, or owner-mismatched work is skipped.
  • Allow the exact Resume continuation to reclaim its already-materialized pre-Pause Inbox assignment only when the durable episode, handoff, continuation, Task, Member, generation, and Session evidence all match. The assignment is acknowledged only after Task success.
  • Fence Task completion and final assistant output until every exact Turn-owned shell/subagent result has been delivered back into and consumed by that same Turn. If convergence does not happen within the bounded correction/teardown budget, cancel the exact jobs and fail closed instead of creating a late wake or false success.
  • Fence Inbox materialization/acknowledgement, Provider admission, Task mutation, and Group Chat submission against lifecycle generation; remove the old automatic/synthetic resume path.
  • Persist restart-safe continuation evidence and reconcile it before generic in-flight cleanup.
  • Expose Pause/Resume outcomes and a read-only draining phase through Tauri, update Overview/Group Chat behavior, keep Paused/Idle Teams free of fallback polling, and provide all 13 locale strings.
  • Keep task_update role-aware for strict Providers while preserving the typed parser as the mutation authority.

The rollout gate remains off. Archive/Delete, UserDirectedWork, interventions, multi-Member mentions, memory jobs, Goal Loop work, and later delivery-stack features are unchanged.

Potential risks

  • Database compatibility: the canonical runtime manifest grows from 15 to 17 tables with no PR4-to-PR5 migration. PR4 and PR5 binaries must use their matching database or a fresh isolated ORGII_HOME. Rollback means reverting the PR5 commits and reopening the database paired with that code; an older binary must not guess a downgrade of a PR5 database.
  • Concurrency and failure behavior: Pause teardown and Resume dispatch are asynchronous. Exact owner identities, durable receipts, unique constraints, generation checks, and runtime leases make retries idempotent. A process that ignores SIGTERM is escalated to SIGKILL. If process/job finality still cannot be confirmed within the bound, the Turn fails closed and the handoff is not reported as successfully released.
  • Platform coverage: whole-process-group termination and three-round real acceptance were verified on macOS. Unix process-group behavior is shared with Linux, but no equivalent real-provider acceptance was run on Windows in this handoff.
  • Restart: continuation dispatch is intentionally at-most-once by durable receipt. Startup reconciliation and duplicate-request tests cover the identified crash windows, but this remains a new persistence/recovery path and should stay behind the rollout gate while the stacked series is under review.
  • Stack dependency: this PR is based on PR feat(agent-org): enforce the formal Task FSM and owner permissions #869 at 49ad34970b80530e0be07043cf8012e60e53110c and requires its formal Task FSM, owner authority, Turn context, Member FIFO, and strict-provider empty-placeholder behavior.
  • UI transport: Paused Teams do not poll. A missed push is repaired on WebSocket reconnect or visibility restoration; there is deliberately no Paused interval fallback.
  • Review size: the final PR5 diff is 97 files and 10,280 added/deleted review lines. The increase comes from runtime identity propagation, shell/subagent ownership and convergence, 13 locale files, and deterministic/rendered/real-provider test coverage. It remains below the approved 15,500-line P90 but is larger than the original file-count control line.
  • Known unrelated check blockers: the all-feature Clippy combination still fails in untouched bar_native.rs because a feature-gated toggle_panel helper is absent, and the repository circular-dependency command cannot resolve two existing React artifact ?raw imports. The changed/default-feature Rust path and staged import graph pass.

Scope and invariants

  • Team lifecycle, Task status, Turn-intent status, runtime ownership, job finality, and drain status remain separate concepts.
  • Runtime leases remain inside the general Session runtime owner; Agent Org episode/continuation policy remains in coordination/lifecycle; shell/subagent ownership extends the existing job registry rather than adding another global registry.
  • Pause/Resume does not create, delete, reassign, complete, fail, or cancel a Task. It only fences and continues still-legal formal work.
  • A Task cannot become terminal while its exact Turn-owned background work is running or has an unconsumed terminal result.
  • UserDirectedWork remains excluded from PR5 handoff selection.
  • Ordinary SDE Send, Queue, Force Send, Stop, Resume, Compaction, and intentional background-job behavior retain their existing contracts.

Verification

Automated checks on the final production diff:

  • cargo test -p agent_core — 3,197 passed, 0 failed, 2 ignored; doc tests completed with 11 ignored.
  • Focused ownership/convergence tests — foreground and background shell cancellation, parent/child/process-group exit, cancellation before/during/after background registration, natural-exit/Pause races, SIGKILL escalation, old owner/lease/callback isolation, exact Inbox reclaim/acknowledgement, same-Turn shell/subagent result consumption, bounded fail-closed teardown, and Provider-error teardown all passed.
  • cargo check -p agent_core and cargo check -p e2e-test — passed.
  • cargo fmt --all -- --check — passed.
  • cargo clippy -p agent_core --all-targets -- -D warnings — passed; the commit hook also passed scoped agent_core Clippy.
  • cargo clippy -p agent_core --all-targets --all-features -- -D warnings — blocked by the pre-existing untouched bar_native.rs feature-combination error described above.
  • pnpm test — 1,116 files and 8,768 tests passed.
  • pnpm typecheck — passed.
  • pnpm lint — exited 0 with five pre-existing warnings in untouched WorkItems components.
  • pnpm check:circular — blocked before graph analysis by the two existing React artifact import-resolution failures described above; commit statistics reported zero circular dependencies in changed files.
  • Changed E2E scripts passed node --check and Prettier; git diff --check passed.
  • Commit hooks and commitlint passed. Final head is ecaa1c721cbb0f4ec60c0136b85c53531421f1ec.

Rendered and packaged-app checks:

  • WEBDRIVER=1 pnpm run tauri:build:fast passed with the frontend compile-time gate, Cargo webdriver feature, and Rust runtime gate enabled. The binary SHA-256 is 967fee3656cd0426baff4da073def8079f9a27320e454be210c0ee1a38c9b0fa; the app file-tree manifest SHA-256 is 2ccd66ba9a8df02955610cd049bdd3cb4d7f04ca6d56fee727402f680ca166b4.
  • The frozen binary was built from the final PR5 production source. The commit was created after acceptance; only the live E2E script was corrected after the binary build, with no later production-code change.
  • The primary rendered PR5 Pause/Resume scenario passed using real controls. A ten-runtime Pause fence completed in 73 ms (budget 250 ms), and nine parent/child process groups drained in 3,042 ms (budget 10 seconds).
  • The background-subagent next-message regression in session-controls-ui passed. Ordinary Stop/Queue/Force Send ownership boundaries also passed at their deterministic Rust owner tests.
  • One historical paused-send rendered case remains blocked because its existing fixture looks for Pause inside a collapsed Overview panel, and additional session-controls-ui cases are blocked by existing prompt-duplication/stopping-state harness assertions. These are recorded as test-infrastructure failures, not product passes.

Real Provider acceptance used one frozen BuildFast binary, fresh isolated homes, independent ports, real Pause/Resume UI controls, direct SQLite evidence, and direct macOS PID/process-group inspection:

Round Paused process group Parent/child PIDs Full drain Resume finality
1 42284 42284 / 42285 / 42286 2,125 ms one continuation; finality job PID 43388
2 47056 47056 / 47057 / 47058 2,173 ms one continuation; finality job PID 48163
3 50947 50947 / 50948 / 50949 2,167 ms one continuation; finality job PID 52166

All three rounds confirmed:

  • Pause removed the parent, child, and whole process group before runtime release; Provider requests and active Turns returned to zero with no late output.
  • A full app quit/restart preserved Paused state.
  • Resume created exactly one durable continuation and did not duplicate the Task or command.
  • A new background command immediately followed by an attempted Task completion kept the Run, Task, continuation, and Inbox nonterminal until the job result was consumed in the same Turn.
  • Final convergence produced one completed Task, one completed continuation, an idle Run, zero unresolved Inbox materializations, one final assistant message, and a stable quiet window with no late wake/output.
  • A final PR4 strict-provider Task start/complete smoke passed after the rebase.
  • Final process inspection found no residual acceptance shell, finality command, WebDriver, or frozen-app process.

Architecture and performance audit

Architecture review covered types, lifecycle state transitions, cancellation reasons, resource ownership, duplicate termination paths, cross-domain boundaries, naming, wire compatibility, initialization parity, and symmetric Session/Turn/lease/generation propagation. No PR6 lifecycle, public protocol, or new persistence format was added by the post-acceptance fixes.

Performance review covered active, idle, Paused, restart, and repeated lifecycle behavior. Exact-owner lookup uses a bounded in-memory index instead of global scans; completed jobs are removed immediately; teardown and correction retries are bounded; ordinary background-job wake/retention behavior is unchanged; and no new poller, timer loop, cache, or persistent worker was added.

UI evidence

The packaged app was exercised through real WebDriver/UI controls, then correlated with SQLite rows, runtime counters, replay output, and OS process inspection. No static screenshot is attached because the acceptance is a timed lifecycle/process transition rather than a stable visual state. The PR remains Draft for stacked review.

@ShiboSheng

Copy link
Copy Markdown
Collaborator Author

orgii://cloud/session/ref?v=1&org=bfa7b134-2486-45fa-81ad-a369441fafb4&owner=776dbd69-ac1d-4f72-a0d4-69cb4f2667dd&session=codexapp-rollout-2026-08-22T18-01-45-01a028eb-4fec-74b2-bbb7-528b8361c483

@ShiboSheng
ShiboSheng requested a review from Neonforge98 August 22, 2026 19:14
@Harry19081 Harry19081 added enhancement New feature or request agent Agent runtime, behavior, memory, providers, or orchestration cloud-collaboration Cloud sync, organizations, channels, or collaboration labels Aug 23, 2026
Persist Pause episodes and per-Turn handoff receipts so a Team can stop formal work immediately, drain runtimes safely, and resume only work that remains legal after restart or concurrent state changes.

Add runtime leases, generation-fenced Inbox and Task boundaries, one-shot continuation dispatch, paused Group Chat enforcement, push-driven draining UI, strict-provider task_update schemas, and rendered recovery coverage.

Verification:
- cargo test -p agent_core (3175 passed, 2 ignored)
- cargo clippy -p agent_core --all-targets -- -D warnings
- pnpm vitest run (1116 files, 8767 tests)
- pnpm typecheck and changed-file ESLint
- BuildFast packaged app with 20/20 ten-runtime Pause/Resume samples
- codexharry GPT-5.4 Mini packaged-app Pause/restart/Resume smoke

Pre-commit hook ran. Total eslint: 5, total circular: 0
Track detachable shell work by its exact Session, Turn, runtime lease, and dialog generation so Pause can stop and verify the whole process group before durable handoff release. Preserve ordinary Stop, Queue, and Force Send behavior, and prevent inverse Pause/Resume controls from accepting one double-click twice.

Verification:
- cargo test -p agent_core — 3188 passed, 0 failed
- cargo clippy -p agent_core --lib -- -D warnings — passed
- pnpm test — 1116 files and 8768 tests passed
- pnpm typecheck and pnpm lint — passed (5 unrelated warnings)
- BuildFast rendered and three-round live Provider acceptance — passed
- renamed live Pause/Resume and Task lifecycle smoke — passed

Pre-commit hook ran. Total eslint: 5, total circular: 0
@ShiboSheng
ShiboSheng force-pushed the codex/issue-760-pr5-pause-resume branch from 4988632 to a717909 Compare August 23, 2026 20:25
Resume could leave the original inbox assignment unread or let a Task finish while Turn-owned shell or subagent work was still running. Track exact Session, Turn, and runtime ownership, feed terminal job results back into the same Turn, and release the assignment only after successful Task completion. Bound teardown and fail closed without changing ordinary SDE job behavior.

Verification:
- cargo test -p agent_core — 3,197 passed, 0 failed
- cargo clippy -p agent_core --all-targets -- -D warnings — passed
- pnpm test — 8,768 passed
- WEBDRIVER=1 pnpm run tauri:build:fast — passed
- Real Provider Pause, restart, and Resume acceptance — passed in three isolated rounds

Pre-commit hook ran. Total eslint: 5, total circular: 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Agent runtime, behavior, memory, providers, or orchestration cloud-collaboration Cloud sync, organizations, channels, or collaboration enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(agent-org): [5/10] add durable Pause and Resume handoff

2 participants