Skip to content

Integrate owner reads and common queue presentation - #1319

Closed
trissim wants to merge 33 commits into
mainfrom
batch/integ-20261029-toad
Closed

trissim wants to merge 33 commits into
mainfrom
batch/integ-20261029-toad

Conversation

@trissim

@trissim trissim commented Oct 29, 2026 •

Copy link
Copy Markdown

Summary

Four source-repair commits across37 files:+403/-659 against the reviewed baseline, including248 deleted test/peer lines;30 production modules changed by the repairs. One integration of #1302 and #1317, paired with agent-comms#3566. Queue refresh no longer rebuilds a notification request per message or searches retained pages. Native queue refresh never reads BUS fragment entries. Unread, filtered and scrolled-up paints return a declared no-work result before creating a worker or opening a transaction.

Source checkpoint, not installed or live-qualified. No measured frame-time or bus-latency improvement is claimed. Root owns the matched carry and actual saved DM/channel/CLI/native journey. The existing carrier branch and this PR are retained; parent checkouts are untouched. Current main is integrated normally.

Queue evidence and the import hierarchy

Before: each BusMessageFragment constructed ReadNotifications, whose builder walked visible targets and retained pages to rediscover a prepared page's display identity. The screen's module also contained the fragment's data type, creating a reverse import dependency.

After:

  • notification_status.MessageQueueEntry contains only the actual MessageReference and NotificationStatus used by its summary/details. The unused display field is deleted.
  • BusMessageFragment receives its immutable page display identity at construction. WireSource supplies the actual Core page through the existing fragment family. Native non-BUS fragments carry no display field. The mutable base field and capture_notification_identity method are deleted.
  • The notification subscription selects identity from visible BUS fragments, not retained pages. Queue entries never construct a notification read request.
  • QueueEvidence.entries derives the bounded visible entries lazily, once per UI read, with MessageReference deduplication and Core's existing window limit. BusQueueSource reads entries; NativeQueueSource never accesses them. Both siblings consume one evidence value instead of a union of eagerly evaluated arguments.
  • QueueEvidenceChanged lives in the existing toad.messages owner, not in the queue screen. Fragments import their data from notification_status; the queue screen imports notification capabilities. No TYPE_CHECKING or function-local import hides this cycle.

This deletes visible × (visible + retained-fragment) work from queue refresh. That is a source relationship, not a measured speedup.

Captured requests own confirmation behavior

Before: shared _prompt returned None for missing confirmation; clear/send-now converted it to bool; NativeQueueSource converted False into an exception.

After: QueueSessionRequest(ClientSessionRequest) specializes unconfirmed handling to raise QueueActionRefused. The ordinary captured request keeps its existing failure-publication behavior. The shared controller still owns transport, captured session/scope, actual failure reporting and deferred-input ordering. Clear/send-now await confirmed completion and return nothing; the bool conversion and dead optional-command branches are deleted. Queue control requests use their real CommsRequest parent, since ClearQueueRequest and SendNowRequest are not PromptRequest subclasses.

Unexpected ValueError still escapes. Missing confirmation never grants retry permission or proves that no side effect occurred. Drafts, accepted producers, uncertain inputs and backend membership remain with their original owners.

Source capabilities, not a submissions-to-view forwarding cycle

QueueSource actions receive the actual AgentPresentation and captured scope. QueueInspection receives one QueueEvidence and the existing repaint callback, never a submissions object to walk back through its view. Its consumers read presentation directly from that evidence; no second presentation attribute is maintained. InputSubmission matching takes the same existing transaction owner as execution, deleting the reverse Conversation import. UI error rendering belongs to Conversation. QueueActionRefused lives with the actual queue attachment below presentation and screens.

The third review required actual ConversationSubmissions annotations on all three ConversationKind.prompt signatures. A retained NRA import map exposed two reverse paths, rather than just a typing inconvenience:

  • conversation_submission -> core.input_events -> navigation_target -> conversation_kind: SelectTarget now lives with NavigationTarget, and every production/test consumer imports it there. The primitive input-events module no longer imports the navigation hierarchy; no re-export is retained.
  • conversation_submission -> submission_queue -> ToolContent -> linked_rows -> navigation_target: QueueInspection moved unchanged in behavior to screens/queue.py. The existing Conversation.open_queue_menu constructs it; the transaction model no longer imports or constructs the modal. Its dead queue_source forwarding property is deleted.

ConversationKind now imports ConversationSubmissions, ChannelPrompt and MessagePrompt at top level. Three function-local prompt-widget imports are deleted. QueueEvidence also defers notification-target acquisition, so native refreshes do not even perform the former visible-widget walk. The after-map has no reverse top-level import path from submissions to conversation kind.

The three reviewed modules—submission_queue.py, conversation_submission.py and agent_presentation.py—have no TYPE_CHECKING blocks or local imports. This does not claim removal of unrelated pre-existing guards throughout ToAD.

Painted reads: no work means no transaction

The first repair correctly replaced nullable transcript cursors but put the no-op too deep: ReadyContent created a read worker, which opened access.write before UnreadTranscriptCursor did nothing. That was a real regression, not a successful no-op.

Now Conversation.painted_read returns NothingToRead for unmounted, filtered, scrolled-up or blocked views. Otherwise the cursor's painted_read chooses the result: UnreadTranscriptCursor returns NothingToRead without invoking the callback; a positioned cursor invokes it. ReadyContent.read_work constructs route/read/write work only on that latter path. The obsolete visible_read_cursor property is deleted. No type switch or cursor-None test replaces it.

TranscriptPresentation initializes/resets to UnreadTranscriptCursor and refresh asks requires_commit(). PageRequest and TranscriptPageWork accept actual cursors. The separate older-page caching fact is owned by PageRevision, while GrowingPageRevision adds the newer read's frontier. The former work_cursor/None marker is deleted, so new arrivals do not invalidate immutable older-page keys.

Remaining competing decisions removed

  • Three source/agent staleness guards are deleted; the captured attachment/session validates the actual action.
  • NothingRead and AfterMessage own first-page acceptance and source advancement; WirePage's undrawn switches are deleted.
  • Unread wire pages use MessagePageRequest.empty, not a copied constructor.
  • CommsChatView holds its WireSource directly. WirePageBuffer and PreparedPageSource are not queue authorities or loader-cast forwarders.
  • The duplicate LiveNotificationRead import is deleted.
  • The two newly added automated-test files and the fake refusing peer are deleted under ToAD's test ban. No automated test was run.
  • Both unused prepared-page notification_identity properties and their imports are deleted. Only BUS fragments retain construction-time display identity. The existing populated-page control reads that fragment identity; its empty-page control reads Core's actual selection, since an empty page has no fragment.
  • Whole-tree caller tracing also found widgets/wire_history.py:60 still reading the deleted page.undrawn attribute, a real channel-path AttributeError that earlier source imports could not expose. AfterMessage.requires_read returns False and NothingRead overrides True; WireHistory asks that cursor-owned behavior. No page flag or compatibility accessor was restored. The existing control was migrated to the same cursor behavior.

Paired API and publication order

Core's answer family is pushed, not a promise: #3566 at22c0ef74e introduced MessageNotification.answer: AssignmentResponse and detail_texts(), with answer time on AnsweredResponse. The cursor's pre-write choice is pushed atd06847ad0. Both declarations were inspected from Git HEAD before publishing this consumer correction.

ToAD uses answer.detail_texts() without an optional-reference probe. EmptyCheckedNotifications owns empty disclosure text. TranscriptScope and CoordinationTranscriptReader carry PageSource directly: LivePageSource by default or RecordedPageSource(HistorySourceKey). Historical notification reads take the same nominal HistorySourceKey. Nullable historical-source strings and the old keyword are deleted, not retained as compatibility paths.

