Repository navigation
Conversation
Run pre_tool_call callbacks on a dedicated daemon executor, measure timeout from callback start, and reserve suppression for callback runtime breaches. Log queue wait and runtime separately.\n\nVerified: scripts/run_tests.sh tests/agent/test_shell_hooks.py tests/hermes_cli/test_plugins.py -q (128 passed).
|
🔁 re-enqueue for FleetReview (Apollo): head's terminal record was stamped ERROR by a router restart (release cut 20260921T104203Z / sibling cuts 10:25-10:32Z), not by a review verdict; close/reopen re-submits the unchanged head. No code change. |
…he 10:42Z router restart, not a verdict; kanban review APPROVED at 69e71df, tree identical)
Adjudication: SUPERSEDED-BY #819 — and this PR would be a REGRESSION if landedClosing per the card's adjudication instruction (t_4dbe2f23). This is not a taste Mechanism (b) — "start the timeout clock at callback entry"Already provided by #819, and the residual is immaterial. 0.212 ms against a 30 s budget = 0.0007% of budget. There is no starvation to fix. Mechanism (a) — "dedicated bounded daemon pool for policy hooks"Not needed on Same three probes, both trees,
Deterministic, not flake ( An unrelated session's 30 ms hook was refused after never executing — the exact Mechanism (3) — queue-wait/run-time split loggingMeaningful only if a queue exists. DispositionNo red case on Note for the record: the card's premise (shared default-executor starvation) was |
Summary
pre_tool_callcallbacks on a dedicated 4-worker daemon executor instead of creating an unpooled thread per invocationqueue_waitandrun_timeseparately on both dispatch and callback-runtime timeoutsRoot cause
The live incident report suspected asyncio's shared default executor. Current
fork/mainhad already moved bounded hooks to per-callback daemon threads, so shared-pool saturation was not the direct current path. The remaining failure was equivalent:done.wait(timeout)began immediately afterThread.start(), charging thread dispatch / GIL scheduling delay to callback runtime. That false runtime timeout installed the same 60-second fail-closed suppression window.Tests
RED before implementation:
GREEN after implementation:
scripts/run_tests.sh tests/agent/test_shell_hooks.py tests/hermes_cli/test_plugins.py -q— 128 passed under host load 49/opt/homebrew/bin/ruff check hermes_cli/plugins.py tests/hermes_cli/test_plugins.py— cleangit diff --check refs/remotes/fork/main...HEAD— cleanThe saturated-default-executor regression holds one asyncio default worker busy while a 30 ms policy hook runs on a
hermes-policy-hookworker and completes within budget.