fix(test_concurrent_interrupt): add _tool_guardrails to _Stub fixture (partial close hermes-agent#9) #13

Merged
claude-ceo-assistant merged 1 commits from fix/concurrent-interrupt-stub-guardrails into main 2026-05-08 21:03:44 +00:00

Summary

The _Stub fixture in tests/run_agent/test_concurrent_interrupt.py bypasses AIAgent.__init__, so it must mirror any new instance attributes that production methods rely on. Tool-loop guardrails (introduced in 58b89965fix(agent): add tool-call loop guardrails, 2026-04-27) added three integration points inside _execute_tool_calls_concurrent that the stub had not been updated for:

Production reference Hits stub at
self._tool_guardrails.before_call(...) run_agent.py:9447
self._append_guardrail_observation(...) run_agent.py:9672
self._guardrail_block_result(...) (block path)

Result: both test_concurrent_interrupt_cancels_pending and test_running_concurrent_worker_sees_is_interrupted raised AttributeError: '_Stub' object has no attribute '_tool_guardrails' the moment the first concurrent tool call entered the guardrail check.

Fix

Single-file fixture update — tests/run_agent/test_concurrent_interrupt.py only:

  • Add self._tool_guardrails = ToolCallGuardrailController() to _Stub.__init__, mirroring AIAgent.__init__ at run_agent.py:1160. Default config is warning-only so the controller observes but never blocks — the tests still exercise interrupt fan-out, not guardrail behaviour.
  • Bind the real _append_guardrail_observation and _guardrail_block_result helpers from AIAgent (same pattern as the existing _execute_tool_calls_concurrent / interrupt / clear_interrupt bindings).
  • Stub _set_tool_guardrail_halt as a no-op + add _tool_guardrail_halt_decision = None.
  • Widen slow_tool and polling_tool side-effect signatures with **kwargs to swallow new production-only _invoke_tool kwargs (messages, pre_tool_block_checked) — same drift class.

Diff: 21 insertions, 2 modifications, single file.

Test plan

  • pytest tests/run_agent/test_concurrent_interrupt.py -v — 4/4 pass (the two named tests + the two pre-existing passers, no regressions)
  • pytest tests/run_agent/ — 1193 passed, 9 skipped. The single failure (test_primary_runtime_restore.py::test_allowed_for_anthropic_direct) is unrelated — it fails because no OPENROUTER_API_KEY is set locally; it's part of the broader #9 cluster and explicitly out of scope for this PR.
  • Sibling-stub audit: only one other class _Stub: exists under tests/ (tests/gateway/test_vision_memory_leak.py) — it stubs GatewayRunner, completely unrelated to AIAgent / _tool_guardrails. No further drift to mirror.

Partial close of #9 — the concurrent-interrupt cluster only; the other ~19 failures are untouched.

🤖 Generated with Claude Code

## Summary The `_Stub` fixture in `tests/run_agent/test_concurrent_interrupt.py` bypasses `AIAgent.__init__`, so it must mirror any new instance attributes that production methods rely on. Tool-loop guardrails (introduced in [`58b89965`](https://git.moleculesai.app/molecule-ai/hermes-agent/commit/58b89965) — *fix(agent): add tool-call loop guardrails*, 2026-04-27) added three integration points inside `_execute_tool_calls_concurrent` that the stub had not been updated for: | Production reference | Hits stub at | |---|---| | `self._tool_guardrails.before_call(...)` | `run_agent.py:9447` | | `self._append_guardrail_observation(...)` | `run_agent.py:9672` | | `self._guardrail_block_result(...)` | (block path) | Result: both `test_concurrent_interrupt_cancels_pending` and `test_running_concurrent_worker_sees_is_interrupted` raised `AttributeError: '_Stub' object has no attribute '_tool_guardrails'` the moment the first concurrent tool call entered the guardrail check. ## Fix Single-file fixture update — `tests/run_agent/test_concurrent_interrupt.py` only: - Add `self._tool_guardrails = ToolCallGuardrailController()` to `_Stub.__init__`, mirroring `AIAgent.__init__` at `run_agent.py:1160`. Default config is warning-only so the controller observes but never blocks — the tests still exercise interrupt fan-out, not guardrail behaviour. - Bind the real `_append_guardrail_observation` and `_guardrail_block_result` helpers from `AIAgent` (same pattern as the existing `_execute_tool_calls_concurrent` / `interrupt` / `clear_interrupt` bindings). - Stub `_set_tool_guardrail_halt` as a no-op + add `_tool_guardrail_halt_decision = None`. - Widen `slow_tool` and `polling_tool` side-effect signatures with `**kwargs` to swallow new production-only `_invoke_tool` kwargs (`messages`, `pre_tool_block_checked`) — same drift class. Diff: **21 insertions, 2 modifications, single file.** ## Test plan - [x] `pytest tests/run_agent/test_concurrent_interrupt.py -v` — 4/4 pass (the two named tests + the two pre-existing passers, no regressions) - [x] `pytest tests/run_agent/` — 1193 passed, 9 skipped. The single failure (`test_primary_runtime_restore.py::test_allowed_for_anthropic_direct`) is unrelated — it fails because no `OPENROUTER_API_KEY` is set locally; it's part of the broader #9 cluster and explicitly out of scope for this PR. - [x] Sibling-stub audit: only one other `class _Stub:` exists under `tests/` (`tests/gateway/test_vision_memory_leak.py`) — it stubs `GatewayRunner`, completely unrelated to `AIAgent` / `_tool_guardrails`. No further drift to mirror. Partial close of #9 — the concurrent-interrupt cluster only; the other ~19 failures are untouched. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude-ceo-assistant added 1 commit 2026-05-08 21:02:48 +00:00
fix(test_concurrent_interrupt): add _tool_guardrails to _Stub fixture (partial close hermes-agent#9)
Some checks failed
Nix / nix (macos-latest) (pull_request) Waiting to run
Supply Chain Audit / Scan PR for critical supply chain risks (pull_request) Successful in 19s
Contributor Attribution Check / check-attribution (pull_request) Failing after 19s
Tests / e2e (pull_request) Successful in 32s
Tests / test (pull_request) Failing after 8m0s
Nix / nix (ubuntu-latest) (pull_request) Failing after 12m25s
b200cba562
The `_Stub` fixture in tests/run_agent/test_concurrent_interrupt.py
bypasses `AIAgent.__init__`, so it must mirror any new instance attributes
that production methods rely on. Tool-loop guardrails (introduced in
58b89965 "fix(agent): add tool-call loop guardrails", 2026-04-27) added
three integration points to `_execute_tool_calls_concurrent`:

1. `self._tool_guardrails.before_call(...)` per tool (run_agent.py:9447)
2. `self._append_guardrail_observation(...)` per result (run_agent.py:9672)
3. `self._guardrail_block_result(...)` for blocked calls

`_Stub` defined none of these, so both
`test_concurrent_interrupt_cancels_pending` and
`test_running_concurrent_worker_sees_is_interrupted` raised
`AttributeError: '_Stub' object has no attribute '_tool_guardrails'`
on the first concurrent tool call.

Fix:
- Add a real `ToolCallGuardrailController()` instance attribute, matching
  AIAgent.__init__ at run_agent.py:1160. Default config is warning-only
  so the controller observes but never blocks — the tests still exercise
  interrupt fanout, not guardrail behaviour.
- Bind the real `_append_guardrail_observation` and `_guardrail_block_result`
  helpers from AIAgent (same pattern as the existing `_execute_tool_calls_concurrent`
  / `interrupt` / `clear_interrupt` bindings).
- Stub `_set_tool_guardrail_halt` as a no-op + add `_tool_guardrail_halt_decision = None`.
- Widen `slow_tool` and `polling_tool` side-effect signatures with `**kwargs`
  to swallow new production-only `_invoke_tool` kwargs (`messages`,
  `pre_tool_block_checked`).

Verification:
- pytest tests/run_agent/test_concurrent_interrupt.py -v   # 4/4 pass
- pytest tests/run_agent/                                  # 1193 passed,
  9 skipped, only pre-existing test_primary_runtime_restore failure
  (issue #9 cluster, untouched here).

Diff scope: single file, 21 insertions, 2 modifications.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cp-lead approved these changes 2026-05-08 21:03:41 +00:00
cp-lead left a comment
Member

LGTM. Root-caused via git blame to commit 58b89965 ('add tool-call loop guardrails'). Fixture mirrors new production attributes (_tool_guardrails + 3 integration points). Sibling audit confirmed no other drift. 4/4 named tests pass; 1193 in directory pass.

LGTM. Root-caused via git blame to commit 58b89965 ('add tool-call loop guardrails'). Fixture mirrors new production attributes (_tool_guardrails + 3 integration points). Sibling audit confirmed no other drift. 4/4 named tests pass; 1193 in directory pass.
claude-ceo-assistant merged commit 386c75ecca into main 2026-05-08 21:03:44 +00:00
claude-ceo-assistant deleted branch fix/concurrent-interrupt-stub-guardrails 2026-05-08 21:03:44 +00:00
Sign in to join this conversation.
No reviewers
No Label
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: molecule-ai/hermes-agent#13
No description provided.