Skip to content

fix: stale RUNNING dispatch recovery with session-type-agnostic liveness#4136

Merged
Trecek merged 4 commits into
developfrom
interactive-sessions-cannot-recover-stale-running-dispatch-s/4133
Jun 28, 2026
Merged

fix: stale RUNNING dispatch recovery with session-type-agnostic liveness#4136
Trecek merged 4 commits into
developfrom
interactive-sessions-cannot-recover-stale-running-dispatch-s/4133

Conversation

@Trecek

@Trecek Trecek commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Interactive sessions have zero path to recover stale RUNNING dispatch state. The bug is a convergence of two gaps: (1) reset_dispatch blocks on RUNNING status without checking process liveness, and (2) neither open_kitchen nor any interactive boot path runs the dispatch reaper. The architectural weakness is that liveness verification before status-based blocking is enforced by convention, not by structure — three callers do it correctly (resume_campaign_from_state, _try_claim_with_liveness, reap_stale_dispatches) while reset_dispatch does not.

The immunity plan extracts a shared resolve_stale_running helper that centralizes the "RUNNING → check liveness → classify if dead" pattern, adds the reaper to open_kitchen, extends the existing AST boot-step symmetry guard to cover open_kitchen, and adds integration tests that would have caught this bug immediately.

The remediation plan replaces mocked resolve_stale_running in two tests (test_reset_dispatch_running_dead_process_succeeds and test_reset_dispatch_running_alive_process_still_blocked) with real PID/boot-id fields on the dispatch record, exercising the full liveness detection path: reset_dispatchfind_locked_dispatchresolve_stale_runningis_dispatch_session_aliveis_session_alive.

Closes #4133

Implementation Plan

Plan files:

  • /home/talon/projects/autoskillit-runs/remediation-20260627-224639-242652/.autoskillit/temp/rectify/rectify_stale_running_dispatch_recovery_2026-06-27_224639.md
  • /home/talon/projects/autoskillit-runs/remediation-20260627-224639-242652/.autoskillit/temp/make-plan/remediation_stale_running_dispatch_recovery_plan_2026-06-28_001600.md

🤖 Generated with Claude Code via AutoSkillit

Token Usage Summary

Step Model count uncached output cache_read peak_ctx turns cache_write time
plan* opus[1m] 4 10.4k 59.3k 5.8M 123.0k 172 318.2k 59m 40s
verify* sonnet 4 296 31.5k 1.6M 71.9k 93 163.0k 17m 43s
implement* MiniMax-M3 4 254.0k 28.5k 4.1M 60.5k 248 0 13m 24s
retry_worktree* sonnet 1 76 3.6k 314.5k 40.7k 24 22.2k 2m 2s
audit_impl* sonnet 4 818 30.4k 974.1k 51.9k 65 127.4k 18m 21s
dispatch:1133b9e6-5319-48e2-b878-5f30f979fbcb* sonnet 1 170 5.8k 1.4M 77.7k 51 77.9k 20m 52s
prepare_pr* MiniMax-M3 3 133.3k 7.1k 548.5k 49.1k 43 0 3m 18s
compose_pr* MiniMax-M3 3 109.8k 5.8k 607.3k 38.5k 39 0 2m 16s
review_pr* sonnet 4 482 46.6k 2.7M 59.5k 155 161.1k 13m 55s
dispatch:9f9aa203-f3ea-44fd-9d3d-8f7ce0265f6f* sonnet 1 298 10.5k 2.8M 84.3k 77 59.8k 1h 5m
merge_gate_fix* sonnet 1 110 5.5k 527.3k 47.6k 35 29.5k 2m 55s
dispatch:0fd3b0cc-c159-4291-9787-f06a3a78854b* sonnet 1 418 14.4k 4.0M 89.4k 104 64.9k 48m 12s
Total 510.3k 249.2k 25.4M 123.0k 1.0M 4h 28m

* Step used a non-Anthropic provider; caching behavior may differ.

Token Efficiency

Step LoC Changed cache_read/LoC cache_write/LoC output/LoC
plan 0
verify 0
implement 735 5593.3 0.0 38.8
retry_worktree 0
audit_impl 0
dispatch:1133b9e6-5319-48e2-b878-5f30f979fbcb 0
prepare_pr 0
compose_pr 0
review_pr 0
dispatch:9f9aa203-f3ea-44fd-9d3d-8f7ce0265f6f 0
merge_gate_fix 7 75335.4 4210.6 791.0
dispatch:0fd3b0cc-c159-4291-9787-f06a3a78854b 0
Total 742 34191.7 1380.1 335.8

Model Usage Breakdown

Model steps uncached output cache_read cache_write time
opus[1m] 1 10.4k 59.3k 5.8M 318.2k 59m 40s
sonnet 8 2.7k 148.4k 14.3M 705.8k 3h 9m
MiniMax-M3 3 497.2k 41.4k 5.3M 0 18m 59s

Trecek and others added 4 commits June 28, 2026 01:13
Extract resolve_stale_running helper to centralize the RUNNING→check liveness
pattern. reset_dispatch now verifies process liveness before blocking RUNNING
dispatches, open_kitchen runs reap_stale_dispatches_async for interactive
sessions, and structural guards prevent future regressions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…mentation

- Extract find_locked_dispatch helper to fleet/_reset.py domain layer to
  satisfy REQ-CNST-008 (no comprehensions in tool handlers)
- Hoist CampaignStateMutator import to module level to satisfy
  IL-3 cross-layer import hoisting and cross-package submodule rules
- Fix test_reset_dispatch_running_dead_process_succeeds to mock
  resolve_stale_running returning False (dead) and assert success
- Fix test_reset_dispatch_running_alive_process_still_blocked to mock
  resolve_stale_running returning True (alive) and assert block
- Update _LEGACY_JSON_WRITES line numbers for tools_kitchen.py shifts
- Bump tools_kitchen.py line limit exemption to 1290 for reaper call

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ess fields in reset_dispatch tests

Exercise the full liveness detection path (resolve_stale_running →
is_dispatch_session_alive → is_session_alive) in
test_reset_dispatch_running_dead_process_succeeds and
test_reset_dispatch_running_alive_process_still_blocked by writing
real PID/boot-id/starttime fields onto the dispatch record via
CampaignStateMutator instead of mocking resolve_stale_running.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
resolve_stale_running only calls mark_dirty() on the dead-dispatch path.
The reaper unconditionally mutates dispatch fields afterward (ended_at,
reaper_reason, reaper_dispatch_id, tombstone, reaper event), so
mark_dirty() must be called unconditionally to ensure persistence.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Trecek Trecek force-pushed the interactive-sessions-cannot-recover-stale-running-dispatch-s/4133 branch from 096c939 to ea37283 Compare June 28, 2026 08:13
@Trecek Trecek added this pull request to the merge queue Jun 28, 2026
Merged via the queue into develop with commit aa38139 Jun 28, 2026
3 checks passed
@Trecek Trecek deleted the interactive-sessions-cannot-recover-stale-running-dispatch-s/4133 branch June 28, 2026 08:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant