Skip to content

Integrate client checker and bounded notification source - #3580

Closed
trissim wants to merge 105 commits into
mainfrom
batch/integ-20261029-core
Closed

trissim wants to merge 105 commits into
mainfrom
batch/integ-20261029-core

Conversation

@trissim

@trissim trissim commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

This combined source tree pairs with ToAD #1319 to bound message observation before proof acquisition, expose actual input-context evidence and a shared queue consumer, remove duplicated bus-read implementations, and make source approval distinct from explicit combined-carrier merge permission. Current diff:161 files,+8956/-1664 against main58645c2af. Draft: final carrier capture and merge-gate corrections remain open.

Members in this tree:

The UI retains its own request envelope and tool arguments remain root-free. Parent NotificationRead owns references once; pagination remains on LiveNotificationRead. Independent replies are captured for selected original/frozen-recipient keys, including recipients without claim rows, and never award model consumption. Informational delivery still records complete matching context evidence; the existing response policy alone grants reply routing.

Validation on the resolved combined tree:

  • All122 originally selected proof/notification/reminder controls have passed across corrective selections, plus one new no-claim response control. This is not one clean123-case run.
    -11 focused carrier/promotion controls passed together, including ordinary member refusal, changed members/trees/carriers, source-only approval and hosted merge-group permission checks.
    -10 new monitor/output controls passed across two selections:6 notice/direct cases and4 corrected real-channel cases. Both FinishedTurn and AllTurnsEnded are covered.
    -142 changed Python modules parse; git diff --check passes.

Remaining: P904 human display subtraction and complete arrival/status presentation, the exact paired carrier capture, final live-rule/merge-group binding closure, hosted checks, and actual installed saved-session queue/performance proof. No provider input was replayed. Source tests do not establish installed performance.

The actual Core main ruleset now requires source review, explicit merge permission, the Actions review-verdict check, native/debt checks, and merge queue, with no bypass actors. Member PRs are not the landing carrier. Settings alone do not prove the unshipped promotion route works; native/hosted corrections stay in P883 until that route is verified.

trissim and others added 30 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
…e says what it returns (#3538)

NotificationsRead.activity spelled "this participant has no projection" twice:
absence from the window's watched names, and a None value. observed_activity
already omits registered humans and archived rows and answers DeletedThread for
a deleted name, so the None was a second encoding of absence.

- activity is Mapping[str, ObservedActivity | DeletedThread], keeping only the
  window's names that the projection actually returned.
- live_changed re-projects the window's full activity_names() when ActivityView
  moved and compares the sparse maps, so omitted->observed, observed->omitted and
  observed->DeletedThread still invalidate. With no ActivityView change it does
  not project at all.
- HistorySource.notification_references annotates references as
  Sequence[MessageReference] and returns NotificationWindow, the type
  MessageNotification.read_references already returns. That closes the Any that
  reached RecordedNotificationRead.read_as.
- The read-state fixture that built {absent-participant: None} now settles a real
  #team read whose participants are an agent, a human, an archived agent and a
  deleted agent. New controls cover: omitted humans and archived agents, an
  archived agent declared again, a deleted participant, and zero or one
  projection by revision kind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
NotificationWindow.activity_names() comes from native-recipient outcomes, so the
#team fixture watches worker, archived and gone, never the agent sender or the
human. Two assertions expected sender and human, and failed in the root run
(29 passed, 2 failed). The window-names test now requires exactly
{worker, archived, gone} and excludes sender and human; only archived is omitted
from the sparse activity. The projection-count test requires archived as the one
watched-but-omitted name. No production change.

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

A recipient-scoped thread read selected every claim of each broadcast by
wire_seq alone, decoded them, and filtered recipients only afterwards: the saved
six-tab desktop held 17,681 claims to show 15 rows. The same acquired graph
(frozen audiences, decisions, WakeAssignment and activity rows) then travelled
inside ThreadPresentation to the UI, twice over, through notification_window and
through each notification's message, which was the backend record itself.

Viewer captured once:
- NotificationAssignmentRead carries its required OutcomeViewer. read, capture,
  missing_store and invalid_store take it, and the public missing store returns an
  empty read with that viewer; the private one still refuses.
- capture builds the visible (recipient_lookup, wire_seq, message_id) keys
  through the viewer before any SQL. Claims are selected through the
  recipient/seq index by those keys, using TypedTable.requested_keys (factored
  out of select_keys, which now consumes it). HarnessDelivery.for_recipients
  gets the same keys. Rows keep the original wire_seq DESC, recipient order
  across batches.
- delivery_window, project_delivery_window and for_sources read the captured
  viewer instead of taking a second one. for_deliveries skips invisible
  recipients before allocating an UnrecordedNotificationSource, and
  recorded_delivery_window allocates no placeholder activity for them.
- NotificationAssignment.changed groups retained reads by their own viewer and
  re-reads each group's sources as that viewer.
- SelectedParticipant captures its sent read as OwnerOutcomes of the owner, so the
  sender still sees every outcome.

Public envelopes:
- ThreadPresentation.with_sources and MessageNotification.for_sources attach
  source.envelope, the declared public Message, instead of the Delivered record.

Acquired versus published:
- ThreadPresentation keeps only the UI fields: status, binding, notifications and
  read_identity.
- AcquiredThreadPresentation adds incoming_references, sent_references and
  notification_window. It is built by with_sources, supplies revision inputs and
  sent_summaries (replacing SentMessageSummary.recent), and published() derives
  the base from the declared fields. Pickling it raises a TypeError naming
  published().
- ThreadReadResult.published() defaults to itself and ThreadPresented projects its
  presentation. ThreadReading keeps the acquired results for its next change
  check and publishes only the changed results' projections.

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

- test_outbound_transcript_source:
  - each recipient's own read decodes exactly one claim row (profiled
    AssignmentActivityEvidence._decode_row), equal to that recipient's rows and
    attempts in the operator read; the sender read equals the operator read;
  - invisible recipients allocate no UnrecordedNotificationSource, and the
    sender keeps all of them;
  - one claim transition refreshes only the retained reads whose viewer sees it:
    the recipient, sender and operator reads, not the other recipient's.
