5db1b72b1f
Resumable ESTOP sentinel at $HERMES_HOME/ESTOP that halts NEW work only: - agent/estop.py: sentinel engage/disengage/is_engaged (single stat, no caching), optional reason + timestamp stored as JSON, paused_reply() notice, check_paused() log-once-per-engagement helper. Corrupt/empty sentinel still pauses (fail safe); a `touch ~/.hermes/ESTOP` works. - cron/scheduler.py: tick() skips dispatch while engaged (logged once per engagement, not per tick). Due jobs simply wait for the next tick after resume — in-flight runs are never touched. - gateway/kanban_watchers.py: dispatcher skips auto-decompose and worker spawning while engaged; zombie reaping still runs and running workers finish naturally. - gateway/run.py: new gateway turns (post-auth, non-internal) get a brief "Hermes is paused" reply instead of an agent run. Internal events (in-flight background completions) bypass the gate. - hermes_cli/subcommands/pause.py: `hermes pause [--reason]` and `hermes resume`, wired into main() and _BUILTIN_SUBCOMMANDS. - hermes_cli/status.py: `hermes status` shows a PAUSED banner (one stat). - tests/test_estop.py: 20 tests — sentinel lifecycle, reason surfacing, log-once, cron skip + resume, kanban gate, gateway paused reply + internal bypass, CLI idempotence, builtin-set parity, status line. Never kills in-flight work; resumable with no restart. Footprint ladder: CLI command only, no new model tool, no new env vars. Ported from: gastownhall/gastown estop.go (MIT); related prior art: #26778 (/panic — kill/exit semantics, deliberately different: ours is a resumable pause), #44617 (interrupt in-flight cron — out of scope here).