fix: _is_unattended_context requires positive cron/subagent signal (#1554) - #1556
fix: _is_unattended_context requires positive cron/subagent signal (#1554)#1556Lexus2016 wants to merge 2 commits into
Conversation
) Adds _is_unattended_context() and _cron_blocked_result() to tools/approval.py. Both fallback approval paths (terminal + execute_code) now check for unattended context and return a non-retryable 'blocked' status with 'Do NOT retry' message, preventing the 44% retry spirals. Closes #1542 Co-Authored-By: Hermes Evolution <evolution@hermes.ai>
…1554) The catch-all 'nobody's home' default in _is_unattended_context() returned True in the non-interactive CI test environment, turning every test's pending_approval into non-retryable blocked and breaking slices 2/8 and 8/8. Fix: require a POSITIVE signal (HERMES_CRON_SESSION or subagent contextvar) instead of defaulting to unattended. Subagents are detected via a new thread-local contextvar (_hermes_subagent_ctx), set in _run_single_child, mirroring the existing _hermes_interactive_ctx pattern. HERMES_SUBAGENT env var is kept as a fallback. Unblocks PR #1550 / accepted issue #1542 (suppress pending_approval retries — 44% of terminal failures). Co-Authored-By: Hermes Evolution <evolution@hermes.ai>
|
Code-review (evolution-integration, 2026-08-01): PASSED review but BLOCKED by autonomous merge gate for two policy reasons (both require human merge):
Verification performed: (a) Not dead code — |
Summary
Fixes the CI failures on PR #1550 (accepted issue #1542 — suppress pending_approval retries, 44% of terminal failures).
Problem
PR #1550 introduced
_is_unattended_context()to convertpending_approvalinto non-retryableblockedin cron/subagent contexts. But the implementation defaulted toTruewhenever no interactive CLI, gateway, or cron session was detected — a catch-all "nobody's home" default. In the non-interactive CI test environment, this evaluates toTrue, breaking every test that expectspending_approvalbehavior (slices 2/8 and 8/8).Fix (#1554)
_is_unattended_context()now requires a positive signal of an unattended context:HERMES_CRON_SESSIONenv var (already set incron/scheduler.py), OR_hermes_subagent_ctx), set indelegate_tool._run_single_childWhy a contextvar instead of
HERMES_SUBAGENT?Issue #1554 proposed checking a
HERMES_SUBAGENTenv var, but that env var does not exist onmain(0 grep matches). Subagents are identified by the_delegate_depthattribute on the agent instance, which module-leveltools/approval.pycannot access. The correct mechanism is acontextvars.ContextVar— mirroring the existing_hermes_interactive_ctxpattern (introduced for the same thread-safety reason: concurrent ACP/gateway sessions on a shared ThreadPoolExecutor). The contextvar is set in_run_single_child(the single entry point for both single and batch delegation) and reset in itsfinallyblock.Validation
test_approval_cron_block.pytests pass (6 existing + 5 new)test_approval.pyfailures (tee/sensitive-redirect patterns) are pre-existing — confirmed by running them against the pre-fix commitc25748edf; they fail identically and are caused by running as root (~/.bashrcpath resolution)Size note
236 insertions across 3 files — over the 200-line autonomous-merge cap. The three files are tightly coupled (contextvar declared in
approval.py, set indelegate_tool.py, tested in the test file); no smaller coherent slice exists. Holding for human review.Closes #1554
Co-Authored-By: Hermes Evolution evolution@hermes.ai