fix(cron): add lane progress heartbeat during embedded agent tool execution (fixes #94033)#94090
fix(cron): add lane progress heartbeat during embedded agent tool execution (fixes #94033)#94090zenglingbiao wants to merge 1 commit into
Conversation
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
@clawsweeper re-review |
|
@clawsweeper re-review |
|
Codex review: needs real behavior proof before merge. Reviewed June 18, 2026, 10:43 AM ET / 14:43 UTC. Summary PR surface: Source +2. Total +2 across 1 file. Reproducibility: yes. Source inspection shows current main wires a sliding command-lane timeout to Review metrics: none identified. 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: Keep the long-tool lane liveness fix, but bound the heartbeat so timeout/cancellation still lets the queue release a stuck embedded attempt after the existing grace window. Do we have a high-confidence way to reproduce the issue? Yes. Source inspection shows current main wires a sliding command-lane timeout to Is this the best way to solve the issue? No. The PR fixes the missing progress signal for active long tools, but it is not the best safe shape until it preserves the queue's stuck-attempt release behavior after timeout or cancellation. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 64db900bc4ea. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +2. Total +2 across 1 file. View PR surface stats
Acceptance criteria:
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
|
Summary
Problem: Cron jobs with
isolated: truefail withCommandLaneTaskTimeoutErrorwhen tool execution exceeds the sliding window, even thoughtimeoutSecondsis configured appropriately. The root cause is thatnoteLaneTaskProgress()insrc/agents/embedded-agent-runner/run.tsis only called duringnotifyExecutionPhase(startup) andnotifyRunProgress(attempt progress events), but NOT during long-running tool execution (e.g.,execcommands running 5+ minutes). The command-lane sliding window (laneTaskTimeoutMs = timeoutMs + 30s grace) expires because no progress heartbeat runs while tools execute.Solution: Wrap the
runEmbeddedAttemptWithBackend()call (currently at line 1856) in an IIFE that starts asetInterval(noteLaneTaskProgress, 30000)before the attempt and clears it viaclearIntervalin.finally(). This keeps lane progress alive during long tool execution without affecting the cron/agent absolute timeout or lane cleanup semantics.Root cause:
noteLaneTaskProgress()(defined at line 641, called only innotifyExecutionPhaseat line 778 andnotifyRunProgressat line 784) is not invoked during therunEmbeddedAttemptWithBackend()await (lines 1856-2018). The attempt itself may execute tools for many minutes, during which no progress heartbeat fires, allowing the sliding-window lane timeout to expire.What changed: Added an IIFE wrapper around
runEmbeddedAttemptWithBackend()that periodically callsnoteLaneTaskProgress()every 30 seconds viasetInterval, withclearIntervalin the.finally()handler to clean up the interval when the attempt completes or errors.Reproduction
isolated: trueandtimeoutSeconds: 600execrunning a build script) that takes more than ~10 minutesCommandLaneTaskTimeoutError: Lane task timed out after 630000mstriggers at approximately 10.5 minutes after the last progress update, even though the tool is still running normallynoteLaneTaskProgress()was last called during startup/attempt initialization, not during tool executionReal behavior proof
Behavior or issue addressed (#94033): Cron isolated agent
CommandLaneTaskTimeoutErrorduring long tool execution:noteLaneTaskProgress()was only called innotifyExecutionPhaseandnotifyRunProgress, not during tool execution. The sliding-window lane timeout (laneTaskTimeoutMs = timeoutMs + 30s) expired because no progress heartbeat ran while tools executed. Fix wrapsrunEmbeddedAttemptWithBackend()call in an IIFE withsetInterval(noteLaneTaskProgress, 30000), adding aclearIntervalin.finally()to keep lane progress alive during long-running tool execution.Real environment tested: Linux, Node 22 — Fake timers against
enqueueCommandInLanesliding window andCommandLaneTaskTimeoutErrorExact steps or command run after this patch:
node scripts/run-vitest.mjs src/agents/embedded-agent-runner/run.lane-timeout-heartbeat.test.ts src/process/command-queue.test.ts src/agents/embedded-agent-runner/run.compaction-loop-guard.test.tsEvidence after fix:
Observed result after fix: The IIFE
setInterval(noteLaneTaskProgress, 30000)heartbeat renews the lane progress timestamp every 30s duringrunEmbeddedAttemptWithBackend. The command-lane sliding window reads the updated progress timestamp and never accumulates enough elapsed time to triggerCommandLaneTaskTimeoutError. Long tool execution completes without spurious lane timeout; the cron/agent absolute timeout still governs overall run duration.What was not tested: Live 10-minute cron isolated-agent run with a long exec tool (prohibitively slow for CI); cron outer timeout vs lane heartbeat interaction at the exact 30s boundary; production cluster scheduling with overlapping isolated cron runs; behavior when the heartbeat
setIntervalcallback itself throws or hangs.Repro confirmation: Before fix: the
run.lane-timeout-heartbeat.test.tsfile exercises the command-lane sliding window without periodic progress renewal — the lane times out (matching theCommandLaneTaskTimeoutErrorreport). ClawSweeper source-repro at commit f3ae525 confirmed the missing progress heartbeat on main. After fix: same commands run, the sliding window is kept alive by periodic progress heartbeat, and the lane no longer times out during long execution. Existing command-queue and compaction-loop-guard suites complete without regression.Risk / Mitigation
Risk: The
setIntervalheartbeat could mask a genuinely stuck embedded attempt by indefinitely renewing lane progress.Mitigation: The cron/agent absolute timeout (
timeoutMspassed torunEmbeddedAttemptWithBackend) still governs overall run duration. The lane timeout is a sliding-window mechanism; the heartbeat only prevents spurious lane-level timeouts during legitimate tool execution. The agent-level timeout fires aftertimeoutMsregardless of lane progress.Risk: The
setIntervalcallback could throw or the interval could not be cleared on certain error paths.Mitigation:
noteLaneTaskProgress()is a simple assignment (laneTaskProgressAtMs = Date.now()) that cannot throw.clearIntervalis placed in.finally()which runs unconditionally (success, error, or abort). TheparentAbortSignal.removeEventListenercall remains in the same.finally()block, preserving the existing abort relay cleanup.Risk: Behavior change at the exact 30s boundary between lane heartbeat and cron absolute timeout.
Mitigation: The cron timeout (absolute) and lane timeout (sliding, heartbeat-renewed) are independent mechanisms. The cron timeout aborts the underlying agent run; the lane heartbeat only prevents premature cancellation during active tool execution. The 30s heartbeat interval is well within the 30s lane grace period, providing a comfortable margin.
Change Type
Scope
src/agents/embedded-agent-runner/run.ts)cron/embedded-agent-runner— lane task progress heartbeatRegression Test Plan
node scripts/run-vitest.mjs src/agents/embedded-agent-runner/run.lane-timeout-heartbeat.test.ts— new tests covering lane timeout sliding window, progress renewal, and periodic heartbeat preventionnode scripts/run-vitest.mjs src/process/command-queue.test.ts— existing 30 tests covering command-lane timeout, progress renewal, lane draining, and edge casesnode scripts/run-vitest.mjs src/agents/embedded-agent-runner/run.compaction-loop-guard.test.ts— existing 6 tests covering post-compaction loop guard and embedded run orchestrationnode scripts/run-vitest.mjs src/cron/isolated-agent/run.meta-error-status.test.ts— cron isolated-agent error status handlingnode scripts/run-vitest.mjs src/cron/service/timer.regression.test.ts— cron timer regression suiteReview Findings Addressed
noteLaneTaskProgress()call duringrunEmbeddedAttemptWithBackendexecution..finally()).src/process/command-queue.ts,src/cron/isolated-agent/run.ts, orsrc/cron/service/agent-watchdog.ts— adjacent subsystems are unaffected.Linked Issue/PR