Skip to content
2 changes: 1 addition & 1 deletion src/agent_comms/goals.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ def goal_text(self, goal: Goal) -> str:

def progress(self, goal: Goal, todos: Sequence[Todo]) -> str:
"""Each problem's order state and latest verdict, as Core records them on its Todo."""
return "\n".join(" · ".join((f"{todo.id}: {todo.order.line}",
return "\n".join(" · ".join((f"{todo.id}: {todo.order.progress_line()}",
*(verdict.line for verdict in todo.verdicts[-1:])))
for todo in todos)

Expand Down
56 changes: 51 additions & 5 deletions src/agent_comms/todos.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,38 @@
@dataclass(frozen=True, kw_only=True)
class OrderDelivery(DeclaredFamily, affix="Delivery"):
at: EventTime = field(default_factory=UnrecordedEventTime)
checked_kind: ClassVar[type[OrderDelivery]]

@property
def work(self) -> str:
return "delivered work"

@property
def check_line(self) -> str:
"""A delivery saved before Core recorded the Check's print says so."""
return "no print recorded"

async def run(self, check, *command):
result = await BoundedRun.run(tuple(command), cwd=check.directory, timeout=120)
if result.outcome != ExitedOutcome(0):
raise ValueError(result.stderr.decode(errors="replace") or "Delivery source check failed.")
return result.stdout.decode(errors="replace")

async def passed(self, check: OrderCheck) -> str:
await self.require_source(check)
result = await self.checked(check)
check.result.require(result)
return result.stdout.decode(errors="replace").strip()

async def verified(self, check: OrderCheck, deletes: OrderDeletion) -> OrderDelivery:
"""The order's check and every count its deletion declares run on the delivered source."""
for each in (check, *deletes.checks):
await self.require_source(each)
each.result.require(await self.checked(each))
return replace(self, at=await self.delivery_time(check))
"""The order's check and every count its deletion declares run on the delivered source;
the checked member records what the order's check printed, never what a report says."""
printed = await self.passed(check)
for each in deletes.checks:
await self.passed(each)
carried = {item.name: getattr(self, item.name) for item in fields(self)}
return self.checked_kind(
**carried | {"at": await self.delivery_time(check), "printed": printed})

@abstractmethod
async def require_source(self, check): ...
Expand Down Expand Up @@ -132,6 +147,16 @@ async def require_source(self, check: OrderCheck) -> None:
f"Delivered commit {self.delivered} is on no origin branch; push it first.")


@dataclass(frozen=True, kw_only=True)
class CheckedCommitDelivery(CommitDelivery, declared_name="checked_commit"):
"""A commit delivery Core verified, with what the order's Check printed on it."""
printed: str

@property
def check_line(self) -> str:
return f"Check printed {self.printed!r}"


@dataclass(frozen=True, kw_only=True)
class OperationDelivery(OrderDelivery):
async def require_source(self, check):
Expand All @@ -144,6 +169,20 @@ async def delivery_time(self, check):
return EventTime.capture(time.time())


@dataclass(frozen=True, kw_only=True)
class CheckedOperationDelivery(OperationDelivery, declared_name="checked_operation"):
"""An operation delivery Core verified, with what the order's Check printed."""
printed: str

@property
def check_line(self) -> str:
return f"Check printed {self.printed!r}"


CommitDelivery.checked_kind = CheckedCommitDelivery
OperationDelivery.checked_kind = CheckedOperationDelivery


@dataclass(frozen=True)
class PullRequest:
number: int
Expand Down Expand Up @@ -929,6 +968,10 @@ def __post_init__(self):

pending: ClassVar[bool]
line: ClassVar[str]

def progress_line(self) -> str:
"""This order's state on the board."""
return self.line
NAMED_FILE: ClassVar[re.Pattern] = re.compile(r"([\w./-]+\.\w+):\d+")

def require_transfer(self, todo: Todo) -> None:
Expand Down Expand Up @@ -1055,6 +1098,9 @@ class DeliveredProblemOrder(ProblemOrder):
pending: ClassVar[bool] = False
line: ClassVar[str] = "Delivered, waiting on install"

def progress_line(self) -> str:
return f"{self.line} · {self.delivery.check_line}"

def require_transfer(self, todo: Todo) -> None:
owner = (todo.assignment or todo.released[-1].previous).owner
raise ValueError(f"{owner} already has {self.delivery.work}; {owner} merges it.")
Expand Down
22 changes: 18 additions & 4 deletions tests/test_commander_problem_orders.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import pytest
from contextlib import closing
from dataclasses import replace
from dataclasses import fields, replace

from cutover.carry import CarryChain

Expand All @@ -32,7 +32,8 @@
from agent_comms.threads import Thread
from agent_comms.tool_caller import ThreadCaller
from agent_comms.tools import (CommsCorrectWordingTool, CommsSendTool, CommsFireTool, CommsOrderCheckTool, CommsOrderDoneTool, CommsOrderTool,
CommsOrderWorkTool, CommsProblemCloseTool, CommsTodoCreateTool, CommsTodoListTool, CommsTodoTool, CommsVerdictTool)
CommsOrderWorkTool, CommsProblemCloseTool, CommsTodoCreateTool, CommsTodoListTool, CommsTodoTool, CommsVerdictTool,
ThreadToolRequest)
from agent_comms.todos import (AwaitingFirstCall, VerdictAudience, OrderMessage, OrderDeletion, GoalRef, CleanHunt, CommitDelivery, FirstCall, Hunt, OrderDelivery,
ApplicationOrderRun, RecordedCommitDelivery, MechanicalOrderRun, TodoStore,
LateObservedFirstCall, OperationDelivery, OrderedHunt,
Expand Down Expand Up @@ -176,6 +177,19 @@ def test_failed_check_keeps_problem_pending(commander):
assert comms.registry.require('worker').goal.state.active


def test_done_declares_no_wrong_measure_and_the_board_shows_the_recorded_check_result(commander):
comms, caller = commander
create(commander, 'P1')
reported = {f.name for f in fields(CommsOrderDoneTool)} - {f.name for f in fields(ThreadToolRequest)}
assert reported == {'problem', 'delivery', 'claims'}
CommsOrderDoneTool(problem='P1', delivery=OperationDelivery(), claims=working(comms)).apply(comms, caller)
delivery = comms.goals.todos.get('P1').order.delivery
assert delivery.printed == '1' and FieldCodec.decode(OrderDelivery, FieldCodec.encode(delivery)) == delivery
worker = comms.registry.require('worker')
progress = GoalSegment.capture(worker, worker.goal, comms.goals).progress
assert progress == "P1: Delivered, waiting on install · Check printed '1'"


def test_close_records_evidence_once_and_does_not_depend_on_artifact_lifetime(commander):
comms, caller = commander
create(commander, 'P1')
Expand Down Expand Up @@ -358,7 +372,7 @@ def test_a_done_record_from_before_the_split_reads_its_one_commit_as_both():
old = {'kind': 'commit', 'commit': 'd012d23b4', 'at': FieldCodec.encode(RecordedEventTime(1000))}
recorded = FieldCodec.decode(OrderDelivery, old)
assert type(recorded) is RecordedCommitDelivery and recorded.delivered == recorded.commit == 'd012d23b4'
assert FieldCodec.encode(recorded) == old
assert FieldCodec.encode(recorded) == old and recorded.check_line == 'no print recorded'


def test_delivery_time_has_its_own_named_requirement():
Expand Down Expand Up @@ -1192,7 +1206,7 @@ def test_a_problem_goal_shows_only_its_live_orders_through_convert_deliver_send_
todo.assignment, 'toolu_3', reason=StrikeReason(why))).apply(comms, caller)
worker = comms.registry.require('worker')
progress = GoalSegment.capture(worker, worker.goal, comms.goals).progress
assert progress.splitlines() == ['P1: Delivered, waiting on install', f'P2: Open · Sent back: {why}']
assert progress.splitlines() == ["P1: Delivered, waiting on install · Check printed '1'", f'P2: Open · Sent back: {why}']
assert (worker.goal.text, worker.goal.progress) == (ProblemGoalOrder.text, '')
with pytest.raises(ValueError, match='block the problem.s Todo, or standby with wait_for'):
comms.goals.update_goal('worker', BlockedGoalAction(block_reason='Waiting on lead.'), actor=ModelInvocable)
Expand Down
Loading