Skip to content

fix(T13890): bound cleo hook stop: detach session end, deadline ask-enforce - #2363

Merged
kryptobaseddev merged 2 commits into
mainfrom
fix/T13890-hook-stop-deadline
Oct 18, 2026
Merged

kryptobaseddev merged 2 commits into
mainfrom
fix/T13890-hook-stop-deadline

Conversation

@kryptobaseddev

Copy link
Copy Markdown
Owner

cleo hook stop took 160 s in rel-qual's smoke, past Claude Code's 120 s Stop timeout. After this PR, a Stop returns within the tick deadline (30 s by default, 60 s at most) plus at most 5 s.

What I found reading the code and probing

  • The tick was already bounded. runTickKillable SIGKILLs the whole process group at the deadline (detached: true with process.kill(-pid)), destroys stdout and unrefs the child, then settles. Nothing awaits close or exit after the timer fires.
    • Probe with the released 10.19 CLI and a sandboxed HOME: a tick whose grandchild calls setsid and holds stdout returns at the deadline (2 s with CLEO_GOAL_TICK_TIMEOUT_MS=2000), for both codex and claude-code.
  • Two steps after the tick were not bounded:
    • claude-code's endSession was spawnSync('cleo session end') with no timeout, so any slow session end (extraction, backups, a cold store) added directly to the Stop.
    • askEnforce was awaited with no deadline.
  • Not reproduced: the 160 s itself. No cleo-goal/ocg mise shim exists on this machine, and a sandboxed session end took 2 s. If rel-qual's run points somewhere else, I'll follow up.

Fix

  • endSession now spawns cleo session end --quiet detached with stdio: 'ignore' and unrefs it. The Stop never waits for it.
  • askEnforce runs under ASK_ENFORCE_DEADLINE_MS (5 s) through an unref'd timer. Past the deadline it counts as no block, consistent with the dispatcher's existing fail-open behaviour.

Tests (stop-dispatch-entry.test.ts)

  • A real tick spawns a detached grandchild that inherits its stdout, so it escapes the group and the pipe never reaches EOF. stopDispatch returns at the 800 ms deadline with sessionEnded: true.
  • With fake timers, a never-settling ask-enforce cannot hold the stop past ASK_ENFORCE_DEADLINE_MS.

Verification

The owner directive is no local heavy runs, so CI runs the tests and typecheck.

  • Biome format and lint are clean on both files.
  • Gates 6 (--check), 25, 38, 40 and 32 (--base origin/main) pass. CI enforces gate 39.

Merges are frozen for 10.20; this targets 10.21.

🤖 Generated with Claude Code

kryptobaseddev and others added 2 commits October 17, 2026 19:10
…nforce

The tick was already bounded (group SIGKILL plus stdout destroy at the
deadline). What followed it was not: claude-code's spawnSync of cleo session
end had no timeout, and ask-enforce was awaited without a deadline. Session
end now runs detached and unawaited; ask-enforce falls back to no-block
after ASK_ENFORCE_DEADLINE_MS (5 s). Tests: a tick whose setsid grandchild
holds stdout returns at the deadline; a hung ask-enforce cannot hold the
stop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dlines

Session end is awaited for at most SESSION_END_WAIT_MS (20 s) through
runBounded, then left running unref'd. A failure or an overrun prints one
stderr line with the duration, and so does the ask-enforce deadline.
Tick (60 s max) plus 5 s plus 20 s stays under Claude Code's 120 s Stop
timeout. Test: a real slow session-end process; the stop returns at the
wait with one warning; a failing one reports its exit status.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kryptobaseddev
kryptobaseddev merged commit a18c3ab into main Oct 18, 2026
79 of 81 checks passed
@kryptobaseddev
kryptobaseddev deleted the fix/T13890-hook-stop-deadline branch October 18, 2026 04:47
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