test: fold the schema-retry context test into the output-schema module
Reuse tests/tools/test_delegate_output_schema.py's _StubChild instead of a new one-test file with its own double; the invariant (the retry turn sees is_delegated_child_context() True and the flag is restored afterwards) is unchanged. Trim the source comment to the WHY.
This commit is contained in:
@@ -297,6 +297,30 @@ class TestRunSingleChildSchemaValidation:
|
||||
assert entry["status"] == "completed"
|
||||
assert "error" not in entry
|
||||
|
||||
def test_retry_turn_runs_in_delegated_child_context(self, monkeypatch):
|
||||
"""The retry is a second run_conversation on the child, issued from the parent's
|
||||
thread where HERMES_KANBAN_TASK is set. Unwrapped it carries the worker's identity,
|
||||
so the kanban stop guard nudges the child toward a board tool it does not have
|
||||
(#109735 / #87671)."""
|
||||
from agent.delegation_context import is_delegated_child_context
|
||||
from tools.delegate_tool_child_run import _validate_child_output_schema
|
||||
|
||||
monkeypatch.setenv("HERMES_KANBAN_TASK", "t_parent")
|
||||
seen: list = []
|
||||
|
||||
class _Child(_StubChild):
|
||||
session_id = "sess-child"
|
||||
|
||||
def run_conversation(self, user_message, task_id=None, **_kwargs):
|
||||
seen.append(is_delegated_child_context())
|
||||
return super().run_conversation(user_message, task_id, **_kwargs)
|
||||
|
||||
child = _Child(['{"city": "Berlin"}'])
|
||||
child._delegate_output_schema = ADDRESS_SCHEMA
|
||||
_validate_child_output_schema(child, {"final_response": "not json", "api_calls": 1}, 0, "child-0", None)
|
||||
assert seen == [True]
|
||||
assert is_delegated_child_context() is False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# delegate_task dispatch-time schema handling
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
"""The schema-retry turn must run inside the delegated-child context.
|
||||
|
||||
The main child turn is wrapped in ``delegated_child_context``; the bounded retry in
|
||||
``_validate_child_output_schema`` is a second ``run_conversation`` on the same child.
|
||||
Unwrapped, it runs with the parent worker's identity: ``HERMES_KANBAN_TASK`` is set and
|
||||
nothing marks it as a child, so the kanban stop guard nudges the child to call
|
||||
``kanban_complete``. A child owns no board task and cannot, so the nudge text displaces
|
||||
the answer the retry exists to produce and the retry fails the same schema again.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from agent.delegation_context import is_delegated_child_context
|
||||
from tools.delegate_tool_child_run import _validate_child_output_schema
|
||||
|
||||
|
||||
class _Child:
|
||||
"""Minimal stand-in recording the context state of each run_conversation turn."""
|
||||
|
||||
def __init__(self):
|
||||
self._delegate_output_schema = {
|
||||
"type": "object",
|
||||
"properties": {"ok": {"type": "boolean"}},
|
||||
"required": ["ok"],
|
||||
}
|
||||
self.session_id = "sess-child"
|
||||
self.seen_in_child_context = []
|
||||
|
||||
def run_conversation(self, user_message=None, task_id=None, stream_callback=None):
|
||||
self.seen_in_child_context.append(is_delegated_child_context())
|
||||
return {"final_response": '{"ok": true}', "api_calls": 1}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def kanban_env(monkeypatch):
|
||||
monkeypatch.setenv("HERMES_KANBAN_TASK", "t_parent")
|
||||
monkeypatch.delenv("HERMES_KANBAN_STOP_NUDGE", raising=False)
|
||||
return monkeypatch
|
||||
|
||||
|
||||
def test_schema_retry_runs_in_child_context(kanban_env):
|
||||
child = _Child()
|
||||
result = {"final_response": "not json at all", "api_calls": 1}
|
||||
|
||||
assert is_delegated_child_context() is False
|
||||
_validate_child_output_schema(child, result, 0, "child-task-0", None)
|
||||
|
||||
assert child.seen_in_child_context == [True], (
|
||||
"the schema-retry turn ran outside delegated_child_context"
|
||||
)
|
||||
assert is_delegated_child_context() is False
|
||||
@@ -424,10 +424,8 @@ def _validate_child_output_schema(
|
||||
# schema re-paste — the child already holds the contract in its context).
|
||||
_retry_result = None
|
||||
try:
|
||||
# Same identity as the main child turn (delegate_tool_child_run's worker): this runs
|
||||
# on the parent's thread, where HERMES_KANBAN_TASK is set, so without the marker the
|
||||
# kanban stop guard nudges the child to call kanban_complete. It owns no board task
|
||||
# and cannot, and the nudge displaces the answer this retry exists to produce.
|
||||
# Same identity as the main child turn: this runs on the parent worker's thread, and an
|
||||
# unmarked turn is misread as the dispatcher-owned worker by every HERMES_KANBAN_* gate.
|
||||
from agent.delegation_context import delegated_child_context
|
||||
with delegated_child_context(str(getattr(child, "session_id", "") or "")):
|
||||
_retry_result = child.run_conversation(
|
||||
|
||||
Reference in New Issue
Block a user