Verification and limits

  • Retained NRA ModuleSyntaxIndex source maps include production ToAD and the paired Core context, with hashes and zero parse omissions. This is source coverage, not dynamic binding or behavior proof.
  • All30 changed production modules imported in separate fresh processes against Core HEAD d06847ad00016517f60965f5b08070a54cbf4870, with no dirty Core source. Each prints ok MODULE; final output is all 30 touched modules imported in fresh processes against paired Core source, exit0. Source hashes are retained in review3-imports.log. The final deletion of the unused submissions queue_source forwarder received a targeted fresh-process reproof, also ok/exit0.
  • NRA indexed the initial17-file correction with zero parse omissions, retaining declarations, writes, decisions, calls and imports. The two additional old test consumers were migrated afterwards, without test/checker execution. The three prompt parameters are annotated ConversationSubmissions. Static import analysis is not a dynamic binding or live-application proof.
  • No local ratchet was run; the hosted ratchet still has to establish the result on this head. The reported red run40919270001 belongs to599d, before this correction.
  • The earlier17-module import log did not establish its exact reference-checkout revision; its former6c88 attribution is withdrawn. The earlier in-flight Core cycle failure is retained and is not counted as a pass.
  • No automated tests, synthetic application, provider call, environment build or installation. The two old ToAD test-consumer files are now migrated too. tests/app/test_recorded_transcript_reader.py uses page_source=RecordedPageSource(source.key) and the nominal page_source identity, with the copied field-name roster assertion deleted. tests/support/native_steps.py reads the already-narrowed AnsweredProgress.response.reference and asks seen_not_answering without a second None/type probe. That response field was verified in committed and remotely pushed Core 9021283e36704573937a9df834812eba369d3b42 before publishing this consumer. No historical_source call or old optional-answer probe remains in ToAD src/tests/tools. These files belong to ToAD, not Core; queue-core-fix correctly left this checkout alone. The other test edits are the requested wire-source migration and SelectTarget import migration. No test or new fixture was introduced or run; root owns combined acceptance against the newer Core head. No compatibility alias was restored.

Assumed: root lands the paired Core before or with this consumer, runs the combined acceptance on the matched heads, and verifies actual Clear/Send now, DM/channel/CLI/native coverage, stale scopes, drafts/mentions, growing history, scroll-up paints and tab return. Source approval, imports, merges and an installed working queue are distinct facts.

unread cursor used to acquire a write lock to announce it had nothing to say
the no-op now happens before SQLite gets invited

trissim and others added 22 commits October 28, 2026 06:33
…omms#3538)

ReadDeliveryOverview restated Core's input-delivery read with its own
include_history; Core deleted it. Toad now sends ReadOwnerOperation carrying
InputDeliveryRead(include_history=...) to the held owner and wraps the
owner's DeliveryOverview in ReadDeliveryView. No-coordination DeliveryView,
require_owner and dismiss_historical_inputs are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…ms#3538)

tests/native/test_native_goal_owner.py: with the real native session, after
until_agent_ready(), controller.input_delivery() and
controller.input_delivery(include_history=True) are each a ReadDeliveryView
whose overview has current_scope OwnerQueueInputDeliveryScope (only the live
owner yields it; the no-coordination DeliveryView and the unobserved
projection never do), and on the fresh private wire empty inputs and
historical_inputs with zero counts. No mocked read, operation, owner or reply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…e root (agent-comms#3538)

Core moved the notification read off the UI request: LiveNotificationRead and
RecordedNotificationRead own references (and the archive), and
ReadNotifications(root, operation) is the one UI envelope.

- HistoryNotifications.notification_request (a ReadNotifications class used as
  a root+references constructor) becomes notification_operation, a root-free
  operation constructor (LiveNotificationRead by default). notification_read
  binds the ProjectPathOwner root: ReadNotifications(root, operation(refs)).
- RecordedContents sets partial(RecordedNotificationRead, source=source).
  ReadRecordedNotifications is gone.
- comms_root keeps keying views and outcomes by the full UI request.
- tests: the historical source test requires the generic envelope with
  RecordedNotificationRead((reference,), source.key); the observation shutdown
  test builds its request with LiveNotificationRead. Assertions unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…operation's references (agent-comms#3538)

Both shutdown tests called process.fileno() to unhook a readiness callback that
ObservationProcess no longer has. The one receiver started at activation keeps
running after stop_observing, so the answer has to be held where it is made: in
this test's own observer child.

- test_stopping_observation_keeps_reads_but_sends_no_late_interest proves the
  child is ready with an empty-window read and SIGSTOPs it (stop confirmed by a
  bounded WNOHANG|WUNTRACED wait). It then registers the real read, stops
  observing with every AnsweringReads identity check and the no-late-interest
  profile, and SIGCONTs the child. The same receiver settles the read:
  set(result.outcomes) is the message's seq/id.
- test_an_observer_dying_during_shutdown_still_releases_its_resources holds the
  child, registers a pending empty-window read and SIGKILLs the child.
  - The read fails with ObserverDied carrying that pid and -SIGKILL.
  - StopSidebar and StopObserving each raise that same object.
  - Teardown may report it only as that same cause, and other grouped errors
    propagate.
  - The connection is closed, the child is gone and nothing is retiring.
- Every path resumes a still-stopped live child and settles any unconsumed read.
- The four paint-fixture reads of request.references now read
  request.operation.references; ReadNotifications has no references of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…on (agent-comms#3538)

read_thread_presentation returned the acquired presentation to Conversation and
ObservedSourcePublication, carrying the notification window and backend records
into the UI. It now returns .published(), the source-free ThreadPresentation;
settlement still reads its read_identity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…ion (#1302)

The commander page wraps CommanderUnitRead(commander, unit, orders) in the UI's
ReadOperation envelope instead of the deleted ReadCommanderUnit. Request id,
root, card/collapsed fencing, worker cancellation and the unavailable-detail
display are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…#1302)

acp/agent.get_goal_snapshot sends ReadOwnerOperation(wire_root, thread,
operation=OwnerGoalRead()) through its existing transcript reader instead of
the deleted ReadOwnerGoal. The local NoGoal/Idle answer without coordination,
the ClientSessionRequest authority, OwnerSnapshotConsumer's captured turn
token, the post-read require_owner and dispatch of every meta.batch update are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
)

KnownTranscript.snapshot_request and update_request, and the historical
sessions screen, build their UI envelope around TranscriptPageRead with every
original cursor and the attached source key; root and publication state stay
on the envelope. acp/transcript_reader's RefreshTranscript keeps its original
identity. A recorded-reader test asserts the exact page arguments on the
operation and root/previous on the envelope.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
Config only, authorized by root (bus 36182) for the P883 matched cut. The
checker guessed the root from src/dot; tools/acceptance.py:34-36 already
declares tools as a script root for its sibling imports (performance.*), so
mypy_path names both roots instead of guessing one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…teg-20261029-toad

# Conflicts:
#	tests/textual/test_historical_notification_source.py
trissim and others added 3 commits October 29, 2026 04:06
historical_sessions keeps #1302's TranscriptPageRead import beside the
shared notification reads; message_notifications takes #1317's read, which
passes the captured display to ReadNotifications(display=...), the field
the Core carrier (agent-comms #3566) now declares. Loads and imports
against the carrier's Core source.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnH6dR85kyFKcc2tQAJLhK
trissim and others added 2 commits October 29, 2026 11:39
The page API carried before/after/through as TranscriptCursor | None,
restating "no bound" as None. Readers, scopes and windows now pass a
PagePosition (LatestPage, EarlierThan, LaterThan) and a PageLimit
(AtFrontier, Through); cursors build their own positions, and a changed
transcript asks its cursor whether a commit is required.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnH6dR85kyFKcc2tQAJLhK
@trissim

trissim commented Oct 30, 2026

Copy link
Copy Markdown
Author

Superseded by #1359 (batch/integ-3566-toad): every commit on batch/integ-20261029-toad is contained in it (git log origin/batch/integ-3566-toad..origin/batch/integ-20261029-toad is empty), so its 22 unmerged commits land through #1359 with agent-comms #3566/#3670.

@trissim trissim closed this Oct 30, 2026
trissim added a commit that referenced this pull request Oct 30, 2026
Combined carrier: Toad #1319 paired with agent-comms #3566
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant