From 51eb6065174b9a3320553c56976620a31397cf66 Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 19 Oct 2026 23:38:04 -0400 Subject: [PATCH 1/5] Apply declared directive wording to fresh native orders before reservation --- src/agent_comms/goal_actions.py | 1 + src/agent_comms/todos.py | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/agent_comms/goal_actions.py b/src/agent_comms/goal_actions.py index 59b73b4ae7..37eaf479ca 100644 --- a/src/agent_comms/goal_actions.py +++ b/src/agent_comms/goal_actions.py @@ -529,6 +529,7 @@ def change(self, goal: Goal, ctx: GoalActionContext) -> Goal: def request_ref(self, ctx: GoalActionContext) -> GoalRef: """Refuse invalid orders before reserving their Todo.""" + self.order.require_wording() commander = ctx.goals.registry.require(self.commander) commander.goal.require().require_id(self.request).require_active().order.require_request() self.deadline.require_deadline() diff --git a/src/agent_comms/todos.py b/src/agent_comms/todos.py index 896e0be57f..a3e18cf80d 100644 --- a/src/agent_comms/todos.py +++ b/src/agent_comms/todos.py @@ -1570,6 +1570,11 @@ class PendingProblemOrder(CheckedProblemOrder): first_call_deadline: EventTime = field( default_factory=lambda: RecordedEventTime(time.time() + 120)) + def require_wording(self) -> None: + """Check fresh order prose, never reinterpret stored history or shell commands.""" + for text in (*self.edits, *self.banned, self.report, *self.run.instructions()): + DirectiveText.require(text) + def reissued(self, at: float) -> PendingProblemOrder: window = (self.first_call_deadline.recorded_at("A first-call deadline") - self.at.recorded_at("An order")) @@ -1952,6 +1957,7 @@ def apply(self, current: Todo) -> Todo: current.require_revision(self.expected_revision) self.actor.require_may_change(current) if self.order != current.order: + self.order.require_wording() self.order.require_writable() assigned = (current if current.assignment == proposed else current.state.assign(current, proposed, self.traced)) @@ -2751,6 +2757,8 @@ def create( identity, assignee = _participant(creator), _thread(owner) deadline.require_deadline() goal.require_reference() + DirectiveText.require(text) + order.require_wording() order.require_writable() proposed = Todo( id=todo_id, From 60710ee12f9bc25b94ce9868c75ca3192b5b0668 Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 19 Oct 2026 23:39:23 -0400 Subject: [PATCH 2/5] Cover fresh native refusal, direct assignments, and readable old orders --- tests/test_commander_problem_orders.py | 39 ++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/test_commander_problem_orders.py b/tests/test_commander_problem_orders.py index 2bc690a82d..9e556b6e0b 100644 --- a/tests/test_commander_problem_orders.py +++ b/tests/test_commander_problem_orders.py @@ -61,6 +61,45 @@ def create(commander, problem, command='printf 1', expected=1, deadline=1000, ow return comms.goals.todos.get(problem) +@pytest.mark.parametrize('text', ( + 'Repair the store; thank you for fixing it', + 'Repairing the store is optional', + 'Can you repair the store', +)) +def test_new_native_order_refuses_soft_prose_before_reservation(commander, text): + comms, caller = commander + original = comms.registry.require('worker').goal + checked = replace(order(comms.root, 'soft-order'), edits=(text,)) + # Old orders still decode: policy belongs to new input, not stored history. + assert FieldCodec.decode(PendingProblemOrder, FieldCodec.encode(checked)) == checked + with pytest.raises(ValueError, match='Soft wording'): + CommsTodoCreateTool(owner='worker', order=CreateProblemGoalAction( + problem='Psoft', order=checked, request='request', + deadline=RecordedEventTime(1000), commander='lead', + repo='OpenHCSDev/agent-comms', text='Repair the store', + )).apply(comms, caller) + assert comms.goals.todos.list() == () + assert comms.registry.require('worker').goal == original + + +@pytest.mark.parametrize('field', ('edits', 'banned', 'report')) +def test_direct_assignment_uses_the_same_wording_family(commander, field): + comms, _ = commander + current = create(commander, 'P1') + text = 'Please repair the store' + value = text if field == 'report' else (text,) + checked = replace(order(comms.root, 'replacement'), **{field: value}) + with pytest.raises(ValueError, match='Soft wording'): + comms.goals.todos.assign( + 'P1', expected_revision=current.revision, + owner=comms.registry.require('worker'), + assigner=comms.registry.require('lead').incarnation, + generation=current.held().generation, goal=current.goal, + deadline=current.deadline, order=checked, + ) + assert comms.goals.todos.get('P1') == current + + def working(comms): """The working claim a delivery stands on: a screenshot that exists.""" screenshot = Path(comms.root) / 'working.png' From 923c12eb6a990745c8a0e6a0de7fd9884f0c987a Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 19 Oct 2026 23:41:16 -0400 Subject: [PATCH 3/5] Verify direct creation and application-run prose share native refusal --- tests/test_commander_problem_orders.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/test_commander_problem_orders.py b/tests/test_commander_problem_orders.py index 9e556b6e0b..558add2d4c 100644 --- a/tests/test_commander_problem_orders.py +++ b/tests/test_commander_problem_orders.py @@ -78,16 +78,18 @@ def test_new_native_order_refuses_soft_prose_before_reservation(commander, text) deadline=RecordedEventTime(1000), commander='lead', repo='OpenHCSDev/agent-comms', text='Repair the store', )).apply(comms, caller) - assert comms.goals.todos.list() == () + assert comms.goals.todos.list('OpenHCSDev/agent-comms') == () assert comms.registry.require('worker').goal == original -@pytest.mark.parametrize('field', ('edits', 'banned', 'report')) +@pytest.mark.parametrize('field', ('edits', 'banned', 'report', 'run')) def test_direct_assignment_uses_the_same_wording_family(commander, field): comms, _ = commander current = create(commander, 'P1') text = 'Please repair the store' value = text if field == 'report' else (text,) + if field == 'run': + value = ApplicationOrderRun('Run the actual application and please show the result') checked = replace(order(comms.root, 'replacement'), **{field: value}) with pytest.raises(ValueError, match='Soft wording'): comms.goals.todos.assign( @@ -100,6 +102,19 @@ def test_direct_assignment_uses_the_same_wording_family(commander, field): assert comms.goals.todos.get('P1') == current +def test_direct_creation_checks_defect_prose_before_writing(commander): + comms, _ = commander + lead = comms.registry.require('lead') + with pytest.raises(ValueError, match='Soft wording'): + comms.goals.todos.create( + 'Psoft', 'OpenHCSDev/agent-comms', 'Please repair the store', + creator=lead, owner=comms.registry.require('worker'), generation='ongoing', + deadline=RecordedEventTime(1000), order=order(comms.root, 'soft-order'), + goal=GoalRef(lead.name, lead.created_at, 'request'), + ) + assert comms.goals.todos.list('OpenHCSDev/agent-comms') == () + + def working(comms): """The working claim a delivery stands on: a screenshot that exists.""" screenshot = Path(comms.root) / 'working.png' From 5039de283c2e67932f095142a8994a87189452fd Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Tue, 20 Oct 2026 00:06:02 -0400 Subject: [PATCH 4/5] P233: give the Claude transport fixture its session-owned subscription --- tests/test_turn_digests.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_turn_digests.py b/tests/test_turn_digests.py index d874f64f71..6f5bad746b 100644 --- a/tests/test_turn_digests.py +++ b/tests/test_turn_digests.py @@ -82,8 +82,9 @@ def test_native_turns_keep_original_calls_results_and_input_identity(tmp_path): def test_claude_file_transport_reaches_the_same_cli_and_tool_answer(tmp_path, monkeypatch): + from agent_comms.child_process import ProcessIdentity from agent_comms.cli_bridges.claude import ClaudeBridge - from agent_comms.cli_bridges.shared import Binding, Options, Watch + from agent_comms.cli_bridges.shared import Binding, Options, Serve, Watch from agent_comms.wire_watch import WatchSource, WireWatch home = tmp_path / 'home' @@ -105,7 +106,10 @@ def test_claude_file_transport_reaches_the_same_cli_and_tool_answer(tmp_path, mo store = Binding.open(state) db = store.__enter__() binding = Binding(str(comms.root), 'claude', 'session') - bridge = ClaudeBridge(Options(Watch(), binding, state), binding, db) + with pytest.raises(ValueError, match='does not own a live transcript subscription'): + Watch().transcript_source() + operation = Serve(owner=ProcessIdentity.capture(os.getpid()), transcript=path) + bridge = ClaudeBridge(Options(operation, binding, state), binding, db) async def publish(): task = asyncio.create_task(bridge.publish_transcript()) From d555a79cb869a8760b91cbd4a43eeb5835842f97 Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Tue, 20 Oct 2026 00:25:34 -0400 Subject: [PATCH 5/5] Own order wording checks on CheckedProblemOrder, where every field they read is declared Co-Authored-By: Claude Opus 5.5 --- src/agent_comms/todos.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/agent_comms/todos.py b/src/agent_comms/todos.py index 9b4bdabec2..7f6577ab30 100644 --- a/src/agent_comms/todos.py +++ b/src/agent_comms/todos.py @@ -1428,6 +1428,11 @@ class CheckedProblemOrder(ProblemOrder, declared_name=None): def checked(self) -> CheckedProblemOrder: return self + def require_wording(self) -> None: + """Check fresh order prose, never reinterpret stored history or shell commands.""" + for text in (*self.edits, *self.banned, self.report, *self.run.instructions()): + DirectiveText.require(text) + id: str owner: FileLine check: OrderCheck @@ -1581,11 +1586,6 @@ class PendingProblemOrder(CheckedProblemOrder): first_call_deadline: EventTime = field( default_factory=lambda: RecordedEventTime(time.time() + 120)) - def require_wording(self) -> None: - """Check fresh order prose, never reinterpret stored history or shell commands.""" - for text in (*self.edits, *self.banned, self.report, *self.run.instructions()): - DirectiveText.require(text) - def reissued(self, at: float) -> PendingProblemOrder: window = (self.first_call_deadline.recorded_at("A first-call deadline") - self.at.recorded_at("An order"))