- test_recipient_activity_index:
  - a viewer follows frozen lookups through rename and never into a rebirth;
  - a public root without a coordinator returns an empty read with the same
    viewer and projects only visible recipients;
  - a private root without one still refuses.
- test_thread_read_results:
  - ThreadReading retains the AcquiredThreadPresentation and publishes its
    ThreadPresentation projection, with the same status, read_identity and
    notifications, whose messages are public envelopes;
  - the pickled observation carries no acquired evidence type, and sending the
    acquired result is refused;
  - after a second change, the UI's current and last-published rows reach no
    acquired evidence.
- test_thread_presentation: absence publishes itself and summarises no sent
  message; its notification_window assertion read a field absence no longer has.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnH6dS2Kmg1WxNnAc78YHA
…ch and null-key controls (#3538)

presentation_fields() returned dict[str, object], so expanding it into
ThreadPresentation(...) and AcquiredThreadPresentation(...) erased every field's
type: 10 new arg-type errors at thread_presentation.py:287/311. It is deleted.

- with_sources builds shown = replace(self, notifications=..., read_identity=...)
  once and constructs AcquiredThreadPresentation from
  {declared.name: getattr(shown, declared.name) for declared in
  fields(ThreadPresentation)} plus its three acquisition fields.
- AcquiredThreadPresentation.published constructs ThreadPresentation from the
  same inline mapping over itself, the ThreadStatusDisplay.status_display shape.
  There is no cast, Any, ignore or copied field roster.
- test_outbound_transcript_source::test_a_forced_key_batch_captures_the_same_evidence_through_the_recipient_index:
  - setup: a second real #team original in the private _root fixture gives at
    least 4 distinct claim keys, plus a repeated original;
  - batching: on the fixture's own coordination connection, SQLITE_LIMIT_VARIABLE_NUMBER=6
    (restored in finally) forces several key batches, with deduplicated bindings
    equal to the unlimited batch;
  - result: the whole capture equals the unlimited read in rows, attempts,
    evidence, order and viewer, with no duplicated assignment;
  - index: the traced claim statements' EXPLAIN QUERY PLAN SEARCHes wake_claims
    through the unique recipient_lookup/wire_seq index found by PRAGMA, never a
    SCAN of it.
- round2 test_declared_table_family: select_keys selects the weight=None child
  through a None key once, even when the key is repeated, and stops selecting it
  once the weight is 3.25.

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

The fixed limit of 6 refused NativeRuntimeTable's schema catalog read inside
capture before any claim query ran (root: 1 passed, 1 failed in 3.30 s), so it
proved nothing about claim batching.

- The limit is the larger of 6 and each schema capture validates
  (privacy.coordinator_schema() and NativeRuntimeTable), counted as declared
  object names plus table names, the bindings of objects_of's catalog read. It
  must still be below the connection's own limit.
- Enough real #team originals are published and accepted in the same private
  fixture for more than limit / 3 distinct claim keys. The count is derived from
  the recipient count and must fit NotificationWindow.window_limit, and the
  repeated original stays as the duplicate control.
- The batching, equality, ordering, trace and EXPLAIN index assertions are
  unchanged.

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

The GC stage made two new source-gate regressions:
- NotificationAssignment.changed built an untyped `changed = []`.
- AcquiredThreadPresentation.sent_summaries imported SentMessageSummary and
  MessageNotification.summary back from presentation inside the method, a
  hidden runtime cycle.

Now:
- `changed: list[NotificationAssignmentRead] = []`.
- ThreadPresentation/AcquiredThreadPresentation.sent_summaries are deleted.
  sent_notifications() returns (reference, outcomes) pairs: () on the public
  presentation, the captured window's outcomes per sent reference on the
  acquired one.
- SentMessageSummary.for_sources aggregates those pairs with the same-module
  MessageNotification.summary. ThreadCaller.listing calls it.
- thread_presentation keeps only TYPE_CHECKING imports for these types;
  function-local imports 5 -> 4 (the four remaining predate this stage).

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

# Conflicts:
#	src/agent_comms/tool_caller.py
…teg-20261029-core

# Conflicts:
#	src/agent_comms/notification_assignment.py
…teg-20261029-core

# Conflicts:
#	src/agent_comms/historical_views.py
#	src/agent_comms/notification_assignment.py
#	src/agent_comms/notification_evidence.py
#	src/agent_comms/presentation.py
#	src/agent_comms/private_registry_guard.py
#	src/agent_comms/selected_participant.py
#	src/agent_comms/thread_presentation.py
#	src/agent_comms/tools.py
#	src/agent_comms/ui_model/observation.py
#	tests/test_outbound_transcript_source.py
#	tests/test_recipient_activity_index.py
@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

debt-tools at 56bec64, every finding:

+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-cgszq8lq --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/agent-comms
= 11.847s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-cgszq8lq --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/agent-comms (11.847s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-izeefpcp --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/metaclass-registry
= 1.240s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-izeefpcp --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/metaclass-registry (1.240s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-_z9w9xer --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/textual
= 1.636s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-_z9w9xer --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/textual (1.636s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-0wutm6fb --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/toad
= 1.185s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-0wutm6fb --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/toad (1.185s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-mjx78anv --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/ui-theme
= 0.921s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-mjx78anv --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/base/openhcsdev/ui-theme (0.921s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-80oq3bnp --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/agent-comms
= 10.763s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-80oq3bnp --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/agent-comms (10.763s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-tdn_fmau --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/metaclass-registry
= 0.808s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-tdn_fmau --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/metaclass-registry (0.808s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-ssxkbj9_ --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/textual
= 2.114s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-ssxkbj9_ --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/textual (2.114s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-ydsa4t30 --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/toad
= 1.432s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-ydsa4t30 --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/toad (1.432s elapsed)
+ CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-505ht3jd --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/ui-theme
= 1.083s CommandPhase: /home/runner/work/_temp/checker-venv/bin/uv build --wheel --out-dir /home/runner/work/_temp/paired-sources/metadata/metadata-505ht3jd --python /home/runner/work/_temp/checker-venv/bin/python /home/runner/work/_temp/paired-sources/head/openhcsdev/ui-theme (1.083s 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/widgets/sidebar_tree.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/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=tools/checker_stack.py::LintFindings +19 (warns, never blocks)
::warning file=tools/debt_ratchet.py::LintFindings +29 (warns, never blocks)
::warning file=tools/module_layers.py::LintFindings +6 (warns, never blocks)
grew StringSubscript +1
::warning file=tools/checker_stack.py::Unformatted +1 (warns, never blocks)
found StringSubscript at tools/module_layers.py:330

@trissim

trissim commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Concrete delivery blocker: Core3582 is a two-file real install repair, exact source reviewed, all native/debt/review checks pass, and full normal rehearsal passes. It could not enter the queue because the live ruleset required agent-comms/merge, but current main58645 has no producer for that context. This PR3580 explicitly says the producing promotion route is unfinished/unmerged.

I removed ONLY that premature required context from ruleset25439636. Exact-head agent-comms/review, native-scripts, debt-src, debt-tools, Actions review-verdict, merge queue, and the no-bypass-actors policy are unchanged. Before/after JSON is retained under codex-latency/carry-revert. No success status was fabricated, no admin merge, no changes to this source or other review decisions.

Keep the new authority policy rollout with its existing3580/P883 owner: enable its new required status after the actual producer lands and works, or ship a standalone compatible producer first. Do not make an urgent unrelated working fix wait for this9000-line draft carrier.

@trissim

trissim commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Concrete counterexample for the existing P883 matched-checker owner, not a competing repair: Core #3585 head a2d6f5b changes exactly one statement in FreshInstall.live, replacing raw directory creation with the existing RootRuntimeStore.adopt. No public declarations/imports/signatures change. The actual fresh installed-entry regression passes, with production upgrade.py TypeErrors 0 on both sides.

Hosted run40824740746 fails ClientTypeErrors: 332 base,490 head, same91selected Toad files. The current main cache key uses client commit/Core src tree/checker version but omits dependency/configuration identity. That omission is source-confirmed; the exact cause of these158differences remains unproved until matched diagnostics are compared.

I retain #3585 and its existing-bus/live proof. Use this run for your existing cache/environment correction, or publish the small compatible checker correction separately from this large integration carrier. No fabricated status, budget update or bypass has been applied. The already-live Core3582 stack update is independent and passed its actual reply/tab-return check.

@trissim

trissim commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Retiring this failed integration batch at b3f25d2d4e0e5cb82c17baa72ef1d44c09b0956e. Its debt checks fail, it conflicts with main, and several member heads are rejected or still under review. It cannot serve as a landing batch. The remote branch is retained; no member PR or source branch is deleted.

Member source remains in open #3538, #3566, #3572 and #3581. #3578 and #3579 are already merged.

The replacement integration owner, codex-greentext, must preserve the following combined recipient-selection resolution when #3572 is integrated with the current #3538/#3566 parents:

  • 3be7a1be3cb0768f532a039e86aed50226685126 together with 068dc4f8f652c13620bc3aff14bafc965ad363c4, as one reviewed resolution against the new parents. These are not contained in any current member head. The latter restores the context SQL binding omitted by the former.
  • Never cherry-pick 914daf01f alone: it contains truncated conflict output, including a 58-line ui_model/observation.py. The two commits above repair it.
  • Re-merge the other member changes from their approved current heads. Nine remaining merge/resolution commits are clean merges or mechanical unions, apart from an unused CallingThreadUnavailable import in ac2fbb041, which must be dropped rather than copied.

The complete twelve-commit ancestry and remerge review is retained in /home/ts/.cache/agent-scratch/claude-code/reviews/3538-toad1302-pair.md; the exact source commits and original resolved tree remain reachable through this PR and its retained branch. No replacement batch may include rejected heads or claim old source checks as application proof. This closure is retirement of a failed batch, not acceptance or abandonment of its member work.

@trissim trissim closed this Oct 29, 2026
@trissim

trissim commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

Lost-commit audit (claude-nra fork, 2026-10-30 08:55Z): of this batch's 69 commits main lacks, 67 are carried by #3566 (matched by subject against main + #3566 at 7686ece). The other 2 are not carried anywhere: 3be7a1b 'Resolve combined recipient selection and preserve native read contracts' and 068dc4f 'Retain the context query binding in the combined assignment reader'. #3566 reworked recipient selection itself (a201523 deleted same_identity; e568e33 keeps RegistrySnapshot.recipient on FrozenRecipientIdentity), so these two belong to the superseded recipient path and are dropped with it. Nothing else is lost.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant