Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ecacf81
P433: derive commander order refusals from standing rules
trissim Oct 21, 2026
ce22c97
P433: centralize unit state derivation and review-head ceiling
trissim Oct 21, 2026
61a327d
Merge remote-tracking branch 'origin/main' into fix/p433-durable-pings
trissim Oct 21, 2026
8a43ca5
P433: replace heartbeat walls with typed durable decision packets
trissim Oct 21, 2026
9083dd2
Merge remote-tracking branch 'origin/main' into fix/p433-durable-pings
trissim Oct 21, 2026
ea987af
P433: own accepted and rejected head review on one verdict family
trissim Oct 21, 2026
283fe03
Merge remote-tracking branch 'origin/main' into fix/p433-durable-pings
trissim Oct 21, 2026
9e46ab7
P433: judge linked PR heads and refuse overdue unreviewed work
trissim Oct 21, 2026
c85563a
Merge remote-tracking branch 'origin/main' into fix/p433-durable-pings
trissim Oct 21, 2026
cf7999f
P433: migrate parity producers to linked head verdicts
trissim Oct 21, 2026
f6ff509
Merge remote-tracking branch 'origin/main' into fix/p433-durable-pings
trissim Oct 21, 2026
ea07419
Preserve plan discovery and typed copied head-review fixtures
trissim Oct 21, 2026
91923fd
Merge current main while keeping head verdicts and shared fixture owners
trissim Oct 21, 2026
2691dd2
Read heartbeat from its owner and cover reviewed native edit evidence
trissim Oct 21, 2026
9d24b01
Merge remote-tracking branch 'origin/main' into fix/p433-durable-pings
trissim Oct 21, 2026
ed30a53
Integrate P439 owners before restoring P433 notifications
trissim Oct 23, 2026
58b6d12
Bring P433 integration baseline onto current main
trissim Oct 23, 2026
d40e1ac
Checkpoint durable unit notifications on current state owners
trissim Oct 23, 2026
75c07f3
Merge remote-tracking branch 'origin/main' into fix/p433-durable-pings
trissim Oct 23, 2026
53dd242
Persist commander rule decisions and recipient pass completion
trissim Oct 23, 2026
3cca8f0
Merge remote-tracking branch 'origin/main' into fix/p433-durable-pings
trissim Oct 23, 2026
f12d495
Persist optional-order head verdicts and refuse overdue reviews
trissim Oct 23, 2026
4aecd04
Merge remote-tracking branch 'origin/main' into fix/p433-durable-pings
trissim Oct 23, 2026
412df2b
Exercise orderless judge dispatch and current notification frames
trissim Oct 23, 2026
71455e9
Merge remote-tracking branch 'origin/main' into fix/p433-durable-pings
trissim Oct 23, 2026
96ad2d0
Merge remote-tracking branch 'origin/main' into HEAD
trissim Oct 23, 2026
9ca432f
Carry stored PR-head reviews during install and gate phase four on th…
trissim Oct 23, 2026
baebd60
Merge remote-tracking branch 'origin/main' into HEAD
trissim Oct 23, 2026
ae734e3
Keep the one-shot head review carry imports minimal
trissim Oct 23, 2026
9fa56f5
Merge main and keep durable heartbeat on CommanderService
trissim Oct 23, 2026
f678ee5
Merge remote-tracking branch 'origin/main' into HEAD
trissim Oct 23, 2026
328a7ac
Publish every current commander observation and remove the duplicate …
trissim Oct 23, 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
218 changes: 147 additions & 71 deletions src/agent_comms/commander/judge.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
the commander to that unit. Nothing here judges code; it only refuses to let an edit pass unread.
"""

from __future__ import annotations

import hashlib
import json
import os
Expand All @@ -24,19 +26,26 @@
import time
from typing import Annotated
from abc import abstractmethod
from dataclasses import asdict, dataclass, field
from dataclasses import asdict, dataclass
from functools import cached_property


from agent_comms.comms import Comms
from agent_comms.change_report import FileLine
from agent_comms.field_codec import Argument, FieldCodec
from agent_comms.declared_family import DeclaredFamily
from agent_comms.head_review import PullRequestHead
from agent_comms.repo import Repo
from agent_comms.todos import (
AcceptedVerdict,
BluntText,
DirectiveText,
DroppedVerdict,
PeerAcceptedVerdict,
ReviewedHeadVerdict,
RejectedHeadVerdict,
HeadVerdict,
HeadReviewOrder,
SendBackVerdict,
StrikeReason,
SupersededVerdict,
Expand Down Expand Up @@ -145,26 +154,92 @@ def run(self, ctx: order.Commander) -> None:
alarm(ctx)


@dataclass(frozen=True, kw_only=True)
class HeadVerb(JudgeVerb):
help = ("accept an order's reviewed PR head: every edit before it is judged and "
"agent-comms/review is posted on the commit")
problem: Annotated[str, Argument(flags=("problem",), options={"metavar": "PROBLEM"})]
head: Annotated[str, Argument(flags=("head",), options={"metavar": "SHA"})]
serves: Annotated[
str, Argument(options={"help": "exact words of the order the head carries out"})
]
class ReviewSubject(DeclaredFamily):
"""Decode the CLI's mutually exclusive selectors once, then keep the selected subject."""

def run(self, ctx: order.Commander) -> None:
held = [todo for todo in ctx.held_orders() if todo.id == self.problem]
if not held:
sys.exit(f'Refused: {self.problem} is not an order this commander holds')
todo, = held
verdict = ReviewedHeadVerdict(todo.revision, ctx.actor_identity, todo.held(),
quote=self.serves, head=self.head)
ctx.call(CommsHeadVerdictTool(problem=todo.id, operation=verdict))
@classmethod
def from_options(cls, options: HeadVerdictVerb, ctx: order.Commander) -> ReviewSubject:
matches = tuple(member for member in cls.members_with(cls) if member.selected(options))
if len(matches) != 1:
sys.exit('Refused: select either edit IDs or --pr')
member, = matches
return member.decode_options(options, ctx)

@classmethod
@abstractmethod
def selected(cls, options: HeadVerdictVerb) -> bool: ...

@classmethod
@abstractmethod
def decode_options(cls, options: HeadVerdictVerb, ctx: order.Commander) -> ReviewSubject: ...

@abstractmethod
def run(self, options: VerdictVerb, ctx: order.Commander) -> None: ...


@dataclass(frozen=True)
class EditReview(ReviewSubject):
ids: tuple[str, ...]

@classmethod
def selected(cls, options: HeadVerdictVerb) -> bool:
return bool(options.ids)

@classmethod
def decode_options(cls, options: HeadVerdictVerb, ctx: order.Commander) -> EditReview:
if options.head or options.repo:
sys.exit('Refused: --head and --repo belong to --pr, not edit IDs')
return cls(options.ids)

def run(self, options: VerdictVerb, ctx: order.Commander) -> None:
if not self.ids:
sys.exit('Refused: select at least one edit ID:CODE')
orders = {todo.id: todo for todo in ctx.held_orders()
if not options.problem or todo.id == options.problem}
edits = {edit.id: edit for edit in Edit.unjudged(ctx.comms, ctx.commander)
if edit.problem in orders}
ids = [token.split(':')[0] for token in self.ids]
if missing := [i for i in ids if i not in edits]:
sys.exit(f'Refused: unknown edit ids {missing}')
if unread := [token for token in self.ids
if token != f'{token.split(":")[0]}:{edits[token.split(":")[0]].code()}']:
sys.exit(f'Refused: wrong or missing read codes {unread}; read each edit to its "=== end" line')
for i in ids:
if not any(line in edits[i].text() for line in options.lines):
sys.exit(f'Refused: none of your --line quotes is in edit {i}; judge it by its own text')
selected = {i: orders[edits[i].problem] for i in ids}
options.check_edits([edits[i] for i in ids])
for i, todo in selected.items():
options.operation(ctx, todo, edits[i]).prepare(ctx.comms.goals, todo).apply(todo)
current = {todo.id: todo for todo in selected.values()}
for i, todo in selected.items():
result = ctx.call(CommsVerdictTool(problem=todo.id,
operation=options.operation(ctx, current[todo.id], edits[i])))
current[todo.id] = result.todo
left = len(Edit.unjudged(ctx.comms, ctx.commander))
print(f'head {self.head} accepted on {todo.id}; {left} still unjudged')
print(f'{options.declared_name}: {len(ids)} edits; {left} still unjudged')


