Skip to content

fix(automation): do not resume scheduled worker sessions on restore - #4

Closed
paulork wants to merge 1 commit into
mainfrom
fix/automation-worker-resume
Closed

paulork wants to merge 1 commit into
mainfrom
fix/automation-worker-resume

Conversation

@paulork

@paulork paulork commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Summary

A scheduled automation worker pane now restores as a plain shell. Its headless agent run is no longer reopened as an interactive session after a server restart, in that lifetime or any later one. Interactive panes, manual ORCH workers, and active_agent automations resume as before.

Follow-up to RizRiyz#488/RizRiyz#490. RizRiyz#488 promised to check how the optional Luvus Devin hook behaves in a scheduled run. That check, run on the merged Devin automation profile, found that the next server restart reopened the worker's session. The cause turned out to be agent-agnostic, so this PR fixes it for every agent instead of special-casing Devin.

Closes RizRiyz#497

What

  • src/app/board.rs: task_start marks the pane in App::automation_worker_panes when it launches TaskAgentLaunch::Automation. Manual task starts and active_agent targets are never marked.
  • src/app/mod.rs: a pane leaves the set when it closes, at the same point as backend_non_restorable. Restore reads the mark back and ignores any agent_session a marked pane still carries, for example from a hand-edited snapshot.
  • src/persist.rs:
    • PaneSnap::automation_worker (#[serde(default, skip_serializing_if = "Not::not")]), so ordinary panes keep the existing session.json shape and older snapshots still load.
    • A worker's reported run is claimed before pass 1. Pass 1 then leaves the worker unbound, exactly as it does a duplicate report, and discovery can't hand that conversation to another pane.
    • A worker without a report stays in its (agent, cwd) discovery group, so an interactive pane beside it remains ambiguous, as today. resolve_pane_sessions never binds a worker, whether by title or as "one pane, one session".
  • The live pane.report_session binding is unchanged, so Mission Control and usage still see the run. It is only kept out of resume.
  • Docs: automation guide (recovery after restart), agents guide (session resume), and website/public/agent-readme.md. Neither skill copy describes restore, so both are unchanged.

Design choices

RizRiyz#497 raised two open questions, each with two options. This PR deliberately takes the first option in both. Either one can be switched if you prefer the other.

  1. Persisted mark vs. leaving workers out of restore: chosen: keep the pane and persist the mark. The finished worker's screen stays visible after a restart, and ORCH needs no change. The other option would also need the pass-2 grouping above: today's backend_non_restorable path drops a pane from its discovery group, and that would let an interactive pane in workspace mode inherit the worker's conversation.
  2. An agent started by hand in a finished worker pane: chosen: the mark lasts for the pane's lifetime, so that pane doesn't resume either. It keeps the rule simple and predictable. The alternative, clearing the mark when an interactive agent appears, would be a small change.

Why

Neither api_pane_report_session nor capture_session_evidence knew that a pane was an automation worker. A worker pane could be bound in either snapshot pass: by a hook report, as measured with Devin and Claude, or by native discovery when its run was the only session in its directory, as measured with Codex. The next restart then ran <agent> --resume <id> interactively, without the worker's access policy, and kept doing so on every restart after that.

It shows up when the run finishes with no client attached, which is the usual case for a scheduled job, or when the server restarts during a run. With a TUI attached, process scans clear the binding when the agent exits. The full measurements are in RizRiyz#497.

The mark can't come from ORCH: pane ids are reallocated on restart, and assignee is either cleared or stale by then. A mark kept only in memory is not enough either. After one restart a restored worker screen can re-identify Codex, and the next restart would rebind it.

Screenshots

The same scheduled Codex worker in both images: workspace mode, run with no client attached, then a server restart and a TUI attach. AGENTS is filtered to Active.

Before, on main: the finished worker tab reopened codex resume by itself and is waiting at Codex's trust prompt. AGENTS shows a live blocked codex that nobody started.

pr-497-before-main

With this PR: the worker tab restores as a shell with the finished run's output. AGENTS shows only the scheduled definition.

pr-497-after-this-pr

Verification

Windows 11, base f6c68e9.

  • cargo fmt --all --check (Rust 1.99.0, the current stable used by CI)
  • cargo clippy --all-targets -- -D warnings (Rust 1.99.0, Windows)
  • New tests:
    • app::board:
      • a_scheduled_worker_does_not_save_its_reported_session_for_resume
      • a_scheduled_worker_is_not_bound_to_its_discovered_session
      • an_interactive_pane_does_not_inherit_a_workers_session
      • an_interactive_pane_keeps_its_own_session_beside_a_reported_worker
      • a_restored_scheduled_worker_stays_unbound_when_identified_again
      • a_restored_worker_keeps_its_mark_across_restarts
      • a_worker_snapshot_that_names_a_session_does_not_resume_it
      • the_worker_mark_is_saved_only_for_workers (including a snapshot without the field)
      • a_worktree_scheduled_worker_is_marked_and_not_resumed
      • a_failed_scheduled_start_leaves_no_worker_mark
      • closing_a_scheduled_worker_forgets_it
      • a_manual_task_worker_still_saves_its_reported_session
    • persist::title_resolution_tests: a_scheduled_worker_title_never_resolves_its_own_run
    • app::automation: active_agent_run_delivers_without_creating_an_orch_worker now also asserts that no pane is marked.
  • Each guarded line was reverted on its own, and at least one of the tests above failed every time. The checks were: marking at start, claiming the reported run, the pass-2 lone-worker guard, keeping an unbound worker in its group, the title guard, saving and restoring the mark, the restore filter, and close cleanup. That check also caught one design mistake before this PR: excluding a reported worker from pass 1 explicitly made an interactive pane beside it lose its own session. an_interactive_pane_keeps_its_own_session_beside_a_reported_worker covers that.
  • Existing tests unchanged and passing: reported_session_*, agent_session_persists_and_resumes, app::automation::tests, persist::title_resolution_tests.
  • cargo test --locked: on this Windows checkout the full suite stalls in five lock-heavy groups, as it does on unmodified main. With those groups skipped (integration::tests, diff::notes::tests, config::tests, cli::tests::wait_status_stream, i18n::cli), this branch reports 2152 passed and 32 failed. The 32 failures are exactly the ones unmodified main reports on this machine (CRLF docs tests, git::local, OSC 8, worktree, module titles, ipc::api streams, uhp::gateway). A few uhp::gateway and app::dispatch::tests::agents tests are flaky here under full-suite load on unmodified main too, and they pass in isolation. CI covers the full suite on Ubuntu and macOS.
  • Fork CI (run 38669110602): fmt · clippy · test (Ubuntu, including the Unix-only persist::tests), macos test, windows protocol · ConPTY, both nix flake jobs, cargo audit, npm audit, and the binary-size jobs passed.
  • cargo build --release --locked
  • npm run build in website/
  • End to end with the debug binary, in an isolated LUVUS_HOME and named sessions. The production server was not touched.
    • Claude worker (read-only, worktree) with its session reported from the worker pane, plus an interactive pane with a reported session:
      • snapshot: the worker gets agent_session: null and "automation_worker": true; the interactive pane keeps its session and gets no new field;
      • restart 1: the worker is a shell, and the interactive pane runs claude --resume '<id>';
      • inside the restored worker pane, I reported its run again, as a hook would;
      • restart 2: the worker is still a shell, it still carries the mark, and the interactive pane still resumes.
    • A Codex worker with no hook report (discovery pass), identified from its screen text: saved with agent_session: null, and restored as a shell. Before this change, the same case saved the exec rollout and restored with codex resume '<id>'.
    • A snapshot with the mark and a session: not resumed. A snapshot without the mark (written before this change): resumed, as noted below.
    • Devin 3000.11.3 with luvus integration install devin, which was the original report. The worker's SessionStart hook bound its run (authority: prior_identity) as before. The snapshot saved it as null with the mark, and two restarts left a shell with no devin --resume. The Devin config was restored byte for byte afterwards.
    • Workspace mode with Codex, beside a pane in the same directory: that pane kept its session and resumed. After the restart, the restored worker screen was re-identified as an agent, and the next snapshot still saved no session for it because the mark was persisted.
    • With a TUI attached: the worker was saved and restored the same way, and kept its mark across two snapshots.

Notes

  • A session.json written before this change has no mark, so the first restart after upgrading still reopens an already-bound worker pane interactively. From then on that pane is an ordinary interactive agent pane and keeps resuming, as it does today, until it is closed. Workers started after the upgrade are marked.
  • The worker's conversation stays in the agent's own store and can still be resumed deliberately (<agent> --resume <id>).
  • Measured on Windows only. Unix-only persist::tests run in CI.

@paulork

paulork commented Oct 16, 2026

Copy link
Copy Markdown
Owner Author

Merged upstream as RizRiyz#506 (squash commit 0062178). Closing this mirror.

@paulork paulork closed this Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Scheduled automation workers resume their agent session interactively after a server restart

1 participant