Repository navigation
Combined carrier: queue unification #3566 with Toad #1319 - #3670
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…b.com/OpenHCSDev/agent-comms into work/claude-nra--P872
ReadNotifications carried a caller-choosable viewer field so the MCP outcomes tool could reuse it under the caller's authority. That made the reader's authority an argument any caller could set, and the UI and MCP sides each restated who reads. Now a BusRead member owns its arguments and its read once, as read_as(comms, reader); the boundary supplies the BusReader. UiRead derives the UI's read() as the operator, and the MCP tool reads under its own ToolCaller, which is now a BusReader. OperatorOutcomes moves to bus_read beside the reader it serves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
) ContentCache stored mypy counts under a key of committed content and checker versions, but mypy also reads the installed packages, which no key names. Hosted #3538 restored debt-ratchet-src-71453f18 (saved by main cache job 40654054315 under a Core-only install, Pydantic 2.14.1) and compared that stored base count with a head count measured under the full stack (Pydantic 2.13.5). - CheckedRevision.findings runs the checker. TypeErrors inherits it and keeps mypy's own --cache-dir, which tracks dependency changes itself. - LintFindings.findings keeps the content-keyed memo unchanged: Ruff reads only the tree and the head's configuration. - Unformatted keeps its per-file blob cache unchanged. - ClientTypeErrors.findings calls checked directly. The client commit and src tree it computed only for the key are gone. Regression: one parameterized real-mypy test switches an installed PEP 561 dependency between two sys.path environments, with committed source, client and configuration unchanged and the same count-cache directory. The count must follow the dependency. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
GoalHistory (tools.py) and GoalHistoryReply (runtime_requests.py) were
empty subclasses of RecordedGoalHistory, and recorded_history took the
result class as an argument so each caller picked its own empty wrapper.
Neither added a field or a behavior.
- RecordedGoalHistory now extends ToolResult (itself a DeclaredReply), so
the MCP tool and the owner socket return the same record.
- GoalHistory and GoalHistoryReply are deleted.
- GoalManagement.recorded_history(name, scope) returns RecordedGoalHistory;
the reply argument, its HistoryReply TypeVar and the TypeVar import are gone.
- CommsGoalHistoryTool and GoalHistoryRuntimeRequest return it directly;
tool name, declared runtime name, subject resolution, scope decoding and
the worker-thread execution are unchanged.
- tests/test_goal_execution.py compares against RecordedGoalHistory.empty().
The encoded record is unchanged: one populated history/failure payload
decodes and re-encodes byte-identically through GoalHistory,
GoalHistoryReply and RecordedGoalHistory before this commit and through
RecordedGoalHistory after it; empty() encodes as
{"failures": [], "history": []} on both sides.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…the owner (#3538) CommsInputDeliveryTool read goals.input_delivery in the connector's own process under UNOBSERVED_DELIVERY_SCOPE: it could not see the owner's live queue, yet returned an ordinary-looking delivery view. The UI and the owner socket each restated the same read again (ReadDeliveryOverview, InputDispositionsRuntimeRequest, with their own include_history). - BusRead is now an abstract capability of the existing ToolDeclaration family (no DeclaredFamily root of its own); UiRead still derives from it. - BusReader owns subject/own_thread/refuse_thread once; the copies on ToolCaller and NoThreadCaller are gone, refusal text and type unchanged. - OwnerReading: one exchange with one held owner incarnation; admit() defaults to any owner, OwnerLiveRead admits only native owners, so an external bridge (live PID, no socket) is refused instead of retried forever. - OwnerReplyConnection.held/answer: the one connection factory (exact incarnation, root, process; 3 s reply timeout, close in finally, no retry after dispatch) for OwnerRead and OwnerLiveRead. - OwnerLiveRead: MCP apply/read_as resolves caller.subject(thread) and asks the owner via asyncio.run on the tool's synchronous boundary; the owner answers in owned(ctx) with ctx.name and its own session. - InputDeliveryRead (comms_input_delivery, thread, include_history=True): the one declaration, in the ToolRequest/native catalogs by capability. owned() takes scope only from the owner's delivery_scope. - OwnerReadRuntimeRequest (owner_read) and ReadOwnerOperation: one generic runtime envelope and one generic UI envelope carrying the operation. - Deleted: CommsInputDeliveryTool, ReadDeliveryOverview, InputDispositionsRuntimeRequest, the input_dispositions request. - The connector encodes Refused, ConnectionError and TimeoutError from an owner read as a failed tool call instead of dropping the HTTP call. - tests/test_command_families.py: owner_read round trips for both history settings; a non-owner-live operation and a bad include_history refuse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
… a live owner (#3538) - test_observer_death: unavailable/stale owner reads are parametrized from ReadOwnerGoal plus ReadOwnerOperation over BusRead.members_with(OwnerLiveRead), not OwnerRead.__subclasses__(); refusal and observer-alive assertions kept. - test_input_delivery_history: the CLI test that asserted a declared owner with no process answers delivery state is deleted (that answer was the defect). The replacement starts the canonical owner socket, queues a prompt, records an off-queue notice, and asserts MCP invoke and the UI envelope both equal the owner's own projection for include_history False and True, that the off-queue notice is current only under the old unobserved projection, and that a stopped owner refuses the MCP read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…re handed (#3538) - OwnerReading.admit is the shared native-transport admission (owner.execution.require_native()). OwnerLiveRead.admit and ReadOwnerOperation.admit were identical restatements and are deleted. ReadOwnerGoal now reaches it too: an external bridge has a live PID and no RuntimeServer socket, and _connect_current waited for that socket forever before dispatch. It is refused before the wait. - OwnerReplyConnection.held compared the registry's incarnation with the supplied one, then OwnerConnection.__init__ read the registry again into _identity, so a replacement incarnation could be recaptured unchecked. held now constructs the connection and OwnerConnection.require_identity compares the captured _identity with the supplied witness before returning; the earlier separate comparison is deleted. No second identity field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…ation, death, external controls (#3538) tests/owner_socket_fixture.py (registered in pytest_plugins) is the one fixture owner: runtime_processes (moved from test_runtime.py; it now yields the spawned children so a test can stop one through its own owner), declare_owner (native or external registration) and silent_socket (the real Unix-socket peer from test_dispatched_request_without_a_reply..., which receives one request and closes without replying). The existing reply-loss test is refactored onto it. New controls, nothing of ours patched, no fabricated reply: - MCP InputDeliveryRead without a reply, for include_history False and True: OwnerReplyLostError, exactly one received request decoding to OwnerReadRuntimeRequest(thread, InputDeliveryRead(thread, include_history)), durable inputs unchanged. The peer runs no domain body. - Root replaced under a held connection (rename to a sibling, new directory at the old path): OwnerIdentityChangedError, zero requests; root restored. - Same-name recreation (unregister, begin_delete, remove, declare): the held read and a new held() with the old witness both refuse; incarnations differ. - Held process death (runtime_processes child stopped through stop_sync): the read refuses with ConnectionError before any request; inputs unchanged. - External owner with a live child and no socket: MCP delivery refuses with RelationViolationError; through the observer, ReadOwnerGoal and the input-delivery envelope (derived from OwnerLiveRead membership) both refuse and the observer stays alive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…ps importing its transport (#3538) OwnerLiveRead extracted OwnerReadRuntimeRequest.declared_name and reached runtime/runtime_requests through two function-local imports to escape the cycle that extraction created, and OwnerReadRuntimeRequest.result returned Any because OwnerLiveRead had no Result default (hosted debt-src: FunctionLocalImport +2, FamilyFlattened +1, TypeErrors runtime_requests +1, BannedVocabulary +3). - OwnerLiveRead[Result: ToolResult = ToolResult]; operation fields stay the bare family class. OwnerLiveRead no longer inherits OwnerReading and has no socket read: owned/decode_reply are its answer and decoder. read_as builds OwnerReadRuntimeRequest(thread=owner.name, operation=self), sends it through OwnerReplyConnection.answer and decodes the reply. asyncio.run stays at the synchronous tool boundary. - OwnerReadRuntimeRequest moves from runtime_requests.py to bus_read.py as OwnerReading[object] + ResultRuntimeRequest; same owner_read action and operation field. Its read(owner) is the one socket exchange: owner.request(self.declared_name, operation=self.operation). - OwnerReading and OwnerReplyConnection move into owner_connection.py beside OwnerConnection. OwnerReading.read takes OwnerConnection. OwnerReplyConnection derives OwnerConnection directly (owner_read and goal_snapshot use the base proxy_payload; only PromptRuntimeRequest needs the ACP controller token). REPLY_TIMEOUT, held/answer/close and the require_identity check are unchanged. - The native transport check is OwnerReading.require_native_owner, called from held; RuntimeRequest.require_owner(entry) is untouched. - ReadOwnerOperation.read builds the envelope with owner.session_id and decodes through the operation; ReadOwnerGoal.read takes OwnerConnection; OwnerRead.ask loses its local import. All imports are module scope. - Old definitions deleted, no aliases. tests/test_runtime.py imports the moved names from bus_read/owner_connection, and the held root/recreation/death controls hand held() the real OwnerReadRuntimeRequest and await its read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…on lifecycle (#3538) Root's selection on 5c26262 ran 60 cases: 49 passed, 11 failed. One same-name control used remove() after begin_delete, which correctly refuses Deleting status; ten observer fixtures used the removed synchronous observer surface (`with`, readable()/results()/fileno(), sync close, NotificationWindow == {}). - test_runtime same-name control: deletion completes through edit.document.remove_originals((old,)) in registry.store.editing(), the path production uses (thread_management.py:537); the captured connection, old witness, zero-dispatch peer and both OwnerIdentityChangedError refusals stay. - test_observer_death: every observer is `async with`; empty-window checks compare .outcomes. The killed-observer test uses the real receiver: one read proves startup, SIGSTOP is confirmed with waitpid(WUNTRACED), the next read is shown pending, then SIGKILL; ObserverDied carries the child pid, -SIGKILL and signal text, and StopSidebar and a later read raise that same object. A stopped child is killed and a pending task cancelled in finally. The failed-observer test asks one real read on the not-a-wire route and requires exit status 1 and the original traceback. The readable() helper is deleted. - test_busy_history_loading: three `async with` sites, the close-once test's `async with process` and awaited repeated close, .outcomes comparisons; the Connection.close endpoint observation is unchanged. - test_deleted_thread_observation and test_unreadable_history_observation (the two readable() importers) start the receiver once with start_receiving and wait on tests/observed_queue.py, a test-local queue of delivered results that raises a receiver failure as its original exception. No manual pumping. No production change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…ked for (#3538) Root's rerun on 9bb8b2e: 22 cases in 70.37 s, 20 passed, 2 failed, both the other_root=False stale-identity cases (goal and input delivery). held looked up the selected thread's process before any identity comparison, so a stale incarnation was refused as "no owner process" (RelationViolationError) instead of OwnerIdentityChangedError. a4702b1 had dropped the initial comparison when it added the constructor check. - OwnerReplyConnection.held: registry.require(owner.name), then thread.incarnation != owner -> OwnerIdentityChangedError, then reading.require_native_owner(thread), then require_process/socket path and construction, then connection.require_identity(owner). The two comparisons check two different registry reads (the selected thread and the constructor's capture); no identity field or cache is added. - ObservedQueue.until holds one 30 s bound over the whole match; next() keeps its own bound for direct callers. Before, each unrelated result restarted the bound, so a steady stream could keep a wait running indefinitely. - test_deleted_thread_observation restores one 30 s bound around waiting for both the missing-thread result and the retirement. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…one (#3538) The notification read lived on the UI request: ReadNotifications held root and request_id beside its references, ReadRecordedNotifications subclassed it for the archive, and comms_message_outcomes built that UI envelope with str(comms.root) only to reach read_as. - notification_reads.py (new): NotificationRead(UiRead[NotificationWindow]) with changed_since; frozen LiveNotificationRead(references) and RecordedNotificationRead(references, source). Two members: archive identity and the change rule differ (live activity/evidence vs history revision). The module sits outside notification_read.py because presentation imports that module and bus_read imports presentation. - NotificationsRead and UnreadableNotificationsRead move unchanged into notification_read.py beside CapturedNotificationWindow. - ReadNotifications(ReadRequest) keeps root and request_id and carries operation: NotificationRead; it is no longer a UiRead. read() delegates to the operation (OperatorReader). ReadRecordedNotifications is deleted. NotificationReading asks request.operation.changed_since; read states stay keyed by the whole request. - CommsMessageOutcomesTool reads LiveNotificationRead((reference,)) under its caller; refusal, visibility, paging and its public schema are unchanged. - Callers migrated: observer fixtures carry LiveNotificationRead(()), test_thread_read_results reads the operation instead of a fake UI root, read-state tests call the operations. New controls: family/codec/pickle and request identity, archive change drops state, root validation per envelope, capability discovery, a foreign operation refused; a 125-message live batch across two window chunks; the recorded operation reads the archive (not live evidence) and a detached key refuses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…e says what it returns (#3538) NotificationsRead.activity spelled "this participant has no projection" twice: absence from the window's watched names, and a None value. observed_activity already omits registered humans and archived rows and answers DeletedThread for a deleted name, so the None was a second encoding of absence. - activity is Mapping[str, ObservedActivity | DeletedThread], keeping only the window's names that the projection actually returned. - live_changed re-projects the window's full activity_names() when ActivityView moved and compares the sparse maps, so omitted->observed, observed->omitted and observed->DeletedThread still invalidate. With no ActivityView change it does not project at all. - HistorySource.notification_references annotates references as Sequence[MessageReference] and returns NotificationWindow, the type MessageNotification.read_references already returns. That closes the Any that reached RecordedNotificationRead.read_as. - The read-state fixture that built {absent-participant: None} now settles a real #team read whose participants are an agent, a human, an archived agent and a deleted agent. New controls cover: omitted humans and archived agents, an archived agent declared again, a deleted participant, and zero or one projection by revision kind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
NotificationWindow.activity_names() comes from native-recipient outcomes, so the
#team fixture watches worker, archived and gone, never the agent sender or the
human. Two assertions expected sender and human, and failed in the root run
(29 passed, 2 failed). The window-names test now requires exactly
{worker, archived, gone} and excludes sender and human; only archived is omitted
from the sparse activity. The projection-count test requires archived as the one
watched-but-omitted name. No production change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…e no acquired evidence (#3538) A recipient-scoped thread read selected every claim of each broadcast by wire_seq alone, decoded them, and filtered recipients only afterwards: the saved six-tab desktop held 17,681 claims to show 15 rows. The same acquired graph (frozen audiences, decisions, WakeAssignment and activity rows) then travelled inside ThreadPresentation to the UI, twice over, through notification_window and through each notification's message, which was the backend record itself. Viewer captured once: - NotificationAssignmentRead carries its required OutcomeViewer. read, capture, missing_store and invalid_store take it, and the public missing store returns an empty read with that viewer; the private one still refuses. - capture builds the visible (recipient_lookup, wire_seq, message_id) keys through the viewer before any SQL. Claims are selected through the recipient/seq index by those keys, using TypedTable.requested_keys (factored out of select_keys, which now consumes it). HarnessDelivery.for_recipients gets the same keys. Rows keep the original wire_seq DESC, recipient order across batches. - delivery_window, project_delivery_window and for_sources read the captured viewer instead of taking a second one. for_deliveries skips invisible recipients before allocating an UnrecordedNotificationSource, and recorded_delivery_window allocates no placeholder activity for them. - NotificationAssignment.changed groups retained reads by their own viewer and re-reads each group's sources as that viewer. - SelectedParticipant captures its sent read as OwnerOutcomes of the owner, so the sender still sees every outcome. Public envelopes: - ThreadPresentation.with_sources and MessageNotification.for_sources attach source.envelope, the declared public Message, instead of the Delivered record. Acquired versus published: - ThreadPresentation keeps only the UI fields: status, binding, notifications and read_identity. - AcquiredThreadPresentation adds incoming_references, sent_references and notification_window. It is built by with_sources, supplies revision inputs and sent_summaries (replacing SentMessageSummary.recent), and published() derives the base from the declared fields. Pickling it raises a TypeError naming published(). - ThreadReadResult.published() defaults to itself and ThreadPresented projects its presentation. ThreadReading keeps the acquired results for its next change check and publishes only the changed results' projections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…ations (#3538) - test_outbound_transcript_source: - each recipient's own read decodes exactly one claim row (profiled AssignmentActivityEvidence._decode_row), equal to that recipient's rows and attempts in the operator read; the sender read equals the operator read; - invisible recipients allocate no UnrecordedNotificationSource, and the sender keeps all of them; - one claim transition refreshes only the retained reads whose viewer sees it: the recipient, sender and operator reads, not the other recipient's. - test_recipient_activity_index: - a viewer follows frozen lookups through rename and never into a rebirth; - a public root without a coordinator returns an empty read with the same viewer and projects only visible recipients; - a private root without one still refuses. - test_thread_read_results: - ThreadReading retains the AcquiredThreadPresentation and publishes its ThreadPresentation projection, with the same status, read_identity and notifications, whose messages are public envelopes; - the pickled observation carries no acquired evidence type, and sending the acquired result is refused; - after a second change, the UI's current and last-published rows reach no acquired evidence. - test_thread_presentation: absence publishes itself and summarises no sent message; its notification_window assertion read a field absence no longer has. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…ch and null-key controls (#3538) presentation_fields() returned dict[str, object], so expanding it into ThreadPresentation(...) and AcquiredThreadPresentation(...) erased every field's type: 10 new arg-type errors at thread_presentation.py:287/311. It is deleted. - with_sources builds shown = replace(self, notifications=..., read_identity=...) once and constructs AcquiredThreadPresentation from {declared.name: getattr(shown, declared.name) for declared in fields(ThreadPresentation)} plus its three acquisition fields. - AcquiredThreadPresentation.published constructs ThreadPresentation from the same inline mapping over itself, the ThreadStatusDisplay.status_display shape. There is no cast, Any, ignore or copied field roster. - test_outbound_transcript_source::test_a_forced_key_batch_captures_the_same_evidence_through_the_recipient_index: - setup: a second real #team original in the private _root fixture gives at least 4 distinct claim keys, plus a repeated original; - batching: on the fixture's own coordination connection, SQLITE_LIMIT_VARIABLE_NUMBER=6 (restored in finally) forces several key batches, with deduplicated bindings equal to the unlimited batch; - result: the whole capture equals the unlimited read in rows, attempts, evidence, order and viewer, with no duplicated assignment; - index: the traced claim statements' EXPLAIN QUERY PLAN SEARCHes wake_claims through the unique recipient_lookup/wire_seq index found by PRAGMA, never a SCAN of it. - round2 test_declared_table_family: select_keys selects the weight=None child through a None key once, even when the key is repeated, and stops selecting it once the weight is 3.25. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…n allows (#3538) The fixed limit of 6 refused NativeRuntimeTable's schema catalog read inside capture before any claim query ran (root: 1 passed, 1 failed in 3.30 s), so it proved nothing about claim batching. - The limit is the larger of 6 and each schema capture validates (privacy.coordinator_schema() and NativeRuntimeTable), counted as declared object names plus table names, the bindings of objects_of's catalog read. It must still be below the connection's own limit. - Enough real #team originals are published and accepted in the same private fixture for more than limit / 3 distinct claim keys. The count is derived from the recipient count and must fit NotificationWindow.window_limit, and the repeated original stays as the duplicate control. - The batching, equality, ordering, trace and EXPLAIN index assertions are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…s its type (#3538) The GC stage made two new source-gate regressions: - NotificationAssignment.changed built an untyped `changed = []`. - AcquiredThreadPresentation.sent_summaries imported SentMessageSummary and MessageNotification.summary back from presentation inside the method, a hidden runtime cycle. Now: - `changed: list[NotificationAssignmentRead] = []`. - ThreadPresentation/AcquiredThreadPresentation.sent_summaries are deleted. sent_notifications() returns (reference, outcomes) pairs: () on the public presentation, the captured window's outcomes per sent reference on the acquired one. - SentMessageSummary.for_sources aggregates those pairs with the same-module MessageNotification.summary. ThreadCaller.listing calls it. - thread_presentation keeps only TYPE_CHECKING imports for these types; function-local imports 5 -> 4 (the four remaining predate this stage). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
There was a problem hiding this comment.
Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.
An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.
# Conflicts: # src/agent_comms/tool_caller.py
The first pass fixed the ten files GitHub's annotation cap showed; the gate's full list named 32 more. Safe ruff fixes, Black on #3566's own line ranges only, explicit strict= on batched, unused loop names, no-op hooks that say so, wrapped strings. FirstRecipient and RecipientLimit are frozen values, declared in ruff's immutable-calls beside the transcript page defaults. Every changed src/tools file now has no more ruff findings than main and no Black-clean file on main became dirty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XbFMSoWgC3ZwKzjT4SwMDu
# Conflicts: # src/agent_comms/installer/runtime_resolution.py
…rders ReadCommanderUnit was deleted by #3566; main's test kept importing it, so the module failed at import. It now builds ReadOperation(root, CommanderUnitRead). OperatorReader only forwarded outcome_viewer to its DisplayCut: OperatorOutcomes is now the ReadAuthority, so the cut itself is the reader. BusRead.apply only renamed read_as: one abstract apply remains, the name the tool boundary calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XbFMSoWgC3ZwKzjT4SwMDu
trissim
enabled auto-merge
October 30, 2026 11:42
trissim
disabled auto-merge
October 30, 2026 11:46
# Conflicts: # src/agent_comms/phase_evidence.py # tools/agent_comms_project.py
…cope open_requests sliced OwnerRequest.stream with islice and failure_evidence took next() of FailedTurnEvidence.stream, leaving each generator suspended inside TypedTable.iterate. The connection scope closed first; when the generator was collected, iterate's finally (typed_table.py:610) closed its cursor on a closed database and printed an ignored ProgrammingError traceback into the losing command's stderr in test_two_commanders_cannot_assign_the_same_unit_concurrently. Each stream is now closed by closing() inside the scope that owns its connection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XbFMSoWgC3ZwKzjT4SwMDu
…ection scope" This reverts commit 5993cb5.
…gent-comms into claude-nra/queue-unification
messages -> task_sources -> goals -> shell_effects -> storage_room -> activity -> thread_presentation -> notification_evidence -> audience_manifest -> bus_tables -> messages: bus_tables needs messages.BusTables while messages is half initialized. Every use in notification_evidence is an annotation, as on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XbFMSpSd8zQxyozgEV2Re7
Notification evidence records decode FrozenRecipient, so it must exist while messages is still importing; audience_manifest cannot supply it because it needs messages itself. The value depends on none of that. It moves to frozen_recipient.py and every importer moves with it, with no alias. Its writes fact imports the tables inside ClassFact, as channel_targets does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XbFMSpSd8zQxyozgEV2Re7
…from its owner The manifest's own validators still call ParticipantIdentity. The following-context test imported InputRequest from agent_backend, which no longer exports it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XbFMSpSd8zQxyozgEV2Re7
Conflicts were import lists and one docstring: native_input_record keeps ThreadLookup and drops main's now-unused WireName; phase_evidence takes main's Checkout history with the carrier's MergeHeadReview. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XbFMSpSd8zQxyozgEV2Re7
The carrier grew a TYPE_CHECKING import in tool_caller and a ClassFact type error in frozen_recipient. Both go. The local bus_tables import in writes stays, as in channel_targets.py:129: the tables import this module. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XbFMSpSd8zQxyozgEV2Re7
…e side by side Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XbFMSpSd8zQxyozgEV2Re7
trissim
enabled auto-merge
October 30, 2026 22:41
trissim
added a commit
that referenced
this pull request
Oct 31, 2026
#3670 made review-verdict refuse every head that isn't a captured combined carrier, so with --admin refused too, no ordinary PR could land by any route. A source's inputs now say whether they owe a carrier's member proof: an ordinary source owes nothing here and the next workflow step reads its head's verdict; a carrier keeps its member and promotion checks unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XbFMSpSd8zQxyozgEV2Re7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Combined carrier for #3566 on current main, paired with OpenHCSDev/toad#1359 (which replaced toad#1319). These land together: Toad's suite needs Core's TranscriptPageRead, and debt-tools type-checks Toad main against the new Core names.
Import cycle fixed at its owner: FrozenRecipient moved to frozen_recipient.py, below the bus tables, as a real runtime import. No TYPE_CHECKING, no alias; every importer moved. Its writes fact imports the tables inside ClassFact, as channel_targets does.
Proof: agent_comms.messages and agent_comms.tools import; 309 Toad modules import against this Core with 0 failures; 37 targeted tests passed (test_audience_manifest, test_wake, test_route_counts, test_following_context_notifications).
🤖 Generated with Claude Code
https://claude.ai/code/session_01XbFMSpSd8zQxyozgEV2Re7