@dataclass(frozen=True)
class PullRequestReview(ReviewSubject):
request: PullRequestHead

@classmethod
def selected(cls, options: HeadVerdictVerb) -> bool:
return bool(options.pr)

@classmethod
def decode_options(cls, options: HeadVerdictVerb, ctx: order.Commander) -> PullRequestReview:
return cls(PullRequestHead(Repo.selected(ctx.repos, options.repo).github,
options.pr, options.head))

def run(self, options: HeadVerdictVerb, ctx: order.Commander) -> None:
options.check_edits([])
linked = HeadReviewOrder.for_request(ctx.held_orders(), self.request, options.serves,
options.head_evidence(), options.problem)
ctx.call(CommsHeadVerdictTool(request=self.request,
operation=options.head_operation(), order=linked))
print(f'PR #{self.request.pr} head {self.request.head}: {options.declared_name}')


@dataclass(frozen=True, kw_only=True)
Expand All @@ -174,8 +249,8 @@ class VerdictVerb(JudgeVerb):

ids: Annotated[
tuple[str, ...],
Argument(flags=("ids",), options={"nargs": "+", "metavar": "ID:CODE"}),
]
Argument(flags=("ids",), options={"nargs": "*", "metavar": "ID:CODE"}),
] = ()
why: str
problem: Annotated[
str,
Expand All @@ -191,50 +266,46 @@ class VerdictVerb(JudgeVerb):
"help": "an exact diff line; repeat for each",
},
),
]
] = ()

@abstractmethod
def operation(self, ctx: order.Commander, todo: Todo, edit: Edit) -> ToolCallVerdict: ...

@abstractmethod
def check_edits(self, edits: list[Edit]) -> None: ...

def subject(self, ctx: order.Commander) -> ReviewSubject:
return EditReview(self.ids)

def run(self, ctx: order.Commander) -> None:
if len(self.why.split()) < 6:
sys.exit('Refused: state which intent the edit serves or violates, and why (6+ words).')
orders = {todo.id: todo for todo in ctx.held_orders()
if not self.problem or todo.id == self.problem}
edits = {edit.id: edit for edit in Edit.unjudged(ctx.comms, ctx.commander)
if edit.problem in orders}
ids = [token.split(':')[0] for token in self.ids]
if missing := [i for i in ids if i not in edits]:
sys.exit(f'Refused: unknown edit ids {missing}')
if unread := [token for token in self.ids
if token != f'{token.split(":")[0]}:{edits[token.split(":")[0]].code()}']:
sys.exit(f'Refused: wrong or missing read codes {unread}; read each edit to its "=== end" line')
for i in ids:
if not any(line in edits[i].text() for line in self.lines):
sys.exit(f'Refused: none of your --line quotes is in edit {i}; judge it by its own text')
selected = {i: orders[edits[i].problem] for i in ids}
self.check_edits([edits[i] for i in ids])
# Validate the whole batch through the operation owner before its first write.
for i, todo in selected.items():
self.operation(ctx, todo, edits[i]).prepare(ctx.comms.goals, todo).apply(todo)
current = {todo.id: todo for todo in selected.values()}
for i, todo in selected.items():
result = ctx.call(CommsVerdictTool(problem=todo.id,
operation=self.operation(ctx, current[todo.id], edits[i])))
current[todo.id] = result.todo
left = len(Edit.unjudged(ctx.comms, ctx.commander))
print(f'{self.declared_name}: {len(ids)} edits; {left} still unjudged')
self.subject(ctx).run(self, ctx)


@dataclass(frozen=True, kw_only=True)
class OkVerb(VerdictVerb):
help = "the edit serves the order"
serves: Annotated[
str, Argument(options={"help": "exact words of the order"})
]
class HeadVerdictVerb(VerdictVerb, declared_name=None):
pr: Annotated[int, Argument(options={"help": "review this PR instead of individual edits"})] = 0
head: Annotated[str, Argument(options={"help": "the exact reviewed SHA; required with --pr"})] = ""
repo: Annotated[str, Argument(options={"help": "registered repository name; optional for one repo"})] = ""
serves: Annotated[str, Argument(options={"help": "exact words of the linked order"})] = ""

def subject(self, ctx: order.Commander) -> ReviewSubject:
return ReviewSubject.from_options(self, ctx)

def head_evidence(self) -> tuple[FileLine, ...]:
return ()

@abstractmethod
def head_operation(self) -> HeadVerdict: ...


@dataclass(frozen=True, kw_only=True)
class OkVerb(HeadVerdictVerb):
help = "the edit or exact PR head is accepted"

def head_operation(self) -> HeadVerdict:
return ReviewedHeadVerdict()
no_owner: Annotated[
str,
Argument(
Expand Down Expand Up @@ -296,13 +367,8 @@ def check_edits(self, edits: list[Edit]) -> None:
pass

@dataclass(frozen=True, kw_only=True)
class BackVerb(VerdictVerb):
help = "send the edit back; Core publishes the recorded reason once"
# A bare annotation would inherit VerdictVerb's default; field() keeps it required.
problem: Annotated[
str,
Argument(options={"metavar": "PROBLEM", "help": "required problem id from order board"}),
] = field()
class BackVerb(HeadVerdictVerb):
help = "reject the edit or exact PR head; Core records its reason once"
roast: Annotated[
str, Argument(options={"help": "blunt, why it is stupid, with because"})
]
Expand All @@ -326,28 +392,38 @@ class BackVerb(VerdictVerb):
str, Argument(options={"help": "PATH:LINE of the declaration that owns it"})
]

@cached_property
def slop(self) -> Slop:
return FieldCodec.decode(Slop, dict(pattern=self.pattern, fact=self.fact,
at=self.at, derive=self.derive, by=self.by))

def operation(self, ctx: order.Commander, todo: Todo, edit: Edit) -> SendBackVerdict:
return SendBackVerdict(todo.revision, ctx.actor_identity,
todo.held(), edit.id,
reason=StrikeReason("\n".join((
self.why, self.roast,
*(f" {line.strip()}" for line in self.lines),
self.slop().render(), f"What replaces it: {self.fix}",
))),
evidence=(FileLine.from_wire(self.at),))

def check_edits(self, edits: list[Edit]) -> None:
@cached_property
def rejection_reason(self) -> StrikeReason:
if len(self.roast.split()) < 12 or not re.search(r'\bbecause\b', self.roast):
sys.exit('Refused: --roast needs 12+ words with because, naming the duplicated fact and its owner')
if len(self.fix.split()) < 8:
sys.exit('Refused: --fix needs 8+ words naming the owner and replacing shape')
self.slop()
order.refuse(self.roast, BluntText)
order.refuse(' '.join((self.why, self.roast, self.fix)), *DirectiveText.checks())
return StrikeReason("\n".join((
self.why, self.roast,
*(f" {line.strip()}" for line in self.lines),
self.slop.render(), f"What replaces it: {self.fix}",
)))

def head_evidence(self) -> tuple[FileLine, ...]:
return (FileLine.from_wire(self.at),)

def head_operation(self) -> HeadVerdict:
return RejectedHeadVerdict(self.rejection_reason)

def operation(self, ctx: order.Commander, todo: Todo, edit: Edit) -> SendBackVerdict:
return SendBackVerdict(todo.revision, ctx.actor_identity,
todo.held(), edit.id, reason=self.rejection_reason,
evidence=self.head_evidence())

def check_edits(self, edits: list[Edit]) -> None:
_ = self.rejection_reason

def main(args, ctx):
JudgeVerb.parse(args or ['show']).apply(ctx)
Loading
Loading