fix: stop Claude tool heartbeats from hiding stuck sessions#96198
fix: stop Claude tool heartbeats from hiding stuck sessions#96198harjothkhara wants to merge 3 commits into
Conversation
|
Pushed a small follow-up ( Code —
Body clarification (User Impact): Made explicit that dropping the heartbeat refresh re-enables the existing |
|
Codex review: needs real behavior proof before merge. Reviewed June 30, 2026, 6:27 PM ET / 22:27 UTC. Summary PR surface: Source +10, Tests +23. Total +33 across 3 files. Reproducibility: yes. at source level: current main leaves Claude live Review metrics: 1 noteworthy metric.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Security Review findings
Review detailsBest possible solution: Rework the fix so wedged Claude tools can age and recover without removing heartbeat freshness for valid quiet foreground tools, then prove both cases with redacted live Claude CLI or packaged-runtime logs. Do we have a high-confidence way to reproduce the issue? Yes at source level: current main leaves Claude live Is this the best way to solve the issue? No. The branch is a plausible diagnostic-boundary fix for the wedge, but it is too broad because it disables the shipped heartbeat freshness path that protects valid long-running Claude-native tools. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 3811001d2783. Label changesLabel justifications:
Evidence reviewedPR surface: Source +10, Tests +23. Total +33 across 3 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
Closes #96168
What Problem This Solves
Fixes an issue where Claude CLI live-session runs could keep a wedged native tool looking fresh when the backend emitted active-tool heartbeat progress. If the CLI emitted
tool_usebut never emitted the matchingtool_result, OpenClaw's diagnostic stuck-session detection could see recent progress and delay surfacing or recovering the blocked work.Why This Change Was Made
cli_live:tool_runningis now treated as passive diagnostic progress: the heartbeat event is still emitted for observability, but it no longer creates or refreshes diagnostic session activity. Real progress, including tool start/result and run result events, still updates the activity clock.This keeps the fix at the diagnostic activity boundary, avoids changing Claude CLI runtime behavior, and does not add config or a new timeout policy. Blast radius is limited to diagnostic run-activity bookkeeping and the existing Claude live-session regression harness.
User Impact
Wedged Claude CLI live-session turns with an active tool that never returns a
tool_resultcan age normally in stuck-session detection instead of being kept fresh by a synthetic heartbeat.Because the synthetic heartbeat no longer masks tool age, Claude live native-tool calls now follow the same stall/abort policy already applied to model calls and embedded runs: a tool with no real intermediate progress is surfaced as a stalled (
blocked_tool_call) session past the warn threshold (default 2 min) and becomes auto-recovery/abort-eligible past the abort threshold (default 6 min, configurable viadiagnostics.stuckSessionAbortMs). Previously such tools were shielded from stall detection indefinitely. Tools that stream real intermediate output keep refreshing the activity clock exactly as before, so normal long-running work is unaffected.Evidence
AI-assisted disclosure: This PR was prepared with Codex assistance; I reviewed the change and validation.
Behavior addressed
Claude live active-tool heartbeat events still emit
run.progress, but no longer refresh diagnostic session "last real progress" state.Real environment tested
Local OpenClaw checkout on current
origin/mainSHAc24d266b2d0943a2376ae9ac87a2fd8e200920e1, using the production Claude live runner path up to the subprocess boundary, diagnostic event bus, timer heartbeat, and session activity snapshot.Exact steps or command run after this patch
node scripts/run-vitest.mjs src/agents/cli-runner.spawn.test.ts -t "reports Claude live stream progress without treating active tool heartbeats as progress"Evidence after fix
Focused run passed:
Test Files 2 passed (2),Tests 2 passed | 118 skipped (120).Supplemental validation:
node scripts/run-vitest.mjs src/logging/diagnostic-session-attention.test.ts- passed,1 file,11 testsnode scripts/run-vitest.mjs src/logging/diagnostic.test.ts -t "does not refresh progress age for Claude live active-tool heartbeats"- passed,1 test | 72 skippednode scripts/run-oxlint.mjs src/logging/diagnostic-run-activity.ts src/agents/cli-runner.spawn.test.ts- passedgit diff --check- passed.agents/skills/autoreview/scripts/autoreview --mode local- clean, no accepted/actionable findings reportedObserved result after fix
After a simulated Claude
tool_usestarts an active tool, the 10s heartbeat still producesrun.progress, butlastProgressReasonremainscli_live:tool_startedandlastProgressAgeMs >= 10000. The heartbeat no longer masks a wedged tool as fresh progress.What was not tested
No live Claude account/API call or actual Claude CLI binary invocation. The proof uses the repo's deterministic live-runner harness because the bug boundary is the JSONL stream, heartbeat timer, and diagnostic activity bookkeeping.