Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9a62cf1
Exercise copied native creation assignment evidence and strikes; repa…
trissim Oct 20, 2026
1329a27
Compare real current and native refusals to hand delivered checked wo…
trissim Oct 20, 2026
303e926
Let BoundedRun own native comparison process outcomes and retirement …
trissim Oct 20, 2026
3d2758a
Preserve caller-owned cwd and sanitized environments in the bounded p…
trissim Oct 20, 2026
c0d7088
Confirm real child environment and project preservation through the s…
trissim Oct 20, 2026
0de65ce
Place checked-commit worktrees under the configured checkout owner
trissim Oct 20, 2026
a4d9ee7
Exercise current and native delivered-commit outcomes on the copied bus
trissim Oct 20, 2026
5a9cb35
Keep every copied delivery order failing until its actual run
trissim Oct 20, 2026
deed3b4
State the checked main commit and migrate the explicit check proof
trissim Oct 20, 2026
2fdae4c
Compare real command process exits without swallowing native refusals
trissim Oct 20, 2026
ad185c8
Keep child timeout and retirement outcomes in the delivery comparison
trissim Oct 20, 2026
968d6b7
Use the lifecycle-owned candidate process launcher for delivered-comm…
trissim Oct 20, 2026
b266156
Compare deletion-intent refusal and acceptance through both real deli…
trissim Oct 20, 2026
f9f65ca
Exercise current command and native entrypoints with exact delivered-…
trissim Oct 20, 2026
598a009
Let declared requests compose argument projection and let the CLI own…
trissim Oct 20, 2026
1131375
Use caller-owned identity and declared native CLI projection in delet…
trissim Oct 20, 2026
209fdd9
Bind the native CLI module at its ordinary module import boundary
trissim Oct 20, 2026
9be6bdb
Delete the other hand-built native tool argv and envelope projection
trissim Oct 20, 2026
8ca3e68
Use the shared bounded lifecycle producer and typed delivery outcomes
trissim Oct 20, 2026
93321e0
Remove codec imports made obsolete by native declaration ownership
trissim Oct 20, 2026
4e8a8c8
Pass candidate Python environment through the shared process owner ex…
trissim Oct 20, 2026
9492f6c
Require explicit project and environment at the common process boundary
trissim Oct 20, 2026
e563fb2
Use the current recorded-time owner throughout native lifecycle compa…
trissim Oct 20, 2026
805e86e
Drop the deleted commander CLI field from the lifecycle constructor
trissim Oct 20, 2026
c545e39
Keep generated deadline input in the actual CLI HH:MM grammar
trissim Oct 20, 2026
efd61ae
Use Repo.git for the complete delivered-commit fixture lifecycle
trissim Oct 20, 2026
da07d6c
Declare why disposable parity scenarios are not live phase implementa…
trissim Oct 20, 2026
41e2552
Compare judge and native Verdict on actual copied-session edits
trissim Oct 20, 2026
ec8d3e7
Verify persisted verdict call identity and the strike event
trissim Oct 20, 2026
7b4a9ec
Check the concrete verdict kind and its committed Goal transition
trissim Oct 20, 2026
6d82546
Prepare the real copied-root rule review before invoking judge
trissim Oct 20, 2026
d5071db
Require the send-back reason in the derived Goal and test the verdict…
trissim Oct 20, 2026
b29e389
Use LifecycleObservation for verdict proof and retain control input w…
trissim Oct 20, 2026
ebb756e
Use the shared bounded producer for real native edit execution
trissim Oct 20, 2026
1c32b37
Keep verdict proof on committed Goal history and correct its owner de…
trissim Oct 20, 2026
8bf3e13
Assert the failed-check strike in deletion parity without changing cu…
trissim Oct 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions plans/commander-native-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
| 5 | Board, overdue, silent, spinning | `order.py board`, `overdue`; `fleet-board.md` | `CommsTodoListTool` and the fleet tool, derived | `one_order_owner` |
| 6 | Branch hygiene | `branches-*.txt` by hand | `CommsWorkBranchesTool` | `one_order_owner` |
| 7 | Strikes and the strike limit | `order.py strike`; `strikes.jsonl` | `Strike` rows, author-declared `StrikeLimit`, one `comms_fire` | `record_plane` |
| 8 | Verdicts on a unit's tool calls | `judge.py ok`, `back`; `verdicts.jsonl` | Order `Verdict`; send-back revises the Goal and records its strike atomically | `record_plane` |
| 8 | Verdicts on a unit's tool calls | `judge.py ok`, `back`; both invoke `CommsVerdictTool` | Order `Verdict`; send-back revises the Goal and records its strike atomically | `record_plane` |
| 9 | Plain words in verdicts and strikes | judge and order checks | `PlainText` | `record_plane` |
| 10 | Refuse soft or vague orders | `order.py` | `OrderText` on every order action | `record_plane` |
| 11 | What a unit did during its order | `tool_watch.py`; `edits.jsonl`; `judge.py show` | `comms_order_work`: both transcript families by `tool_call_id` | `review_queue` |
Expand All @@ -61,7 +61,7 @@
| 28 | Measured load and self-report | `capacity.py` unit count | `commander.capacity.Load.of` reports orders, overdue, edits and forks; self-report comparison unverified | `control_plane` |
| 29 | No handoff of finished, checked work | none; scratch transfer moved P507 after its fix was pushed and checked | Transfer refuses delivered checked work; that owner merges; check is reused | `record_plane` |
| 30 | A proof is bound to the build it ran on | none; screenshots and logs are bare paths | Build run records runtime and Core/Toad revisions; close requires delivered commit | `record_plane` |
| 31 | An order states its intent as a deletion target | none; P520 renamed a decision rather than deleting it | Order owns deletion family/site/count; delivery refuses nonzero or renamed decisions | `record_plane` |
| 31 | An order states its intent as a deletion target | Current order command with a declared FamilyDeletion | CommsTodoCreateTool and CommsOrderDoneTool check the delivered family count | `record_plane` |
| 32 | Where units' time goes, classified | none; activity.log lacks durations and turn boundaries | `UnitTime`: model, build/check/test/shell, dependency/commander waits and idle; classify large waits | `commander_page`, `control_plane` |
| 33 | Order progress beside its deadline; the unit never sees a due time | `order.py board`, `overdue`, `due`, `send`; `rotate.py` | Derived `OrderProgress`; merged work draws no strike; first commit 10 min, later progress 15 min STALL; units see no due time | `one_order_owner`, `control_plane` |
| 34 | Several commanders on one machine and bus, one per project | none; a second commander.json install overwrites the first | Registry commander/project ownership; caller from --from; scoped orders, strikes, load, board, alarms and forks; raw-send guard; duplicate project commander refused | `one_order_owner`, `cutover` |
Expand Down
4 changes: 2 additions & 2 deletions src/agent_comms/commander/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -742,8 +742,8 @@ def due(self, pid: str, at: str, why: str) -> None:
moved = RecordedEventTime(time.time()).at_clock(at)
self.call(CommsChangeProblemTool(problem=pid, operation=AssignTodo(
todo.revision, todo.assignment, moved, todo.order, todo.goal)))
self.on_record(f'{time.strftime("%Y-%m-%d %H:%M")} {todo.holder} ({pid} order {todo.order.id} due '
f'{todo.deadline.clock()} moved to {moved.clock()}): {why}', 'Deadline',
self.on_record(f'{time.strftime("%Y-%m-%d %H:%M")} {todo.holder} ({pid} order {todo.order.id} '
f'deadline changed): {why}', 'Deadline',
VerdictAudience.of(todo.held().owner, todo.goal))
print(f'{pid}: due {moved.clock()}')

Expand Down
295 changes: 295 additions & 0 deletions src/agent_comms/commander_parity/deletion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
"""Deletion intent parity through real orders, pushed commits and native delivery checks."""

from __future__ import annotations

import contextlib
import io
import json
import shlex
import sys
import tempfile
from abc import ABC, abstractmethod
from dataclasses import dataclass, replace
from pathlib import Path

from ..change_report import FileLine
from ..child_process import CountPrinted, ExitedOutcome
from ..commander.order import OrderFile
from ..field_codec import FieldCodec
from ..goal_actions import CreateProblemGoalAction
from ..goals import CountCheckResult, OrderCheck
from ..plan_phases import RecordPlanePhase
from ..progress_claims import MergedClaim
from ..repo import Repo
from ..todos import (
CheckedCommitDelivery,
CommitDelivery,
DeliveredProblemOrder,
FailedCheckStrike,
FamilyDeletion,
MechanicalOrderRun,
Todo,
)
from ..tool_caller import ThreadCaller
from ..tools import CommsOrderDoneTool, CommsTodoCreateTool, ProblemOrdered
from . import ParityComparison, ParityRow
from .lifecycle import LifecycleObservation, LifecycleRun, MeasuredCommander


@dataclass(frozen=True)
class DeletionComparison(ParityComparison):
current: LifecycleObservation
native: LifecycleObservation

@property
def passed(self) -> bool:
return (
self.current.passed and self.native.passed and self.current.checks == self.native.checks
)

def describe(self) -> str:
return f"current: {self.current.describe()}; native: {self.native.describe()}"

@classmethod
def read(cls) -> DeletionComparison:
return cls(CurrentDeletionPath.read(), NativeDeletionPath.read())


class DeletionPath(ABC):
"""Both adapters exercise the same scenario on independent LifecycleRun copies."""

@classmethod
@abstractmethod
def send(cls, run: LifecycleRun, document: OrderFile) -> Todo: ...

@classmethod
@abstractmethod
def delivery_command(cls, run: LifecycleRun, todo: Todo, commit: str) -> tuple[str, ...]: ...

@staticmethod
def commit(repository: Path, text: str, message: str) -> str:
(repository / "family.py").write_text(text)
Repo.git(repository, "add", "family.py")
Repo.git(repository, "commit", "-qm", message)
Repo.git(repository, "push", "-q", "origin", "main")
return Repo.git(repository, "rev-parse", "HEAD").strip()

@classmethod
def read(cls) -> LifecycleObservation:
with LifecycleRun.open() as original:
# Keep source checkouts out of the copied worker's storage accounting.
# Limits remain unchanged; all detached checks use this persistent owned base.
rule = original.commander.comms.registry.storage_rule
with tempfile.TemporaryDirectory(
prefix="parity-deletion-", dir=rule.checkout_base
) as checkouts:
root = original.commander.root
(root / "storage.json").write_text(
json.dumps(FieldCodec.encode(replace(rule, checkout_base=Path(checkouts))))
)
(root / "commander.json").write_text(original.commander.model_dump_json())
run = replace(original, commander=MeasuredCommander.read_root(root))
return cls.exercise(run)

@classmethod
def exercise(cls, run: LifecycleRun) -> LifecycleObservation:
repository = Path(run.commander.comms.registry.require(run.worker).worktree)
remote = repository.parent / "deletion-origin.git"
Repo.git(repository, "init", "-q", "-b", "main")
Repo.git(repository, "config", "user.name", "Deletion parity")
Repo.git(repository, "config", "user.email", "deletion-parity@localhost")
Repo.git(repository, "init", "--bare", "-q", "--initial-branch=main", str(remote))
Repo.git(repository, "remote", "add", "origin", str(remote))
family = "class RetiredDecision:\n pass\n\n"
cls.commit(
repository,
family + "def decisions():\n return [RetiredDecision()]\n",
"Declare the obsolete decision family and its caller",
)
count = OrderCheck(
str(repository),
shlex.join(
(
sys.executable,
"-c",
"import ast;from pathlib import Path;"
"print(sum(isinstance(node,ast.ClassDef) for node in "
"ast.walk(ast.parse(Path('family.py').read_text()))))",
)
),
CountCheckResult(0),
)
deletion = FamilyDeletion(
"obsolete decision family", FileLine(str(repository / "family.py"), 1), count
)
initial = OrderFile.read(run.document.read_text())
order = replace(
initial.order,
check=OrderCheck(
str(repository),
shlex.join((sys.executable, "-c", "import family;assert not family.decisions()")),
),
run=MechanicalOrderRun(),
deletes=deletion,
)
document = replace(initial, order=order)
run.document.write_text(json.dumps(FieldCodec.encode(document)))
output = io.StringIO()
with contextlib.redirect_stdout(output):
before = cls.send(run, document)
goal_threads = (run.commander.commander, run.worker)
before_goals = tuple(
run.commander.comms.registry.require(name).goal for name in goal_threads
)
partial = cls.commit(
repository,
family + "def decisions():\n return []\n",
"Repair the caller but leave one obsolete family site",
)
refused = run.invoke_command(
cls.delivery_command(run, before, partial), cwd=repository, env=run.python_environment()
)
after_refusal = run.commander.comms.goals.todos.get(before.id)
remaining = count.recorded(partial, str(repository))
after_refusal_goals = tuple(
run.commander.comms.registry.require(name).goal for name in goal_threads
)
failed_check = False
strike_only = False
match after_refusal.strikes[len(before.strikes) :]:
case (FailedCheckStrike() as strike,):
failed_check = strike == FailedCheckStrike.make(
before, strike.at.recorded_at("The failed delivery"), strike.reason
) and strike.reason.startswith(
f"reported done at {partial} but its order check failed: "
f"The order check is 1 away from {count.result}."
)
strike_only = after_refusal == replace(
before, strikes=(*before.strikes, strike), revision=before.revision + 1
)
complete = cls.commit(
repository,
"def decisions():\n return []\n",
"Delete the last obsolete family declaration",
)
accepted = run.invoke_command(
cls.delivery_command(run, before, complete),
cwd=repository,
env=run.python_environment(),
)
after_acceptance = run.commander.comms.goals.todos.get(before.id)
zero = count.recorded(complete, str(repository))
primary = order.check.recorded(partial, str(repository))
delivered = after_acceptance.order
checks = (
("native order retained deletion intent", before.order.checked().deletes == deletion),
(
"partial commit passed primary check",
bool(primary) and order.check.result.distance(primary[0]) == CountPrinted(0),
),
(
"partial commit retained one family site",
bool(remaining) and count.result.distance(remaining[0]) == CountPrinted(1),
),
("nonzero delivery refused", refused.outcome == ExitedOutcome(1)),
(
"refusal preserved pending order and assignment",
after_refusal.order == before.order
and after_refusal.assignment == before.assignment
and after_refusal.pending,
),
(
"refusal preserved order and thread goals",
after_refusal.goal == before.goal and after_refusal_goals == before_goals,
),
("partial delivery recorded its precise failed-check strike", failed_check),
("refusal changed only the failed-check strike and revision", strike_only),
(
"zero count checked on delivered commit",
bool(zero) and count.result.distance(zero[0]) == CountPrinted(0),
),
(
"zero delivery accepted",
accepted.outcome == ExitedOutcome(0) and not after_acceptance.pending,
),
(
"accepted delivery names the zero-site commit",
isinstance(delivered, DeliveredProblemOrder)
and isinstance(delivered.delivery, CheckedCommitDelivery)
and delivered.delivery.delivered_commit == complete,
),
("accepted delivery retained deletion intent", delivered.checked().deletes == deletion),
)
return LifecycleObservation(
CommsOrderDoneTool.__qualname__,
len((refused, accepted)),
checks,
output.getvalue() + f"partial={partial}; zero={complete}; "
f"refused={refused.outcome.summary}: "
f"{refused.stdout.decode(errors='replace')}{refused.stderr.decode(errors='replace')}; "
f"accepted={accepted.outcome.summary}: "
f"{accepted.stdout.decode(errors='replace')}{accepted.stderr.decode(errors='replace')}",
)


class CurrentDeletionPath(DeletionPath):
@classmethod
def send(cls, run: LifecycleRun, document: OrderFile) -> Todo:
return run.create()

@classmethod
def delivery_command(cls, run: LifecycleRun, todo: Todo, commit: str) -> tuple[str, ...]:
return (
sys.executable,
"-c",
"import sys;from agent_comms.commander.order import Commander,DoneVerb;"
"DoneVerb(unit=sys.argv[2],message=sys.argv[3],sha=sys.argv[4]).run("
"Commander.read_root(sys.argv[1]))",
str(run.commander.root),
run.worker,
todo.order.checked().id,
commit,
)


class NativeDeletionPath(DeletionPath):
@classmethod
def send(cls, run: LifecycleRun, document: OrderFile) -> Todo:
result = run.commander.call(
CommsTodoCreateTool(
owner=run.worker,
order=CreateProblemGoalAction(
problem="P1",
repo=Repo.of(Path(document.order.check.directory)).github,
text=document.defect,
order=document.order,
request=document.order.request(
run.commander.comms.registry.require(run.commander.commander), ("P1",)
),
deadline=run.deadline,
commander=run.commander.commander,
),
)
)
if not isinstance(result, ProblemOrdered):
raise TypeError("Native creation returned no ordered problem")
return result.todo

@classmethod
def delivery_command(cls, run: LifecycleRun, todo: Todo, commit: str) -> tuple[str, ...]:
request = CommsOrderDoneTool(
problem=todo.id,
order_id=todo.order.checked().id,
delivery=CommitDelivery(commit=commit, delivered_commit=commit),
claims=(MergedClaim(commit),),
)
return request.cli_command(ThreadCaller(run.commander.commander)).argv(run.commander.root)


class DeletionTargetParityRow(ParityRow):
number = 31
capability = "An order states its intent as a deletion target"
current = "Current order command with a declared FamilyDeletion"
native = "CommsTodoCreateTool and CommsOrderDoneTool check the delivered family count"
phases = (RecordPlanePhase,)
comparison = DeletionComparison.read
Loading
Loading