Skip to content

Queue/notification UI, Core: integrate P872, P883 and P904 read operations - #3566

Merged
163 commits merged into
mainfrom
work/checkout-caller--P883
Oct 30, 2026
Merged

163 commits merged into
mainfrom
work/checkout-caller--P883

Conversation

@trissim

@trissim trissim commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Source correction at 7924d42 — four files, +52/-42

This is an additional source checkpoint, not merged/installed/application acceptance. All prior source, review history, failures and unfinished P904/P942 obligations below remain retained at their original revisions.

  • Before: ThreadPresentation.with_sources imports its consumer and activity module inside the method; the reader/presentation cycle exists at runtime. After: TranscriptSourceWindow.presentation (transcripts.py:646) owns acquisition/projection from its captured incoming and sent references; ThreadPresentation.read_sources (thread_presentation.py:251) consumes that behavior. Removed with_sources, its three local imports, the unused window import and the no-longer-used absent-member override. The window now does its own work instead of phoning itself for directions.
  • RequirementMarker (installer/runtime_resolution.py:376) decodes the external packaging Marker/null value once per requirement selection through MRO-declared handlers; both actual requirement readers use it. Deleted StackSources.requirement_active and the foreign marker absence decision. Marker evaluation, extras and unmarked-requirement semantics are retained.
  • ParticipantIdentity.require_canonical (participant_identity.py:26) retains its exact full-match regex and refusal, removing the nullable-match comparison; this is leaf text validation, not domain-state dispatch.
  • Source evidence: retained NRA import graph exposed the real cycle; source search across src/tests/tools found no remaining with_sources caller. Four edited files parse; git diff --check exits zero. Own correction is +52/-42, not the carrier's whole accumulated diff. Both existing remote refs read back exactly 7924d42cc9f1dc0cc383552fa5e05e4c4f16ac8f.
  • Not run by author: pytest, checkers, builds, imports, provider/runtime/rehearsal/install. No original or UNKNOWN input replay. Root owns exact paired qualification with current ToAD main +1319, filesystem watcher and retained-store partial-index carry, and installed mailbox/channel/CLI/native common-queue acceptance. Neither source inspection nor earlier copied-bus rehearsal establishes that journey.
  • Assumed: no other owner changed the captured-source contracts during publication. Original proof/cohort/session fences, drafts, mentions, raw unread/viewer records and independent-answer obligations remain binding. This checkpoint does not assert all remaining import findings or whole-feature obligations are closed.

Summary

122 commits; 171 files; +10,344 / -2,291 against main 71840842f. This is the durable Core queue/notification carrier for P872, P883, P904 and P924, paired with ToAD #1319 at 3a864eb45855f80f7271582ae98ab33404bc844f. Latest correction d09e763b6 is 1 test file, +2 / -0: prepare both heads on the fixture's existing Git reader before identity comparison. The preceding f8d56de40 corrects 3 test files, +7 / -7: fixture URL and filesystem consumers use existing SourceRepository.remote_path, not remote alias origin. Production is unchanged by both corrections; the six ownership repairs at 7035ada68 remain carried. Main 71840842f is included by normal merge. Current Core source head: d09e763b6f5b08c39e627e71bd288d5dbaf35206.

This is published source, not a claim of merge, installation or live UI acceptance. The final paired application qualification remains with the integration owner. Older source approvals and test runs do not approve this head automatically.

Read authority and display ownership (P872)

Before: UI/MCP/owner entrypoints reconstructed read authority, OperatorReader carried DisplayIdentity | NoDisplaySelection, and a class-body TYPE_CHECKING annotation pretended to own ThreadReadTool.thread.

After:

  • ReadAuthority owns the caller-side read contract in read_basis.py; the SQL BusReader remains a different acquisition role.
  • ToolCaller and OperatorReader inherit ReadAuthority.
  • DisplayIdentity and NoDisplaySelection inherit DisplayCut; no-display behavior derives through OperatorOutcomes rather than a duplicate return path. All members use outcome_viewer(registry: Registration), with no catch-all parameter. Empty slots on both bases preserve the slotted display values.
  • OwnerLiveRead inherits the real ThreadReadTool field. Runtime owner requests use the existing request declaration.
  • NotificationRead owns live versus recorded notification requests; ReadNotifications carries the operation and DisplayCut, not a second sibling union. Delivered resolves through its real bus_publication declaration. ToAD imports ReadNotifications from ui_model.observation; no notification_reads compatibility export was added.
  • Selected frozen recipient keys are bounded before evidence joins. Existing requested-key batching and main's SQL-derived queued presence are retained.

bus_read.py's latest repair removes 73 lines and adds 16. notification_reads.py removes 27 and adds 11. These are deleted competing contracts, not compatibility aliases.

Transcript source and painted-read lifetime

Before: nullable archive strings chose identity classes, position and through-cursor checks independently decided readability, and clients used cursor is None to infer whether compaction needed a commit.

After:

  • PageSource has LivePageSource and RecordedPageSource(HistorySourceKey). Each acquires its namespace and produces the correct read identity. ResolvedPageSource carries the acquired stores.
  • HistorySourceKey has its own nominal codec; archived notification and transcript requests carry it rather than an untyped identity string.
  • PagePosition/PageLimit carry latest/earlier/later/frontier/through behavior. TranscriptCursor.require_readable owns the runtime-and-offset contract.
  • TranscriptReadIdentity.page_source derives the next request's source. ACP and observation consumers use the same family.
  • ToAD's compaction consumers call requires_commit() rather than probing absence.
  • TranscriptCursor.painted_read(callback, nothing) chooses acknowledgement work before a worker or write context is opened. UnreadTranscriptCursor returns nothing without calling the callback. A no-op after acquiring SQLite was too late.

The unrelated Pi durable-store pagination token remains nullable: it is not a TranscriptCursor or a commit decision. The review's original broad grep was corrected on that source evidence.

The main transcript repair removes 85 lines and adds 75; the later painted-work dispatch adds nine lines. No old cursor-argument API or archive-string compatibility path is retained.

One response and one consumption fold (P904/P924)

Before: the publication producer and reply display scope separately decided who owed an answer; harness attempts and stored inputs were kept in parallel maps; optional answer references and optional timestamps could disagree.

After:

  • Message.awaits_answer_from(ThreadTarget) owns the obligation. Publication and RepliesDisplayScope both invoke it.
  • ConsumptionEvidence is implemented by the actual monitor/context facts. SourceEvidence and NotificationAssignmentRead each have one consumption mapping and one fold.
  • RecipientSelection.entries supplies the frozen key with its recipient. Native-input capture reuses it.
  • AnsweredProgress(proof, response: AnsweredResponse) owns the sole reply fact. MessageNotification.answer is a derived AssignmentResponse property; the separately stored field and disagreement exception are deleted. ContextualProgress preserves an underlying answer while retaining its independent evidence. Workflow completion (CompletedProgress) is not itself proof of a reply.
  • detail_texts() dispatches on the response, and AnsweredProgress derives its time label from the response it owns. The independent optional answered_at_ms field is deleted.
  • Harness rows no longer forward consumption behavior to their monitor. The evidence owns that behavior. Fifteen unconsumed arrival-count, arrival-label and input-detail methods are deleted; the actual consumption controls remain.
  • NativeInputContext.context_columns derives context-column construction from the existing FieldCodec plans and NativeContext declarations. The native writer no longer repeats a five-field wrapper roster or manually encodes the session-entry ID.

Wake presentation must not change message identity

Before: ClaudeBridge.wake used replace(message, body=...) to present a short wake notice. Delivery storage then saw a different content-derived message ID from the original attempt. The wake-specific reception path made that retained-row mismatch visible; adding an evidence parent to the row was not a valid explanation for changing its key.

After: the original Message is retained. PendingMonitor/WakeOnlyMonitor own the displayed frame body. The otherwise-unused BoardWake parent is deleted: WakeOnlyMonitor owns its notice text, and the collision control reads that actual owner instead of requiring test-only inheritance on ClaudeBridge. MonitorIncomingFrame carries the reception member. A wake cannot become proof that the original packet entered model context, including when its text happens to equal the original body.

Qualification and merge permission remain separate (P883)

  • Actual Core and companion source cuts are captured and checked together, including resolved merge trees and cut-local client import roots.
  • IntegrationCheckout.materialize(source, repository, path) owns creation and preparation. IntegrationSource takes the nominal SourceCheckout capability for its irreducible preparation behavior; head_review no longer imports integration, even under TYPE_CHECKING. The integration/todos import is module-level.
  • Checker preparation uses one private dependency solve, exact locked sync and mandatory dependency checking. Project metadata and strict native UV options retain their owners.
  • Source review does not itself arm a carrier. Current source authority, forge heads, ancestry/trees and queue policy are revalidated before promotion.
  • Existing reviewed/grown records remain compatible with the installed decoder; forge promotion is separate from saved source approval.
  • The latest source-root repair deletes raw nested TOML reads and the foreign toml_config_types["mypy_path"] lookup. It uses mypy's actual typed Options and native parser, retaining absent-versus-empty overrides and upstream string/list conversion. Parser diagnostics fail rather than silently selecting fallback roots.

Native proof schema and resumed caller closure

The first attempted move at 144d49ed7 was broken: making the journal inherit its file joined TypedTable and FILE family roots, and a real fresh import raised TypeError: A declaration cannot belong to two families. That bad relationship is deleted, not hidden by a delayed import.

Current structure: NativeContextJournal(..., DurableTables) remains a table. NativeInputProof(DurableFile, ReconciledFile, PatternFile, DurableTables) is the file/schema owner; table_schema() returns the file class and schema_rows() declares (NativeContextJournal,). Neither family inherits the other. The filename, table, fields, indexes and guard are retained. Session files and bus-file controls consume the actual file owner.

Core's transcript helper now consumes operation.page_source; display controls pass their actual registry; the wake collision control uses its actual monitor owner. The native proof-context fixture is named native_source, distinct from the deleted archive-selection API. The unrelated retained-result key original_historical_source_proofs is not an API alias. The two app/support callers originally attributed to Core belong to ToAD; its owner migrated them on #1319 at 3a864eb4, without a helper or alias.

Six retained P904 findings: declaration and consumer closure

  1. Dead API: 15 arrival_count / arrival_label / input_detail methods become zero. The harness control now asserts real consumption rather than dead presentation helpers.
  2. Milliseconds: MessageInput and ObservedMonitor inherit Timestamped; at_ms owns rounding and derives units from EventMilliseconds. Independent replies and their controls consume that projection, instead of disagreeing between int and round.
  3. Native range: eight production copies of the JS safe-integer bound become one declaration, NativeIntegerBounds.maximum. PositiveNativeIntegerBounds owns the positive interval. Context records, input references and file-write artifacts inherit the capability; SQLite checks and default generation intervals derive it. The input-reference encode/decode round trip is deleted; the nullable session value is narrowed at its boundary.
  4. Native verification: the private cross-module _verify_context is deleted. ContextCommitted.record_for normalizes the optional event against the required NativeContextRecord declaration. NativeEvidenceRead.verify_context joins the original input event with the normalized record and checks the acquired durable journal. Historical and private-send callers use that reader's corroborates_context method. Exact generation, context, identity, file and byte-position equality remain required.
  5. Dependency direction: by decision40136, the viewer/cursor/selection family is together in lower notification_evidence, not re-exported by read_basis. A fresh interpreter disproved the earlier prescribed direction. FrozenRecipientIdentity(ParticipantIdentity) owns the frozen pair and validation; FrozenRecipient adds projection-writing capability without dragging bus tables into the lower cursor. Native proof records no longer import session files, events or entry readers, even under TYPE_CHECKING; proof opening takes the actual path, and lineage comparison takes NativeInputLocation.
  6. Recipient limit: RecipientLimit(SingleFieldValue) owns scalar decoding, minimum/maximum, validation and schema. The tool, live/recorded reads, history/presentation APIs and direct controls all consume it. The repeated tool/selection validations are deleted. External wire size remains an integer.

The main merge retained the renamed Tristan notice on WakeOnlyMonitor, its actual owner, rather than resurrecting a second copy on ClaudeBridge.

Native session-value continuation

Before: reference acquisition fed an already nominal PiSessionId through the codec again, and group validation decoded the same reference fields before acquisition repeated that work. The first continuation wrongly gave every generic NativeColumn a session capability through NativeSessionIdValue. That abstraction and the column behavior are now deleted. NativeInputContext.reference_session_id acquires its raw column through the existing NativeContext(PiSessionId) directive. NativeRuntimeInput overrides with its actual NativeSessionSelection; PiSessionId returns itself and the absent member refuses required identity. The generic column no longer claims to know anything about sessions. The group validates remaining columns, deriving reference membership from the acquired reference's declaration instead of maintaining a field roster.

The existing native-reference controls now check retained nominal object identity, cursor-text acquisition, empty/wrong-type session columns and prior partial-group cases. They are UNRUN on this head. My initial claim that the old nominal re-decode would reject the value was false: NominalSpelling.decode overrides strict text decoding and accepts string subclasses. That claim was withdrawn in bus40493; this change removes redundant decoding, not a demonstrated live rejection.

Notification source invalidation: reads three, watches three

The prior notification_paths watched only coordinator commits while NotificationAssignment.capture_detached also acquired independent bus replies and stored inputs. An open notification interest could therefore remain stale after those sources changed. MEMB-2: fact = notification evidence sources; at presentation.py:799; derive by declaration on NotificationAssignment.source_paths (notification_assignment.py:173). That owner now declares coordinator, bus and input-disposition commit signals once; WireRevision.notifications derives them. The registry dependency remains on its existing revision field.

RecordedNotificationRead now inherits NotificationRead directly: live and recorded operations are siblings, not a false live subtype that overrides every live behavior.

Two controls in tests/test_notification_observation_sources.py, subsequently passed by root at 6e1714992, keep a real ObserveNotifications interest open through ObservationService and real pipe frames. One commits following-input model context only; one publishes an independent reply only. Both assert the coordinator signal stayed unchanged, inspect the refreshed actual outcomes and confirm another unchanged observation sends nothing. The input case checks exact context time without inventing a reply; the reply case checks the exact original response without inventing consumption. Shared private-root retirement is retained. Root's pass covers pipe observation, not filesystem watching or the installed application.

Explicit Git acquisition: delete nine calls to the removed branch API

Hosted base job 40953172544/123726343570 failed at integration.py:401 before paired-source qualification: WorkBranches.require_commits no longer exists. The whole-call inventory found six stale calls in integration.py and three in head_review.py, not merely the two initially identified. All nine now call Repo.require_commits(checkout, repository.remote, exact captured commits), whose existing implementation at repo.py:191 fetches only missing commits and validates them. Branch/parent/message readers remain WorkBranches; there is no restored alias, fetching copy or relaxed gate. Removed the now-unused local branch reader from MergedIntegrationSource.expected_tree.

The existing actual ordinary-CI and combined PR/queue controls now create the synthetic tested merge only at their real local Git origin. They assert it is absent locally before acquisition and assert exact tree/parents after checkout creation. Existing companion materialization, current-head revalidation and no merge/promotion-write assertions remain. A new control refuses a foreign-origin-only commit and an unavailable commit without creating the destination or changing local HEAD/tree or remote refs. These changed/new controls are UNRUN; root owns their continuation selection:

  • tests/test_integration.py::test_actual_ci_cli_uses_verified_carrier_not_synthetic_message_and_revalidates (both events)
  • tests/test_integration.py::test_actual_ordinary_ci_cli_captures_main_once_and_never_steals_an_unlinked_overlay
  • tests/test_integration.py::test_integration_checkout_refuses_commits_absent_from_its_own_origin
  • existing resolved-companion and merge-gate cases if needed to qualify the three corrected head-review callers.

The retained targeted NRA syntax map and whole-src/tests/tools call inventory establish the owner/caller closure. No full-corpus rescan, import matrix, test, checker, provider or runtime operation was run for this correction.

Prepared-reader correction at d09e763

Root's continuation at f8d56de40: 9 passed, 1 fixture failure, 2 root UV setup errors in 111.63s; 2 parameters unexecuted after maxfail3. The three original URL failures now pass. The remaining source omission was test_companion_input_changes_the_identity_of_an_unchanged_local_patch calling change_identity without preparing its existing WorkBranches reader. tests/test_head_review.py:549–550 now calls reader.prepare_pull_request("main", head.head) for both actual local heads before reading identities. This captures the real remote publication through its existing owner; no lazy acquisition, fallback, guessed base, new reader or production edit. Unprepared publication reads still refuse. Root corrects its own UV interpreter setup and runs only the five remaining failed/setup/unexecuted cases, carrying the nine passes and earlier first pass. Author ran no controls. Git publication preparation now precedes the read; chronology is an underrated dependency.

Fixture URL correction at f8d56de

Root's changed-owner run used combined Core 946a65a83 and ToAD 57e235e9: one passed, three failed at the fixture URL, eleven unexecuted after maxfail3. It did not reach the three failed cases' source-status behavior. The retained stack really contained git = "origin"; production correctly refused it.

tests/integration_source_fixture.py:86 now writes client.remote_path.as_uri(). Both plugin/client URL consumers at tests/test_checker_stack.py:170/:175 use the same owner. The whole-tests remote inventory also found four filesystem consumers in tests/test_source_revision.py:52/:57/:58/:65: those now pass source.remote_path as Git's working directory. The actual Git remote argument remains origin; SourceRepository.remote_path, Repo.acquire, validators, unknown UV settings, subdirectory and both-cut/incompatible-environment controls are unchanged. No second property or alias was added.

Author execution: no tests, checkers, builds, provider, runtime or installer. The first continuation selected only the three failed and eleven unexecuted cases, carrying the passed first case unchanged; its newer result and remaining five cases are recorded above. The existing source publication control additionally changed is tests/test_source_revision.py::test_seeded_source_publishes_descendants_and_merges_main_without_replacing_a_pr; root decides whether to include that exact newly corrected consumer. The earlier 35-Core pass and 12-ToAD partial run retain their original revision and interruption limits. Neither this fixture patch nor the growth table proves installed queue behavior.

Review corrections retained from 7035ada

  • Review lifetime: HeadVerdict.recording at todos.py:4141 owns merge withdrawal, source withdrawal, recording and final publication for both ordinary and carried verdicts. begin_review, the overriding second post path and unbound HeadReview.post are deleted. UnderReviewHeadVerdict needs no repeated final pending publication. Decided verdicts publish once after the record. Two permissions still require two pre-record withdrawals: the existing failed-permission-write and failed-final-rejection controls remain intact. This corrects the review's unsafe request to withdraw only merge permission; the counterexamples were supplied in native messages 41617/41631.
  • Source roles: LocalIntegrationSource at head_review.py:259 owns actual tested-head materialization and local ordinary-carrier validation; companion members retain their own reconstruction. IntegrationInput.local requires exactly one local member and companions derives membership from source declarations. Creation, validation, capture, canonical codec fixture and materialization consumers are migrated. Source-role selection no longer uses repository equality. Repository identity equality remains where it validates actual origins/carriers, not where it selects behavior.
  • Commit acquisition: Repo.acquire at repo.py:201 derives this repository's checkout and remote. All nine corrected integration/head-review callers now use that instance. The explicit-remote class API remains for its real distinct use.
  • ACP identity: OwnedTurn decodes its incoming session identity at owned_turn.py:140; OwnedSend.session_id carries AcpSessionId. Both consumer constructor casts in turn_input_source.py are deleted. The existing native following-input route is unchanged; no queue or delivery API was added.
  • Local package membership: StackSources.local_packages at installer/runtime_resolution.py:422 reads the actual source declarations once through its existing boundary. Both runtime compatibility and CheckerStack.local_names consume that owner; the separate hard-coded Core membership is deleted. A new control covers both documents, normalized spelling, branch/path sources and excluded registry entries.
  • Dead exception: the repeated IntegrationFailed arm is removed from the catch whose parent ToolRefusedError already covers it.
  • Scanner instruction superseded by root41785/15: the attempted ModuleBindings optimization and new old-scanner guard were removed with a revision-checked reverse patch. Compared against published audit Test the public fields that pinned Toad builds read from agent-comms #7 d7401d6: scripts/audit/lexical.py:165 owns ModuleScope, cached scope/declaration facts and decorator binding. Core debt_ratchet.py is only the CI entry; the pinned audit owns every measure #3606/claude-nra owns the replacement pin. No scanner wrapper, competing lexical owner or restoration of a deleted path was added. The obsolete local patch is retained only as review evidence in owned scratch.
  • Pair linkage: root41785/14 explicitly retains ToAD fix(P354): remove obsolete goal pause file checks #1319 at 3a864eb4. That declaration is not a claim that hosted capture has selected it: the integration owner must read the actual captured SHA. Core-alone landing remains forbidden.

All current controls are UNRUN by the author. Root's changed-owner continuation includes the two retained failure controls (test_failed_permission_withdrawal_preserves_the_preceding_source_record, test_failed_final_rejection_status_leaves_the_member_pending_not_old_green), changed reviewing/qualification publication controls, local/companion canonical input and capture/materialization controls, test_local_package_membership_comes_from_both_source_declarations, and the acquisition controls listed above. No unchanged 35-control rerun, author checker, environment, provider, UI, service or installer action occurred. git diff --check passed. Both carrier refs were read back at the current head; the tree was clean after publication.

Evidence

  • Root at previous 6e1714992: 35 selected Core controls passed in 17.53 seconds, including both new observer pipe controls, native references, ordinary/following proof, viewer/source provenance and generic partial-index/schema cases. This does not prove filesystem wakeup, real retained-store carry or installed behavior. Paired ToAD 3a864eb4 selection passed 12 cases before its 160-second limit interrupted the remaining queue cases; no clean full-head OK. The failing required hosted base above still blocked qualification; it is the reason for 4a27c246a, not an environment excuse.

  • Historical 6e1714992 imports: eight independent fresh-process imports passed: native_input_proof, cli_bridges.shared, transcripts, bus_read, notification_assignment, read_basis, native_entries, pi_events. Reverse import order also passed after the main merge. This is import/binding evidence, not test or application acceptance.

  • Historical 6e1714992 syntax corpus: 1,842 modules, 8,570 classes, 33,204 selected sites, zero parse omissions; per-file hashes recorded. Core src/tests/tools are paired with immutable Git blobs from ToAD 3a864eb4, not the author's reused checkout. This is not a debt score or a complete dynamic-binding proof.

  • Exact paired ToAD source/test search found no callers of the deleted native helpers or qualified old viewer APIs. Locks are byte-identical to published 3a5e351a8 (uv.lock 0f41cbc2…; stack lock 5bc260ec…).

  • Earlier ordered six-file selection: 99 passed, 14 warnings, 75.39 s; exit 0. Includes real monitor delivery, automatic reply, following-context notification, historical notification, wake index and live-context notification controls. The board-wake exact-body-collision control remains intact. This predates the newer rule that channels never automatically receive finals: it does not qualify that rule or the permanent P924 Info-reply-loop repair.

  • Revision precision: pytest loaded 22c0ef74e; the subsequent nine-line d06847ad0 cursor dispatch addition was published while that run was active. Its dispatch, the nominal archive codec and required imports were checked separately in a fresh process. Do not describe the 99-test result as a fresh full selection at d06847ad0.

  • The earlier required four-module imports passed in forward and reverse order, and native mypy-options acquisition passed. These were before the resumed correction; they are not fresh import or runtime proof at 144d49ed7.

  • AST map: 938 modules, 7,720 classes, no parse omissions. The final paired ToAD source root differs from the initial frozen root; these totals are coverage, not a before/after debt score. Dynamic ResolvedPageSource.identity constructors and protocol callbacks were traced semantically; this syntax census does not prove all interprocedural bindings or runtime execution.

  • if TYPE_CHECKING: 271 current / 272 main. No historical_source, TranscriptCursor | None, displayed_cursor is None or work_cursor remains in Core production.

  • Resumed AST coverage: retained map of 1,520 Core/test/tool/paired-production modules, 8,402 classes, no parse omissions. Targeted reproof at 144d49ed7 parsed the 18 changed Python modules (267 classes, 359 selected sites, no parse omissions); no second full scan. Syntax evidence is not dynamic binding or application proof.

  • Both carrier refs read back at 6e17149921b3f0617e8218a65fa4ed532ca19536. Existing Queue/notification UI, Core: integrate P872, P883 and P904 read operations #3566 remains the carrier. P883's released review/history was not rewritten. Earlier holder updates were refused while the order was under review. After the actual send-back, the corrected P904 delivery claim was recorded on order 89c621070727 at this head (message c870115ffe15). That claim is not acceptance. Current-pair qualification and application proof remain open.

  • No author pytest, checker, environment, provider, service or install operation ran under the resumed order. git diff --check and source inspection are not test acceptance.

  • The retained hosted debt-tools run 40915225731 reported 14 new client diagnostics in seven ToAD files; debt-src was cancelled. They are old-pair diagnostics, not a current-head success or a demonstrated current-head failure. Current paired qualification belongs to root, without aliases to preserve obsolete client APIs.

  • One initial test invocation collected zero because an extra outer lease duplicated pytest's existing lease owner. That wrapper was removed; it is not counted as a test result.

  • Warnings include pytest trying to retire pre-existing foreign DQDock bind snapshots under /tmp/pytest-of-ts/garbage-96d90360-4e92-4438-8a4d-7b9bf512a204 and receiving EBUSY. Those mounts and their source data were not deleted or unmounted by this work.

Retained source maps, consumer searches and result logs: /home/ts/.cache/agent-scratch/queue-core-fix/. One-shot migration source was deleted after application.

Remaining acceptance

The integration owner must review Core d09e763b6 with ToAD 3a864eb4, include the current P872/audit replacement through its existing owner, execute only the five remaining failed/setup/unexecuted changed-owner cases above and require hosted base to reach actual qualification. Do not rerun the unchanged 35 Core controls as reassurance. Root also owns the unfinished ToAD queue-case diagnosis and current-pair debt/application qualification. Changed risks: fresh forward/reverse imports and persisted codecs; native proof file/schema membership, unchanged generated native contract and existing partial-index carry; integration/head-review materialization; open-window input-only/reply-only controls (tests/test_notification_observation_sources.py), native reference/session-value and partial-group controls (tests/test_native_context_reference.py); transcript pages and viewer-boundary controls; wake collision; historical/live/following independent replies. Existing 27/13 fixture corrections remain carried, not rerun here.

P904 stays an application order. Ordinary/following/grouped model context must show Consumed with its actual time before the answer; weak acceptance/transcript persistence is insufficient. Original-reference/recipient/birth and source fences, arrived/waiting/unknown/passive facets, independent replies, exact context-ack equality and bounded pre-join windows remain required. Raw human read marks/unread/other viewers remain unchanged while the source-matching viewer's self-read is omitted only from the display. Root still owes archive/rename/recreated-incarnation controls, the real declared-index carry rehearsal and the installed copied-session mailbox/channel/DM common-queue journey. Source/agent mismatches refuse before mutation; unsupported actions remain explicit and programming errors escape. No original or uncertain input may be replayed.

No merged, installed, current-checker-green or whole-feature success is claimed. No live package or saved user session was modified.

replace the message body to send a shorter notice
message gets a new ID
ask where the original row went

The row did not go anywhere. We sent its stunt double.

Growth

Measured by the existing WorkBranches.source_growth / CodeLines.of owners against immutable main 71840842f683e7453cdb0b635fb443671a5e5ab8 at head f8d56de408858035ff8b82f4fe064a6884408994. 56 growing production files; +2,570 positive code lines; +2,072 net production code lines. Code lines exclude leading imports, module docstrings, comments and blanks; these are not physical diff additions. The earlier estimate of 57 files is not substituted for this exact owner result. Each row names the gained mechanism, including moved behavior; growth alone does not prove a new semantic authority.

Production is byte-for-byte unchanged between measured f8d56de40 and current d09e763b6 (git diff --exit-code f8d56de40 d09e763b6 -- src/agent_comms returned 0); the final commit changes only the fixture preparation above.

Source file Net code growth Required mechanism gained
src/agent_comms/agent_backend.py +7 InputStartListener gains original and following context callbacks, with explicit no acknowledgement behavior for unowned listeners.
src/agent_comms/backend.py +89 TrackedInputContext joins each actually sent command with its input event and corroborated context, before terminal output.
src/agent_comms/bus.py +8 BusReader acquires first independent replies only for bounded frozen recipient and original pairs, before decoding message bodies.
src/agent_comms/bus_read.py +32 BusRead shares operation declarations between UI and tool readers, while OwnerLiveRead obtains answers from the actual native owner.
src/agent_comms/bus_tables.py +19 AnsweredSource selects each frozen author's first reply using bounded indexed original references, avoiding complete reply body acquisition.
src/agent_comms/child_process.py +1 SpawnedProcess accepts the selected LaunchRuntime so private checker commands execute under their common interpreter rather than the host runtime.
src/agent_comms/cli_bridges/monitor.py +4 ReaderState and AttachedReader carry monitor reception through attachment preparation, preserving whether the emitted body proves original consumption.
src/agent_comms/cli_bridges/shared.py +10 MonitorIncomingFrame derives wake text from reception without replacing Message identity, and transport retirement belongs to recorded monitor evidence.
src/agent_comms/cli_verbs/merge_review.py +1 MergeReviewCliCommand passes declared repositories to exact combined source and merge permission validation, instead of approving source alone.
src/agent_comms/commander/judge.py +22 QualifyVerb records incomplete source qualification separately from acceptance, while PromoteVerb explicitly validates and enqueues only a captured combined carrier.
src/agent_comms/connector.py +3 ToolsCallMessage translates declared relation, record, branch and transport refusals into structured tool failures while unrelated programming errors still escape.
src/agent_comms/following_inputs.py +4 OwnerFollowingInputs derives its current lease and claimed generation from the real sender instead of retaining the initial queue scope.
src/agent_comms/harness_delivery_states.py +27 ObservedMonitor owns timed original body consumption; WakeOnlyMonitor owns wake presentation and cannot promote a notice into original context proof.
src/agent_comms/head_review.py +433 IntegrationSource and IntegrationInput own exact cross repository cuts, typed local and companion roles, canonical metadata, and separate source versus merge permissions.
src/agent_comms/historical_views.py +6 HistorySource gains nominal archive identity and forwards bounded recipient cursors through recorded namespace notification reads without live identity substitution.
src/agent_comms/history_views.py +1 HistoryViews exposes recipient cursor and size limits to the notification owner so evidence acquisition is bounded before expensive joins.
src/agent_comms/input_attempt.py +29 ContextConsumedInput retains the first corroborated native context on its existing started binding, refusing contradictory continuations without inventing a reply.
src/agent_comms/input_disposition.py +23 InputDispositions atomically acknowledges exact original input batches after checking every member and the current registry turn lease before writes.
src/agent_comms/installer/runtime_resolution.py +416 ProjectMetadata and StackSources prepare backend derived package metadata and lossless native UV settings for one shared baseline and candidate checker environment.
src/agent_comms/integration.py +464 IntegrationRun captures and materializes actual Core and companion graphs together; event members validate ordinary, merge group and main provenance without granting source checks promotion authority.
src/agent_comms/messages.py +4 Message owns the recipient answer obligation used by publication and display, while inherited Timestamped supplies the common millisecond projection.
src/agent_comms/native_entries.py +40 NativeEvidenceRead verifies input and normalized context against acquired transcript bytes and indexed journal, without granting acceptance or replay authority.
src/agent_comms/native_input_proof.py +167 Proof identity, journal and file schema move below session execution to break import cycles while retaining exact lineage and durable evidence checks.
src/agent_comms/native_integer.py +17 NativeIntegerBounds declares the JavaScript safe integer interval once, deriving validation, generation ranges and SQLite constraints for native proof consumers.
src/agent_comms/native_prompt_send.py +6 PromptBoundary declares context acknowledgement on the existing send boundary, with explicit absence of durable recipient acknowledgement for unowned prompts.
src/agent_comms/native_runtime_input.py +7 NativeRuntimeInput records first context separately from triage settlement, derives immutable context columns, and keeps session identity on its actual selection.
src/agent_comms/native_session_reopen.py +4 NativeSessionSelection owns required recorded identity: PiSessionId returns itself while absence refuses, eliminating generic column session capabilities and redundant decoding.
src/agent_comms/native_transcript.py +1 NativeTranscript consumes required indexed journal evidence through NativeInputLocation and translates missing context into its existing publication refusal instead of probing nullable proof.
src/agent_comms/notification_evidence.py +184 RecipientSelection and OutcomeViewer bound frozen audiences before evidence joins; SourceEvidence folds real consumption once while retaining independent responses and source scoped human read visibility.
src/agent_comms/notification_progress.py +14 DeliveryProgress composes actual timed context with an independent AnsweredResponse, removing the separate answer copy and preventing workflow completion from fabricating replies.
src/agent_comms/notification_read.py +29 NotificationsRead keeps captured evidence and participant activity with the backend read owner, replacing observation side reconstruction with source specific change checks.
src/agent_comms/notification_reads.py +28 LiveNotificationRead and RecordedNotificationRead share one operation contract while preserving their own namespace, display authority, recipient bounds and revision dependencies.
src/agent_comms/order_tools.py +79 Shared read declarations expose native input delivery, live owner goals, bounded transcript pages and commander unit detail through existing authority and acquisition owners.
src/agent_comms/owned_send.py +27 OwnedSend acknowledges original and following context under the current registry lease and exact source binding, using its already nominal ACP session identity.
src/agent_comms/owned_turn.py +3 OwnedTurn decodes incoming ACP identity and supplies actual cursor delivery to the native run, allowing context evidence to reach disposition acknowledgement.
src/agent_comms/owner_connection.py +42 OwnerReading and OwnerReplyConnection bind requests to one native incarnation, reject external owners before dispatch, and bound the actual socket response lifetime.
src/agent_comms/participant_identity.py +22 FrozenRecipientIdentity separates stable send time participant identity from projection writing, allowing lower notification selectors without importing bus writer dependencies.
src/agent_comms/phase_evidence.py +1 RulesetGates verifies both source and merge permission contexts plus the actual merge queue, and refuses any configured bypass actors.
src/agent_comms/pi_commands.py +17 Prompt and FollowingPrompt dispatch corroborated context to existing distinct input callbacks, so following commitments cannot restart the original prompt or inbox.
src/agent_comms/pi_events.py +19 ContextCommitted normalizes optional foreign acknowledgement fields into required NativeContextRecord and checks original input identity before independent durable corroboration.
src/agent_comms/pi_native_backend.py +1 PiNativeBackend forwards the owning turn's cursor delivery into stream_agent_events, preserving the evidence reader required by live native context acknowledgement.
src/agent_comms/private_send_claim.py +15 PrivateSendClaim acknowledges corroborated context while the model is active through the existing coordinator transaction, retaining original dispatch and evidence fences.
src/agent_comms/private_send_stage.py +1 NativeSendStage owns shared context acknowledgement; FullNativeSend supplies its irreducible response fence and triage verdict settlement remains separate from first consumption.
src/agent_comms/private_sidecar.py +9 The native request digest includes actual image payloads and streaming behavior, so context corroboration matches the complete command rather than text alone.
src/agent_comms/read_basis.py +32 ReadAuthority and DisplayCut declare caller access and source matched human visibility, replacing nullable display unions and duplicate operator versus owner read contracts.
src/agent_comms/recorded_time.py +7 Timestamped owns Unix seconds to integer milliseconds conversion for messages and observed monitor proof, eliminating independently rounded consumer timestamps.
src/agent_comms/registry_provenance.py +5 RegisteredRecipient exposes its actual input owner through the recipient family, while archived and absent members return no live owner without downstream type switches.
src/agent_comms/repo.py +3 Repo.acquire retains checkout and remote ownership at the repository instance, and repository_url shares actual origin identity decoding with declared stack sources.
src/agent_comms/started_input.py +1 StartedInput normalizes the raw ACP session selector before nominal acceptance updates and release, preserving the existing queue state publication sequence.
src/agent_comms/thread_presentation.py +32 AcquiredThreadPresentation retains private notification evidence with its reader and publishes only presentation values, preventing evidence transport leakage and repeated sent summary reconstruction.
src/agent_comms/todos.py +50 Existing Todo and head review owners gain cross repository source links, qualification decisions, safe verdict recording and exact current source authority without individual source auto merging.
src/agent_comms/transcripts.py +66 PagePosition, PageLimit and PageSource own bounded live and archived traversal; cursor owned readability and painted acknowledgement replace nullable decisions and needless write acquisition.
src/agent_comms/turn_input_source.py +13 TurnAcceptance retains nominal session identity and checks that following context still belongs to its actual accepted source before acknowledging durable consumption.
src/agent_comms/turn_inputs.py +1 InputForwarding registers accepted following commands with the existing native turn's context tracker before commitment events arrive, without creating another queue.
src/agent_comms/typed_table.py +17 RequestedKeys factors declared key encoding, deduplication and SQLite variable limited indexed joins for native evidence readers, rather than rebuilding or post filtering whole tables.
src/agent_comms/work_branches.py +7 WorkBranches reads exact commit parents and messages through existing explicit acquisition, letting integration validate graph ancestry and canonical metadata without copying Git readers.

trissim and others added 19 commits October 27, 2026 12:43
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
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
@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

debt-src at a678243, every finding:

+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-yijs4dn0 --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/agent-comms
= 14.697s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-yijs4dn0 --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/agent-comms (14.697s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-s7bdqs50 --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/metaclass-registry
= 1.052s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-s7bdqs50 --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/metaclass-registry (1.052s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-my00omay --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/textual
= 1.956s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-my00omay --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/textual (1.956s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-leufiktl --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/toad
= 1.299s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-leufiktl --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/toad (1.299s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-oc_myvlm --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/ui-theme
= 1.234s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-oc_myvlm --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/ui-theme (1.234s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-1ci6o9c8 --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/agent-comms
= 18.562s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-1ci6o9c8 --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/agent-comms (18.562s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-cynzv04q --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/metaclass-registry
= 0.765s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-cynzv04q --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/metaclass-registry (0.765s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-o415qp7t --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/textual
= 1.640s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-o415qp7t --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/textual (1.640s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-fwv8ytsx --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/toad
= 1.063s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-fwv8ytsx --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/toad (1.063s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-h00fjztx --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/ui-theme
= 0.833s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-h00fjztx --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/ui-theme (0.833s elapsed)
grew ClientTypeErrors:batrachian-toad/src/toad/acp/agent.py +1: this change removes or changes an agent_comms name that client file uses; run mypy on it to see which
grew ClientTypeErrors:batrachian-toad/src/toad/acp/agent_controller.py +1: this change removes or changes an agent_comms name that client file uses; run mypy on it to see which
grew ClientTypeErrors:batrachian-toad/src/toad/screens/commander.py +1: this change removes or changes an agent_comms name that client file uses; run mypy on it to see which
grew ClientTypeErrors:batrachian-toad/src/toad/screens/historical_sessions.py +1: this change removes or changes an agent_comms name that client file uses; run mypy on it to see which
grew ClientTypeErrors:batrachian-toad/src/toad/session_scope.py +7: this change removes or changes an agent_comms name that client file uses; run mypy on it to see which
grew ClientTypeErrors:batrachian-toad/src/toad/transcript_publication.py +2: this change removes or changes an agent_comms name that client file uses; run mypy on it to see which
grew ClientTypeErrors:batrachian-toad/tests/textual/test_historical_notification_source.py +1: this change removes or changes an agent_comms name that client file uses; run mypy on it to see which
::warning file=src/agent_comms/todos.py::GodClassExcess +12 (warns, never blocks)
::warning file=src/agent_comms/typed_table.py::GodClassExcess +13 (warns, never blocks)
::warning file=src/agent_comms/acp_extension.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/agent_activity.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/agent_backend.py::LintFindings +2 (warns, never blocks)
::warning file=src/agent_comms/backend.py::LintFindings +4 (warns, never blocks)
::warning file=src/agent_comms/bus_read.py::LintFindings +2 (warns, never blocks)
::warning file=src/agent_comms/bus_tables.py::LintFindings +2 (warns, never blocks)
::warning file=src/agent_comms/child_process.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/cli_verbs/merge_review.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/commander/judge.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/head_review.py::LintFindings +22 (warns, never blocks)
::warning file=src/agent_comms/historical_views.py::LintFindings +2 (warns, never blocks)
::warning file=src/agent_comms/history_views.py::LintFindings +3 (warns, never blocks)
::warning file=src/agent_comms/input_attempt.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/input_disposition.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/installer/runtime_resolution.py::LintFindings +20 (warns, never blocks)
::warning file=src/agent_comms/integration.py::LintFindings +62 (warns, never blocks)
::warning file=src/agent_comms/native_entries.py::LintFindings +2 (warns, never blocks)
::warning file=src/agent_comms/native_file_artifact.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/native_input_proof.py::LintFindings +2 (warns, never blocks)
::warning file=src/agent_comms/native_input_record.py::LintFindings +2 (warns, never blocks)
::warning file=src/agent_comms/native_prompt_send.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/native_runtime_input.py::LintFindings +2 (warns, never blocks)
::warning file=src/agent_comms/native_transcript.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/notification_assignment.py::LintFindings +2 (warns, never blocks)
::warning file=src/agent_comms/notification_evidence.py::LintFindings +14 (warns, never blocks)
::warning file=src/agent_comms/notification_read.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/notification_reads.py::LintFindings +3 (warns, never blocks)
::warning file=src/agent_comms/order_tools.py::LintFindings +4 (warns, never blocks)
::warning file=src/agent_comms/owned_send.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/owned_turn.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/owner_connection.py::LintFindings +2 (warns, never blocks)
::warning file=src/agent_comms/pi_commands.py::LintFindings +2 (warns, never blocks)
::warning file=src/agent_comms/pi_events.py::LintFindings +2 (warns, never blocks)
::warning file=src/agent_comms/presentation.py::LintFindings +5 (warns, never blocks)
::warning file=src/agent_comms/prompt_state.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/read_basis.py::LintFindings +5 (warns, never blocks)
::warning file=src/agent_comms/registry_provenance.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/repo.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/selected_participant.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/selected_triage.py::LintFindings +1 (warns, never blocks)
::warning file=src/agent_comms/todos.py::LintFindings +2 (warns, never blocks)
::warning file=src/agent_comms/transcript_updates.py::LintFindings +3 (warns, never blocks)
::warning file=src/agent_comms/transcripts.py::LintFindings +4 (warns, never blocks)
::warning file=src/agent_comms/bus_read.py::Unformatted +1 (warns, never blocks)
::warning file=src/agent_comms/native_input_proof.py::Unformatted +1 (warns, never blocks)
::warning file=src/agent_comms/notification_reads.py::Unformatted +1 (warns, never blocks)
found ClientTypeErrors:batrachian-toad/src/toad/acp/agent.py at batrachian-toad/src/toad/acp/agent.py:18 (Module "agent_comms.ui_model.observation" has no attribute "ReadOwnerGoal"  [attr-defined])
found ClientTypeErrors:batrachian-toad/src/toad/acp/agent_controller.py at batrachian-toad/src/toad/acp/agent_controller.py:26 (Module "agent_comms.ui_model.observation" has no attribute "ReadDeliveryOverview"  [attr-defined])
found ClientTypeErrors:batrachian-toad/src/toad/screens/commander.py at batrachian-toad/src/toad/screens/commander.py:17 (Module "agent_comms.ui_model.observation" has no attribute "ReadCommanderUnit"  [attr-defined])
found ClientTypeErrors:batrachian-toad/src/toad/screens/historical_sessions.py at batrachian-toad/src/toad/screens/historical_sessions.py:12 (Module "agent_comms.ui_model.observation" has no attribute "ReadRecordedNotifications"  [attr-defined]) batrachian-toad/src/toad/screens/historical_sessions.py:32 (Incompatible types in assignment (expression has type "partial[Any]", variable has type "type[ReadNotifications]")  [assignment])
found ClientTypeErrors:batrachian-toad/src/toad/session_scope.py at batrachian-toad/src/toad/session_scope.py:170 (Unexpected keyword argument "before" for "ReadTranscript"  [call-arg]) batrachian-toad/src/toad/session_scope.py:170 (Unexpected keyword argument "after" for "ReadTranscript"  [call-arg]) batrachian-toad/src/toad/session_scope.py:170 (Unexpected keyword argument "through" for "ReadTranscript"  [call-arg]) batrachian-toad/src/toad/session_scope.py:170 (Unexpected keyword argument "historical_source" for "ReadTranscript"  [call-arg]) batrachian-toad/src/toad/session_scope.py:172 (Argument 2 to "ReadTranscript" has incompatible type "str"; expected "TranscriptPageRead"  [arg-type]) batrachian-toad/src/toad/session_scope.py:185 (Unexpected keyword argument "after" for "ReadTranscriptUpdate"  [call-arg]) batrachian-toad/src/toad/session_scope.py:185 (Argument 2 to "ReadTranscriptUpdate" has incompatible type "str"; expected "TranscriptPageRead"  [arg-type])
found ClientTypeErrors:batrachian-toad/src/toad/transcript_publication.py at batrachian-toad/src/toad/transcript_publication.py:275 ("TranscriptReadIdentity" has no attribute "after"  [attr-defined]) batrachian-toad/src/toad/transcript_publication.py:428 (Unexpected keyword argument "through" for "replace" of "TranscriptReadIdentity"  [call-arg])
found ClientTypeErrors:batrachian-toad/tests/textual/test_historical_notification_source.py at batrachian-toad/tests/textual/test_historical_notification_source.py:5 (Module "agent_comms.ui_model.observation" has no attribute "ReadRecordedNotifications"  [attr-defined])
found LintFindings:src/agent_comms/acp_extension.py at src/agent_comms/acp_extension.py:57 (E501 Line too long (128 > 100)) src/agent_comms/acp_extension.py:714 (B008 Do not perform function call `LivePageSource` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable)
found LintFindings:src/agent_comms/agent_activity.py at src/agent_comms/agent_activity.py:25 (I001 [*] Import block is un-sorted or un-formatted)
found LintFindings:src/agent_comms/agent_backend.py at src/agent_comms/agent_backend.py:320 (B027 `InputStartListener.original_context_committed` is an empty method in an abstract base class, but has no abstract decorator) src/agent_comms/agent_backend.py:323 (B027 `InputStartListener.following_context_committed` is an empty method in an abstract base class, but has no abstract decorator)
found LintFindings:src/agent_comms/backend.py at src/agent_comms/backend.py:593 (UP046 Generic class `NativeCommitObservation` uses `Generic` subclass instead of type parameters) src/agent_comms/backend.py:624 (E501 Line too long (105 > 100)) src/agent_comms/backend.py:625 (E501 Line too long (109 > 100)) src/agent_comms/backend.py:692 (E501 Line too long (101 > 100))
found LintFindings:src/agent_comms/bus_read.py at src/agent_comms/bus_read.py:2 (I001 [*] Import block is un-sorted or un-formatted) src/agent_comms/bus_read.py:10 (E501 Line too long (101 > 100))
found LintFindings:src/agent_comms/bus_tables.py at src/agent_comms/bus_tables.py:209 (B911 `itertools.batched()` without an explicit `strict` parameter) src/agent_comms/bus_tables.py:209 (E501 Line too long (113 > 100))
found LintFindings:src/agent_comms/child_process.py at src/agent_comms/child_process.py:2303 (B008 Do not perform function call `LaunchRuntime.running` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable)
found LintFindings:src/agent_comms/cli_verbs/merge_review.py at src/agent_comms/cli_verbs/merge_review.py:45 (E501 Line too long (112 > 100))
found LintFindings:src/agent_comms/commander/judge.py at src/agent_comms/commander/judge.py:677 (E501 Line too long (101 > 100))
found LintFindings:src/agent_comms/head_review.py at src/agent_comms/head_review.py:138 (E501 Line too long (104 > 100)) src/agent_comms/head_review.py:150 (E501 Line too long (103 > 100)) src/agent_comms/head_review.py:153 (E501 Line too long (105 > 100)) src/agent_comms/head_review.py:168 (E501 Line too long (111 > 100)) src/agent_comms/head_review.py:221 (E501 Line too long (101 > 100)) src/agent_comms/head_review.py:223 (E501 Line too long (110 > 100)) src/agent_comms/head_review.py:247 (E501 Line too long (105 > 100)) src/agent_comms/head_review.py:256 (E501 Line too long (113 > 100)) src/agent_comms/head_review.py:292 (E501 Line too long (101 > 100)) src/agent_comms/head_review.py:343 (E501 Line too long (109 > 100)) src/agent_comms/head_review.py:348 (E501 Line too long (104 > 100)) src/agent_comms/head_review.py:352 (E501 Line too long (103 > 100)) src/agent_comms/head_review.py:366 (B007 Loop control variable `branch` not used within loop body) src/agent_comms/head_review.py:394 (E501 Line too long (101 > 100)) src/agent_comms/head_review.py:479 (E501 Line too long (101 > 100)) src/agent_comms/head_review.py:480 (E501 Line too long (105 > 100)) src/agent_comms/head_review.py:495 (E501 Line too long (105 > 100)) src/agent_comms/head_review.py:499 (E501 Line too long (115 > 100)) src/agent_comms/head_review.py:501 (E501 Line too long (101 > 100)) src/agent_comms/head_review.py:754 (E501 Line too long (120 > 100)) src/agent_comms/head_review.py:771 (E501 Line too long (105 > 100)) src/agent_comms/head_review.py:805 (E501 Line too long (104 > 100))
found LintFindings:src/agent_comms/historical_views.py at src/agent_comms/historical_views.py:370 (B008 Do not perform function call `FirstRecipient` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable) src/agent_comms/historical_views.py:371 (B008 Do not perform function call `RecipientLimit` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable)
found LintFindings:src/agent_comms/history_views.py at src/agent_comms/history_views.py:227 (B008 Do not perform function call `FirstRecipient` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable) src/agent_comms/history_views.py:227 (B008 Do not perform function call `RecipientLimit` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable) src/agent_comms/history_views.py:230 (E501 Line too long (104 > 100))
found LintFindings:src/agent_comms/input_attempt.py at src/agent_comms/input_attempt.py:469 (E501 Line too long (107 > 100))
found LintFindings:src/agent_comms/input_disposition.py at src/agent_comms/input_disposition.py:471 (E501 Line too long (105 > 100))
found LintFindings:src/agent_comms/installer/runtime_resolution.py at src/agent_comms/installer/runtime_resolution.py:113 (E501 Line too long (107 > 100)) src/agent_comms/installer/runtime_resolution.py:212 (E501 Line too long (111 > 100)) src/agent_comms/installer/runtime_resolution.py:216 (E501 Line too long (102 > 100)) src/agent_comms/installer/runtime_resolution.py:217 (E501 Line too long (101 > 100)) src/agent_comms/installer/runtime_resolution.py:218 (E501 Line too long (101 > 100)) src/agent_comms/installer/runtime_resolution.py:223 (E501 Line too long (103 > 100)) src/agent_comms/installer/runtime_resolution.py:229 (E501 Line too long (105 > 100)) src/agent_comms/installer/runtime_resolution.py:242 (E501 Line too long (112 > 100)) src/agent_comms/installer/runtime_resolution.py:259 (E501 Line too long (111 > 100)) src/agent_comms/installer/runtime_resolution.py:266 (E501 Line too long (112 > 100)) src/agent_comms/installer/runtime_resolution.py:271 (E501 Line too long (107 > 100)) src/agent_comms/installer/runtime_resolution.py:279 (E501 Line too long (112 > 100)) src/agent_comms/installer/runtime_resolution.py:299 (E501 Line too long (117 > 100)) src/agent_comms/installer/runtime_resolution.py:368 (E501 Line too long (106 > 100)) src/agent_comms/installer/runtime_resolution.py:374 (E501 Line too long (110 > 100)) src/agent_comms/installer/runtime_resolution.py:435 (E501 Line too long (119 > 100)) src/agent_comms/installer/runtime_resolution.py:439 (E501 Line too long (111 > 100)) src/agent_comms/installer/runtime_resolution.py:456 (E501 Line too long (119 > 100)) src/agent_comms/installer/runtime_resolution.py:466 (E501 Line too long (106 > 100)) src/agent_comms/installer/runtime_resolution.py:655 (E501 Line too long (108 > 100))
found LintFindings:src/agent_comms/integration.py at src/agent_comms/integration.py:24 (E501 Line too long (296 > 100)) src/agent_comms/integration.py:73 (B027 `FailureAction.require_qualification` is an empty method in an abstract base class, but has no abstract decorator) src/agent_comms/integration.py:156 (E501 Line too long (105 > 100)) src/agent_comms/integration.py:328 (E501 Line too long (113 > 100)) src/agent_comms/integration.py:347 (E501 Line too long (102 > 100)) src/agent_comms/integration.py:362 (E501 Line too long (102 > 100)) src/agent_comms/integration.py:379 (E501 Line too long (109 > 100)) src/agent_comms/integration.py:385 (E501 Line too long (107 > 100)) src/agent_comms/integration.py:394 (E501 Line too long (105 > 100)) src/agent_comms/integration.py:403 (E501 Line too long (127 > 100)) src/agent_comms/integration.py:409 (E501 Line too long (110 > 100)) src/agent_comms/integration.py:414 (E501 Line too long (123 > 100)) src/agent_comms/integration.py:424 (E501 Line too long (110 > 100)) src/agent_comms/integration.py:444 (E501 Line too long (103 > 100)) src/agent_comms/integration.py:459 (E501 Line too long (108 > 100)) src/agent_comms/integration.py:468 (E501 Line too long (122 > 100)) src/agent_comms/integration.py:476 (E501 Line too long (102 > 100)) src/agent_comms/integration.py:493 (E501 Line too long (104 > 100)) src/agent_comms/integration.py:494 (E501 Line too long (106 > 100)) src/agent_comms/integration.py:495 (E501 Line too long (105 > 100)) src/agent_comms/integration.py:497 (E501 Line too long (109 > 100)) src/agent_comms/integration.py:500 (E501 Line too long (111 > 100)) src/agent_comms/integration.py:507 (E501 Line too long (109 > 100)) src/agent_comms/integration.py:516 (E501 Line too long (129 > 100)) src/agent_comms/integration.py:519 (E501 Line too long (104 > 100)) src/agent_comms/integration.py:520 (E501 Line too long (104 > 100)) src/agent_comms/integration.py:530 (E501 Line too long (105 > 100)) src/agent_comms/integration.py:539 (E501 Line too long (112 > 100)) src/agent_comms/integration.py:550 (E501 Line too long (112 > 100)) src/agent_comms/integration.py:554 (E501 Line too long (109 > 100)) src/agent_comms/integration.py:573 (E501 Line too long (101 > 100)) src/agent_comms/integration.py:574 (E501 Line too long (109 > 100)) src/agent_comms/integration.py:611 (E501 Line too long (102 > 100)) src/agent_comms/integration.py:620 (E501 Line too long (112 > 100)) src/agent_comms/integration.py:621 (E501 Line too long (117 > 100)) src/agent_comms/integration.py:628 (E501 Line too long (106 > 100)) src/agent_comms/integration.py:646 (E501 Line too long (106 > 100)) src/agent_comms/integration.py:647 (E501 Line too long (107 > 100)) src/agent_comms/integration.py:651 (E501 Line too long (112 > 100)) src/agent_comms/integration.py:659 (E501 Line too long (119 > 100)) src/agent_comms/integration.py:661 (E501 Line too long (102 > 100)) src/agent_comms/integration.py:665 (E501 Line too long (106 > 100)) src/agent_comms/integration.py:666 (E501 Line too long (130 > 100)) src/agent_comms/integration.py:682 (E501 Line too long (108 > 100)) src/agent_comms/integration.py:684 (E501 Line too long (101 > 100)) src/agent_comms/integration.py:686 (E501 Line too long (105 > 100)) src/agent_comms/integration.py:694 (E501 Line too long (108 > 100)) src/agent_comms/integration.py:701 (E501 Line too long (112 > 100)) src/agent_comms/integration.py:705 (E501 Line too long (115 > 100)) src/agent_comms/integration.py:710 (E501 Line too long (104 > 100)) src/agent_comms/integration.py:712 (E501 Line too long (127 > 100)) src/agent_comms/integration.py:824 (E501 Line too long (103 > 100)) src/agent_comms/integration.py:826 (E501 Line too long (101 > 100)) src/agent_comms/integration.py:828 (E501 Line too long (104 > 100)) src/agent_comms/integration.py:830 (E501 Line too long (104 > 100)) src/agent_comms/integration.py:832 (E501 Line too long (110 > 100)) src/agent_comms/integration.py:835 (E501 Line too long (105 > 100)) src/agent_comms/integration.py:838 (E501 Line too long (123 > 100)) src/agent_comms/integration.py:857 (E501 Line too long (101 > 100)) src/agent_comms/integration.py:860 (E501 Line too long (114 > 100)) src/agent_comms/integration.py:892 (E501 Line too long (101 > 100)) src/agent_comms/integration.py:896 (E501 Line too long (108 > 100))
found LintFindings:src/agent_comms/native_entries.py at src/agent_comms/native_entries.py:42 (E501 Line too long (114 > 100)) src/agent_comms/native_entries.py:441 (E501 Line too long (102 > 100)) src/agent_comms/native_entries.py:442 (E501 Line too long (102 > 100))
found LintFindings:src/agent_comms/native_file_artifact.py at src/agent_comms/native_file_artifact.py:15 (E501 Line too long (102 > 100))
found LintFindings:src/agent_comms/native_input_proof.py at src/agent_comms/native_input_proof.py:7 (I001 [*] Import block is un-sorted or un-formatted) src/agent_comms/native_input_proof.py:170 (E501 Line too long (105 > 100))
found LintFindings:src/agent_comms/native_input_record.py at src/agent_comms/native_input_record.py:370 (E501 Line too long (120 > 100)) src/agent_comms/native_input_record.py:413 (E501 Line too long (120 > 100))
found LintFindings:src/agent_comms/native_prompt_send.py at src/agent_comms/native_prompt_send.py:40 (B027 `PromptBoundary.context_committed` is an empty method in an abstract base class, but has no abstract decorator)
found LintFindings:src/agent_comms/native_runtime_input.py at src/agent_comms/native_runtime_input.py:35 (F811 [*] Redefinition of unused `FreshNativeId` from line 22: `FreshNativeId` redefined here) src/agent_comms/native_runtime_input.py:43 (F401 [*] `.native_input_record.NumberNativeColumn` imported but unused) src/agent_comms/native_runtime_input.py:45 (F401 [*] `.native_input_record.TextNativeColumn` imported but unused)
found LintFindings:src/agent_comms/native_transcript.py at src/agent_comms/native_transcript.py:392 (E501 Line too long (107 > 100))
found LintFindings:src/agent_comms/notification_assignment.py at src/agent_comms/notification_assignment.py:30 (E501 Line too long (142 > 100)) src/agent_comms/notification_assignment.py:217 (E501 Line too long (107 > 100)) src/agent_comms/notification_assignment.py:238 (E501 Line too long (104 > 100)) src/agent_comms/notification_assignment.py:256 (E501 Line too long (106 > 100)) src/agent_comms/notification_assignment.py:305 (E501 Line too long (102 > 100))
found LintFindings:src/agent_comms/notification_evidence.py at src/agent_comms/notification_evidence.py:3 (I001 [*] Import block is un-sorted or un-formatted) src/agent_comms/notification_evidence.py:38 (F401 [*] `.coordination_tables.harness_deliveries.HarnessDelivery` imported but unused) src/agent_comms/notification_evidence.py:82 (E501 Line too long (107 > 100)) src/agent_comms/notification_evidence.py:132 (B008 Do not perform function call `FirstRecipient` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable) src/agent_comms/notification_evidence.py:133 (B008 Do not perform function call `RecipientLimit` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable) src/agent_comms/notification_evidence.py:138 (E501 Line too long (112 > 100)) src/agent_comms/notification_evidence.py:199 (E501 Line too long (102 > 100)) src/agent_comms/notification_evidence.py:200 (E501 Line too long (109 > 100)) src/agent_comms/notification_evidence.py:252 (E501 Line too long (125 > 100)) src/agent_comms/notification_evidence.py:264 (E501 Line too long (125 > 100)) src/agent_comms/notification_evidence.py:280 (E501 Line too long (101 > 100)) src/agent_comms/notification_evidence.py:281 (E501 Line too long (110 > 100)) src/agent_comms/notification_evidence.py:283 (E501 Line too long (106 > 100)) src/agent_comms/notification_evidence.py:310 (E501 Line too long (105 > 100))
found LintFindings:src/agent_comms/notification_read.py at src/agent_comms/notification_read.py:4 (I001 [*] Import block is un-sorted or un-formatted)
found LintFindings:src/agent_comms/notification_reads.py at src/agent_comms/notification_reads.py:2 (I001 [*] Import block is un-sorted or un-formatted) src/agent_comms/notification_reads.py:11 (E501 Line too long (102 > 100)) src/agent_comms/notification_reads.py:32 (E501 Line too long (103 > 100))
found LintFindings:src/agent_comms/order_tools.py at src/agent_comms/order_tools.py:72 (E501 Line too long (116 > 100)) src/agent_comms/order_tools.py:216 (E501 Line too long (105 > 100)) src/agent_comms/order_tools.py:622 (E501 Line too long (108 > 100)) src/agent_comms/order_tools.py:629 (E501 Line too long (101 > 100))
found LintFindings:src/agent_comms/owned_send.py at src/agent_comms/owned_send.py:3 (I001 [*] Import block is un-sorted or un-formatted)
found LintFindings:src/agent_comms/owned_turn.py at src/agent_comms/owned_turn.py:3 (I001 [*] Import block is un-sorted or un-formatted)
found LintFindings:src/agent_comms/owner_connection.py at src/agent_comms/owner_connection.py:169 (E501 Line too long (105 > 100)) src/agent_comms/owner_connection.py:300 (E501 Line too long (105 > 100))
found LintFindings:src/agent_comms/pi_commands.py at src/agent_comms/pi_commands.py:59 (I001 [*] Import block is un-sorted or un-formatted) src/agent_comms/pi_commands.py:260 (E501 Line too long (105 > 100))
found LintFindings:src/agent_comms/pi_events.py at src/agent_comms/pi_events.py:3 (I001 [*] Import block is un-sorted or un-formatted) src/agent_comms/pi_events.py:444 (E501 Line too long (104 > 100))
found LintFindings:src/agent_comms/presentation.py at src/agent_comms/presentation.py:201 (B008 Do not perform function call `FirstRecipient` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable) src/agent_comms/presentation.py:201 (B008 Do not perform function call `RecipientLimit` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable) src/agent_comms/presentation.py:238 (E501 Line too long (116 > 100)) src/agent_comms/presentation.py:248 (B008 Do not perform function call `FirstRecipient` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable) src/agent_comms/presentation.py:249 (B008 Do not perform function call `RecipientLimit` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable) src/agent_comms/presentation.py:289 (E501 Line too long (116 > 100)) src/agent_comms/presentation.py:716 (E501 Line too long (105 > 100))
found LintFindings:src/agent_comms/prompt_state.py at src/agent_comms/prompt_state.py:3 (I001 [*] Import block is un-sorted or un-formatted)
found LintFindings:src/agent_comms/read_basis.py at src/agent_comms/read_basis.py:11 (F401 [*] `.bus_tables.AwaitingAnswerSource` imported but unused) src/agent_comms/read_basis.py:14 (F401 [*] `.class_fact.ClassFact` imported but unused) src/agent_comms/read_basis.py:19 (F401 [*] `collections.abc.Sequence` imported but unused) src/agent_comms/read_basis.py:31 (F401 [*] `.field_codec.RecordMember` imported but unused) src/agent_comms/read_basis.py:31 (E501 Line too long (118 > 100)) src/agent_comms/read_basis.py:31 (F401 [*] `.field_codec.ThreadLookup` imported but unused) src/agent_comms/read_basis.py:37 (F401 [*] `.projection_source.ProducedRow` imported but unused)
found LintFindings:src/agent_comms/registry_provenance.py at src/agent_comms/registry_provenance.py:3 (I001 [*] Import block is un-sorted or un-formatted) src/agent_comms/registry_provenance.py:240 (E501 Line too long (111 > 100))
found LintFindings:src/agent_comms/repo.py at src/agent_comms/repo.py:3 (I001 [*] Import block is un-sorted or un-formatted)
found LintFindings:src/agent_comms/selected_participant.py at src/agent_comms/selected_participant.py:253 (E501 Line too long (117 > 100))
found LintFindings:src/agent_comms/selected_triage.py at src/agent_comms/selected_triage.py:28 (I001 [*] Import block is un-sorted or un-formatted)
found LintFindings:src/agent_comms/todos.py at src/agent_comms/todos.py:8 (I001 [*] Import block is un-sorted or un-formatted) src/agent_comms/todos.py:6284 (E501 Line too long (115 > 100))
found LintFindings:src/agent_comms/transcript_updates.py at src/agent_comms/transcript_updates.py:3 (I001 [*] Import block is un-sorted or un-formatted) src/agent_comms/transcript_updates.py:79 (E501 Line too long (116 > 100)) src/agent_comms/transcript_updates.py:85 (E501 Line too long (121 > 100))
found LintFindings:src/agent_comms/transcripts.py at src/agent_comms/transcripts.py:120 (E501 Line too long (102 > 100)) src/agent_comms/transcripts.py:271 (E501 Line too long (102 > 100)) src/agent_comms/transcripts.py:303 (E501 Line too long (102 > 100)) src/agent_comms/transcripts.py:583 (E501 Line too long (103 > 100)) src/agent_comms/transcripts.py:689 (B008 Do not perform function call `LatestPage` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable) src/agent_comms/transcripts.py:690 (B008 Do not perform function call `AtFrontier` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable) src/agent_comms/transcripts.py:718 (B008 Do not perform function call `LatestPage` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable) src/agent_comms/transcripts.py:719 (B008 Do not perform function call `AtFrontier` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable) src/agent_comms/transcripts.py:720 (B008 Do not perform function call `LivePageSource` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable) src/agent_comms/transcripts.py:735 (B008 Do not perform function call `LatestPage` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable) src/agent_comms/transcripts.py:736 (B008 Do not perform function call `AtFrontier` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable) src/agent_comms/transcripts.py:737 (B008 Do not perform function call `LivePageSource` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable)
found Unformatted:src/agent_comms/bus_read.py at src/agent_comms/bus_read.py:1 (Black would reformat this file)
found Unformatted:src/agent_comms/native_input_proof.py at src/agent_comms/native_input_proof.py:1 (Black would reformat this file)
found Unformatted:src/agent_comms/notification_reads.py at src/agent_comms/notification_reads.py:1 (Black would reformat this file)

trissim and others added 2 commits October 28, 2026 08:51
…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
@trissim
trissim marked this pull request as ready for review October 28, 2026 12:57
Copilot AI balanced review requested due to automatic review settings October 28, 2026 12:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

trissim and others added 5 commits October 28, 2026 09:21
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
trissim and others added 18 commits October 30, 2026 01:52
# Conflicts:
#	src/agent_comms/installer/runtime_resolution.py
same_identity hand-compared recipient_lookup and canonical_thread, which
FrozenRecipient already declares as its fields; it existed only because
the direct-read path built a bare FrozenRecipientIdentity that dataclass
equality would not match. Build the audience entry there, delete
same_identity, and ask membership with not-in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RecordedGoalHistory is the goal-history tool's reply once the GoalHistory
wrapper is gone, so it is a ToolResult; WakeOnlyMonitor.confirm returns
the MessageReference tuple MonitorEvidence declares.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	src/agent_comms/child_process.py
#	src/agent_comms/integration.py
#	src/agent_comms/owner_connection.py
Main (#3589/#3661) owns the commander wake on every bridge and hands the
Monitor the original message, so the branch's Claude-only wake override
goes, and with it WakeOnlyMonitor, the PendingMonitor family it was the
second member of, the reception parameter threaded through monitor.py and
shared.py, and the frame's displayed body. Their one test goes with them.
CommanderUnitDetail is both the comms_commander_unit tool reply and a
disclosed record, so it inherits ToolResult and DisclosedRecord.
commander_pings imports the DeliveredOutcome main's notice returns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TestedSources.validate refused whenever live main differed from the tested
merge's base, so on a fleet that merges every few minutes the PR's own base
and debt jobs died with 'main moved; recapture' before measuring anything
(two consecutive runs on #3566). The event owns the freshness answer:
require_current_mains stays on TestedSources for the merge group and main
push, and PullRequestSources answers that its captured bases stand, since
the merge group re-proves currency before anything lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Narrowing RegistrySnapshot.recipient to FrozenRecipient broke the
observe_recipients caller (agent_activity.py:181, TypeErrors +1), and
narrowing the evidence path instead would import audience_manifest into
notification_evidence, a cycle. FrozenRecipientIdentity is the declaration
that stays free of the projection writers; every runtime recipient is the
FrozenRecipient audience entry, so membership still compares dataclasses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@trissim
trissim disabled auto-merge October 30, 2026 09:17
@trissim
trissim marked this pull request as draft October 30, 2026 09:17
trissim added a commit that referenced this pull request Oct 30, 2026
debt-src/debt-tools named them (run 41028710035): I001 blocks, E501 lines,
Black-dirty new files, F401/B007 in tools/module_layers.py, and B008 on the
frozen page-window defaults. LatestPage, AtFrontier and LivePageSource are
frozen value types, so ruff's flake8-bugbear immutable-calls owns that fact.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XbFMSoWgC3ZwKzjT4SwMDu
trissim added a commit that referenced this pull request Oct 30, 2026
# Conflicts:
#	src/agent_comms/tool_caller.py
trissim added a commit that referenced this pull request Oct 30, 2026
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
trissim pushed a commit that referenced this pull request Oct 30, 2026
…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 trissim closed this pull request by merging all changes into main in bdba5bd Oct 30, 2026
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.

2 participants