Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e0ca015
Record delivery integration on checked orders and decode named PR rows
trissim Oct 19, 2026
31f8ca1
Add observed commander board with read-only progress and lazy cards
trissim Oct 19, 2026
897909e
Read actual checkout commits only for opened commander cards
trissim Oct 19, 2026
696e873
Integrate retained send-refusal dependency for commander alarms
trissim Oct 19, 2026
ec5f945
Verify real order records and delivery states through the CLI
trissim Oct 19, 2026
c446fc3
Record final candidate checks and outstanding record-plane blockers
trissim Oct 19, 2026
256efcc
Merge remote-tracking branch 'origin/main' into feat/cn-phase3-record
trissim Oct 19, 2026
6ec698c
Render retained refusals and bounded original turn summaries on comma…
trissim Oct 19, 2026
fd2dd82
Decode named PR rows once and retain post-main CLI acceptance
trissim Oct 19, 2026
e248a6a
Resolve commander identity through the observation boundary and refre…
trissim Oct 19, 2026
bcf11c8
Record physical commander acceptance, owner evidence and review limits
trissim Oct 19, 2026
8f03b4b
Keep remote progress when the local checkout is absent
trissim Oct 19, 2026
f9fc186
Scope local progress to its checkout and bounded reading window
trissim Oct 19, 2026
1b8bf1c
Read recorded check evidence without executing from progress
trissim Oct 19, 2026
6182e05
Merge remote-tracking branch 'origin/main' into feat/cn-phase6-page
trissim Oct 19, 2026
fcc5e75
Integrate receipt-only progress source and delete obsolete status source
trissim Oct 19, 2026
65fb631
Derive unit-card commit momentum from canonical order and branch evid…
trissim Oct 19, 2026
db68514
Revert "Integrate receipt-only progress source and delete obsolete st…
trissim Oct 19, 2026
9db59da
Merge remote-tracking branch 'origin/main' into feat/cn-phase6-page
trissim Oct 19, 2026
7b1c81d
Remove competing phase 3 source changes and repair page typing and er…
trissim Oct 19, 2026
68d7c93
Allow a focused single-unit physical commander journey
trissim Oct 19, 2026
c200025
Integrate P250 checkout capabilities into commander evidence reads
trissim Oct 19, 2026
a90135b
Merge remote-tracking branch 'origin/main' into feat/cn-phase6-page
trissim Oct 19, 2026
43dfbd2
Represent actual GitHub process failures without swallowing Git and s…
trissim Oct 19, 2026
09c3543
Integrate current main missing-distance semantics with bounded page e…
trissim Oct 19, 2026
d07ee61
Own recorded order-time normalization in checked orders and migrate n…
trissim Oct 19, 2026
ca9c4f8
Let delivery-progress variants select their failed notifications
trissim Oct 19, 2026
5bc9d15
Record cadence workflow acceptance and current review limits
trissim Oct 19, 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
110 changes: 110 additions & 0 deletions docs/agents/commander-page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Commander board and derived commit cadence

Issue #1495. Core #1937, paired Toad #1080. Owner: pi-cn-phase6.
These are source-paired changes, not an installation claim.

## Working slice

`/commander <commander-thread>` opens the ordinary Toad page. It shows held
orders, deadlines and delivery state, overdue first. Expanding one unit reads
its actual checkout commit and three original recent turns. Recorded failures,
stalled orders and retained refused sends appear in the alarms pane.

The collapsed card also shows commit cadence, the forecast next-commit time,
its margin against the order deadline, and whether that forecast has already
been missed. Missing checkouts and insufficient/non-increasing samples remain
explicitly unknown. Delivery removes the forecast.

**Interpretation:** commit cadence predicts the next commit, not completion.
There is no known amount of remaining work from which to calculate a completion
ETA. The page explicitly says `completion unknown`; no prediction is persisted.
Full row-27 prediction/outcome comparison remains unverified. pi-plan-claims
owns that row's declaration in #1992; no second parity registry was added here.
Strikes, review queue, story and sidebar decorations remain outside this slice.

## Owners and deletions

- `TodoStore.held_for_commander`, nominal holder identities and `Todo.overdue`
supply the canonical order selection and deadline meaning.
- `ProblemOrder.instructions` supplies order text. `CheckedProblemOrder.ordered_at`
owns recorded-time normalization; all numeric order-time consumers use it.
- `ProgressSource.progress` supplies one progress-selection algorithm.
`StatusProgressSource` inherits it and disables acceptance-check execution.
- Main's `ProgressCheckout` family owns available versus removed checkouts.
Its concrete classes supply commit details and bounded cadence evidence.
Both distance and cadence reads share `PresentCheckout.branch_commits` and
the `ProgressPoint` decoder. #1935's competing source changes were reverted.
- `UnitCard.momentum` derives `CommitMomentum` values from canonical orders and
observed commits. Unknown and forecast states are distinct types, not zero
timestamps standing in for missing observations. No new store or timer exists.
- `DeliveryProgress.failed_notifications` owns failure selection. `FailedProgress`
returns the original notification. The alarm reader's `isinstance` filter was
deleted; Toad has no failure-kind switch or notification copy.
- Actual GitHub command failures and timeouts become explicit unread progress.
Other Git, decode and source errors propagate; there is no ValueError catch-all.
- Existing observation interests, store/WAL revisions and expiry notifications
drive updates. Closing the page retires its interest.
- Toad renders the Core model through its existing Collapsible widgets. GoalBar
presence is derived from actual composition, not an optional cached child;
opening/clicking a bus conversation no longer raises `goal_bar` AttributeError.

Used the existing refactor-audit Package parser across both production roots.
The declaration/consumer evidence reports zero parse omissions. Dynamic family
and MRO resolution was read semantically, not claimed as an AST behavioral proof.
Evidence is under `/home/ts/.cache/agent-scratch/pi-cn-phase6/`.

## Physical application proof

`physical-failure-owner/` contains the latest plain-st/Xvfb run, using normal
Toad, its real observation process, copied registered saved sessions, real
scratch-bus orders created through Core tools, and an actual retained refused
send. No provider was started, and no original session or uncertain input was
changed. `sources.txt` records provenance.

The journey opened a real bus conversation, submitted `/commander claude-code`,
clicked one card, observed a tool-recorded delivery update without reopening,
and returned to the same conversation. Captures were inspected; no traceback
pane or unavailable-page error is present.

- `level-0.txt/png`: collapsed cards, known and unknown cadence, alarms.
- `level-1.txt/png`: one expanded card and original turns; second card collapsed.
- `live-delivery.txt/png`: delivery updates the same open card and clears its stall.
- `result.txt`: successful continuous-journey result.

Actual collapsed screen excerpt:

```text
Commander claude-code | 2 units | Overdue first
P1: 3.0m/commit; next commit ~17:17 (132.1m before due); forecast missed by 133.2m; due 1.2m ago; completion unknown
P2: cadence unknown (need two increasing commit times); due in 29.8m; completion unknown
STALLED pi-cn-phase6 P1: pushed d07ee61b8 at 17:14, no PR
REFUSED SEND #1 claude-code -> unregistered-page-target: Target 'unregistered-page-target' is not a registered thread.
```

## Checks and review limits

The stable source checkpoint passed **142 tests in 72.67s**. After removing the
failure-family filter, all **10 commander-model checks passed in 1.88s**, including
selection of the original failed notification. Tests ran in the background under
`timeout 300 ... -X faulthandler`, with default parallelism/coverage disabled.
The environment warns about the unavailable pytest-timeout plugin; the outer
timeout was enforced. Ruff for the new model and both diff checks pass.

The retained full ratchet reports at the stable checkpoint both exit **1**, solely
for `SourceLines` (+465 Core, +205 tools), with no introduced findings. Rechecking
`ca9c4f881` after deleting the failure-family filter gives **src 1/tools 1**, solely
+474/+205 SourceLines, again with introduced findings empty. No budget,
suppression or checker change was made. Under the commander's ruling in message
3617, SourceLines-only failures do not block review. Reports name their exact
base/head; they are not a claim about a fresh merger integration head.

The checking interpreter is the shared QA Python, with the paired Toad source and
existing stack dependencies visible. Toad now publishes its inline declarations
through `py.typed`; no copied Toad schema or stub was introduced. The installed
stack interpreter lacks the checking modules and was not modified.

The required `order pr P243` create command was attempted once. It failed on its
derived missing `pi-cn-phase6--P243` directory; this task's assigned checkout is
`pi-cn-phase6--P1`, and #1937 already exists. The existing PR is updated in place
with metadata generated by its canonical order owner. GitHub currently reports
a conflict with current main; no merge-main or unrequested rebase was performed.
4 changes: 2 additions & 2 deletions src/agent_comms/commander/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ def first_calls(held, activity):
unit, order = todo.assignment.owner, todo.order
if not todo.pending:
continue
acted = activity[unit].since(order.at.recorded_at("An order"))
acted = activity[unit].since(order.ordered_at)
if not acted and time.time() < order.first_call_deadline.at:
continue
event = ObservedFirstCallEvent if acted else NotifiedFirstCallEvent
Expand Down Expand Up @@ -590,7 +590,7 @@ def deadline_lines(pending: list[Todo], read: dict[str, OrderProgress], since: f
now = time.time()
late, stalled = [], []
for t in pending:
ordered, work = t.order.at.recorded_at('An order'), read[t.id]
ordered, work = t.order.ordered_at, read[t.id]
if work.stalled_between(ordered, since, now):
stalled.append(f'STALL {t.id} {t.holder}: {work.line} -> push it now or reassign')
if not t.overdue(now):
Expand Down
4 changes: 2 additions & 2 deletions src/agent_comms/external_transcript.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ def unjudged_query(cls, thread, todo):
tools = tuple(tool.declared_name for tool in ToolDeclaration.members_with(MutatingPathTool))
judged = tuple(verdict.tool_call_id for verdict in todo.verdicts)
placeholders = lambda values: ",".join("?" for _ in values)
since = todo.order.at.recorded_at("An order")
since = todo.order.ordered_at
# SQL NULL is the original undated fact, decoded through FieldCodec
# as UnrecordedEventTime when a row is read. No time is substituted.
table = cls.declared_name
Expand Down Expand Up @@ -382,7 +382,7 @@ def unjudged(self, todo):
for encoded, in rows:
event = FieldCodec.decode(TranscriptEvent, FieldCodec.parse(encoded))
yield from event.edit_calls(todo.held().owner, todo.id,
todo.order.at.recorded_at("An order"))
todo.order.ordered_at)

def unjudged_count(self, todo) -> int:
where, parameters = ExternalEventRow.unjudged_query(self.transcript.lookup, todo)
Expand Down
2 changes: 2 additions & 0 deletions src/agent_comms/history_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ def require(self, name: str) -> ThreadPresentation:


class HistoryViews:
goals: Goals

def __init__(
self,
root: Path,
Expand Down
10 changes: 10 additions & 0 deletions src/agent_comms/notification_progress.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ class DeliveryProgress(ThreadPresentationStatus):
def notification_label(self, notification: MessageNotification) -> str:
return self.label

def failed_notifications(
self, notification: MessageNotification
) -> tuple[MessageNotification, ...]:
return ()


class PendingDelivery:
delivered: ClassVar[bool] = False
Expand Down Expand Up @@ -87,6 +92,11 @@ class InterruptedProgress(ContextualProgress):
class FailedProgress(ContextualProgress):
label = "Failed"

def failed_notifications(
self, notification: MessageNotification
) -> tuple[MessageNotification, ...]:
return (notification,)


class EngagedProgress(ContextualProgress, WorkingThreadPresentationStatus):
priority = 1
Expand Down
37 changes: 26 additions & 11 deletions src/agent_comms/presentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
from .channels import Channel, ChannelMemberIndex, Channels
from .cli_result import CliResult
from .coordination_errors import DMPaintedBoundChanged, StaleRevision
from .declared_family import DeclaredFamily
from .display_order import ChannelSort, NameChannelSort
from .display_references import DisplayReferences
from .external_transcript import ExternalTranscript
from .declared_family import DeclaredFamily
from .field_codec import FieldCodec, JsonShapeFamily, JsonShapeMember, projected
from .goal_presentation import ThreadGoalExecution
from .goal_waits import GoalWaits
Expand All @@ -39,15 +39,27 @@
from .notification_progress import DeliveryProgress, HumanReadProgress, ReadDelivery
from .pi_counts import PiCount
from .private_path import FileRevision
from .ui_model.unread import UnreadPresentation, UnobservedUnread
from .read_basis import (AnonymousViewer, CapturedSelection, ChannelDisplayScope, DisplayViewer,
NamedViewer)
from .read_basis import (
AnonymousViewer,
CapturedSelection,
ChannelDisplayScope,
DisplayViewer,
NamedViewer,
)
from .read_ledger import NoReadNotice, ReadLedger, ReadNotice
from .recorded_bus import RecordedBusIdentity
from .registry_document import DEFAULT_ROSTER, RosterVisibility
from .revision_views import (STATUS_SOURCE, BusSourceView, ExternalTranscriptsView,
NotificationsView, RegistrationsView, RevisionView,
SidebarView, StoresView)
from .revision_views import (
STATUS_SOURCE,
BusSourceView,
CommanderView,
ExternalTranscriptsView,
NotificationsView,
RegistrationsView,
RevisionView,
SidebarView,
StoresView,
)
from .runtime_info import RuntimeInfo
from .store_files import SharedLock, _store_lock
from .thread_identity import ThreadIncarnation
Expand All @@ -59,6 +71,7 @@
from .thread_status import ThreadStatus, ThreadStatusName
from .threads import Thread
from .tool_result import ToolResult
from .ui_model.unread import UnobservedUnread, UnreadPresentation
from .wire_log import WireLog, WireSnapshot

if TYPE_CHECKING:
Expand Down Expand Up @@ -771,7 +784,9 @@ class WireRevision(ReadState):
"""One observation of every Comms store a view derives from, by name."""

registry: tuple[FileRevision, ...] = revision_source(lambda views: (views.registry.store.path,),
*STATUS_SOURCE, RegistrationsView, NotificationsView)
*STATUS_SOURCE, RegistrationsView, NotificationsView, CommanderView)
todos: tuple[FileRevision, ...] = revision_source(lambda views: views.goals.todos.source_paths(),
StoresView, CommanderView)
catalog: tuple[FileRevision, ...] = revision_source(lambda views: (views.channels.catalog.path,),
*STATUS_SOURCE, RegistrationsView)
bus_log: tuple[FileRevision, ...] = revision_source(lambda views: (views.bus.log.path,),
Expand All @@ -793,12 +808,12 @@ class WireRevision(ReadState):
relationships: tuple[FileRevision, ...] = revision_source(lambda views: (relationships.RelationshipStore.path_in(views.root),),
*STATUS_SOURCE)
notifications: tuple[FileRevision, ...] = revision_source(lambda views: NotificationAssignment.handling_paths(views.root),
*STATUS_SOURCE, NotificationsView)
*STATUS_SOURCE, NotificationsView, CommanderView)
external_transcripts: tuple[FileRevision, ...] = revision_source(lambda views: ExternalTranscript.source_paths(views.root),
StoresView, ExternalTranscriptsView)
StoresView, ExternalTranscriptsView, CommanderView)
# Notifications change once per expiry period, not per tick.
expiry_tick: int = field(metadata={
"views": (NotificationsView,),
"views": (NotificationsView, CommanderView),
"differs": lambda before, now: (
before // WireRevision.expiry_seconds != now // WireRevision.expiry_seconds)})

Expand Down
4 changes: 4 additions & 0 deletions src/agent_comms/revision_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ class SidebarView(RevisionView):
"""Native unread progress updates counts without rebuilding thread presentations."""


class CommanderView(RevisionView):
"""Orders, retained deliveries, recent turns and time-derived commander attention."""


class ExternalTranscriptsView(RevisionView):
"""CLI drafts and finished items, independently of coordination status."""

Expand Down
Loading