Skip to content

Batch round 0805: nine ready heads, merged and tested once - #2679

Merged
trissim merged 32 commits into
mainfrom
batch/round-0805
Oct 23, 2026
Merged

trissim merged 32 commits into
mainfrom
batch/round-0805

Conversation

@trissim

@trissim trissim commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Round one of batch merging: every ready head, merged once into one branch from main 6518c52 and tested once.

Carries #2654, #2657, #2658, #2660 (with #2664 inside it), #2662, #2663, #2665 and #2666 at ddda84193.

Dropped: #2661. It changed LockedStore.update to return DocumentChange and left four connector callers on the old contract, so /register answered 500 and 19 connector tests failed. That fix is pushed (eab807d58) and #2661 rides the next round.

Conflicts resolved once:

Test: 25 files touched by these PRs, -n 4, with PYTHONPATH=src so the hook test's child process imports the same tree: 424 passed, 1 failed. The failure, test_child_process.py::test_namespace_deadline_kills_group_escaping_descendant, fails the same way on main 6518c52 and is owned separately.

🤖 Generated with Claude Code

trissim and others added 30 commits October 23, 2026 03:45
A thread kept its turn twice: turn_record said whether a turn was live,
and last_finished_turn_id beside it said which one finished, with a
validator and four writers keeping the two in step (IDEN-5). The finished
turn is now a FinishedTurn member of the TurnRecord family; the field,
its validator and every writer that cleared it are gone.

- release_turn records FinishedTurn(turn_id); lease_turn's StoredTurnRecord
  replaces it, so nothing clears it by hand.
- TurnRecord.state() owns the TurnState capture; released() keeps a
  finished turn when the live reference is dropped; require_generation()
  refuses a finished turn without a prior generation.
- A stale metadata writer now keeps the previous turn record whole
  instead of nulling the finished id.

Carry: tools/cutover/carry_finished_turns.py moves each stored
last_finished_turn_id into turn_record. On a copy of the live bus:
194 threads, 49 finished IDs carried, second dry run reports 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stack.owners used Executor.map, which cancels the requests not yet started
the moment the first result raises, and drops every sibling failure but
the first. Submit every request, let the pool exit join them all, then
refuse naming each failed owner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
BusHistory, OwnerRequests and TurnOwner each decode the registry with
the target decoder, which refuses the retired last_finished_turn_id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pyproject addopts forced -n auto on every invocation, so running one touched
test spawned eight xdist workers; two such loops held the box at load 36.
The worker pool now lives on the two CI full-suite commands that want it.
The installer's -n 0 and the acceptance doc's -o addopts='' only existed to
undo the forced default, so both are deleted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lease_turn wrote turns/{lookup}-{N+1}.json and never deleted generation N, so
the live bus held 8,739 turn files for 51 current turns. The registry edit now
deletes the superseded file after it saves the registry naming N+1, inside the
same registry lock. A lease from a superseded generation asks the registry
snapshot whether its thread still names that generation before it reads its
file, so a late transition returns nothing instead of reading a deleted file.
Thread.turn_identity owns the (incarnation, generation) pair its three
callers spelled by hand. carry_dead_turns deletes the 8,688 dead files already
on disk and refuses if any thread's current file is missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DeadTurns decodes the registry, and the registry decoder refuses the
retired last_finished_turn_id field that FinishedTurns removes. Ordered
by addition date, whichever PR merged first decided whether the sweep
crashed on the old field. FinishedTurns rewrites only registry.json, so
declaring DeadTurns among the steps it precedes costs nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	src/agent_comms/threads.py
BusProjectionTables.create_schema replayed history with one header stream
plus one point SELECT per capability table per record (read_from), each
through the strict decode boundary. On a copy of the live bus (19,448
records) that replay took 46.5 s; the joined cursor takes 37.7 s with
identical projection rows. The reads were about 9 s of it; the rest is
project_into writing about 162k projection rows, which this change does
not touch.

BusCapability.history reads every record in sequence order from one
cursor: the history header LEFT JOINed to each capability table (members
derived from TypedTable.members_with(CapabilityRow)), each stored row
decoded once at its column offset. read_from and history share one
assembly step (BusCapability.assembled). The per-row replay loop in
create_schema is deleted; the projection stamp is unchanged.

TypedTable gains qualified() and decode_at() so a select joining tables
that share column names decodes each table's slice by declaration order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
BusProjectionTables.create_schema replayed history with one header stream
plus one point SELECT per capability table per record (read_from), each
through the strict decode boundary. On a copy of the live bus (19,448
records) that replay took 46.5 s; the joined cursor takes 37.7 s with
identical projection rows. Writes are not the remaining cost: a profile
puts all projection inserts at about 6 s. The rest is decoding records
and replaying claim transitions.

BusCapability.history reads every record in sequence order from one
cursor: the history header LEFT JOINed to each capability table (members
derived from TypedTable.members_with(CapabilityRow)), each stored row
decoded once at its column offset. read_from and history share one
assembly step (BusCapability.assembled). The per-row replay loop in
create_schema is deleted; the projection stamp is unchanged.

TypedTable gains qualified() and decode_at() so a select joining tables
that share column names decodes each table's slice by declaration order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every replayed claim called HeldResources.claims_for, which reselected
and fully decoded the current holders' Claimed rows that the same
in-order pass had already decoded.

HeldResources.project and apply now take a ClaimHolders member:
StoredHolders reads the holders of one live append from their table
(the read a separate append transaction needs); ReplayedHolders, owned
by create_schema's pass, carries the owners it already projected and
never rereads them. apply stays the one owner of which resources leave
and hands that set to the holders. The Claimed.replay_claim split is
folded into project_into.

On a copy of the live bus (19,457 records), the same joined-cursor pass
with stored holders took 41.5 s and with carried holders 34.5 s; every
projection table's contents are identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	src/agent_comms/thread_management.py
#	src/agent_comms/threads.py
# Conflicts:
#	src/agent_comms/native_session_files.py
# Conflicts:
#	src/agent_comms/bus_tables.py
Copilot AI balanced review requested due to automatic review settings October 23, 2026 08:16
@trissim
trissim merged commit 27043e0 into main Oct 23, 2026
2 checks passed

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 reached their quota limit.

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.

@github-actions

Copy link
Copy Markdown

debt-tools at bff3568, every finding:

grew NoneAbsence:tools/cutover/carry_finished_turns.py +2
grew StringSubscript +2
grew TypeErrors:tools/cutover/carry_finished_turns.py +2
grew Unformatted:tools/cutover/carry_owner_requests.py +1
introduced NoneAbsence:tools/cutover/carry_finished_turns.py at lines [91, 93]

@github-actions

Copy link
Copy Markdown

debt-src at bff3568, every finding:

grew FunctionLocalImport:src/agent_comms/turn_lease.py +1
grew GodClassExcess:src/agent_comms/typed_table.py::TypedTable +15
grew LintFindings:src/agent_comms/bus_tables.py +5
grew LintFindings:src/agent_comms/declared_family.py +1
grew LintFindings:src/agent_comms/diagnostics.py +1
grew LintFindings:src/agent_comms/historical_views.py +1
grew LintFindings:src/agent_comms/installer/backup.py +1
grew LintFindings:src/agent_comms/native_session_files.py +1
grew LintFindings:src/agent_comms/private_registry_guard.py +2
grew LintFindings:src/agent_comms/registry_document.py +1
grew LintFindings:src/agent_comms/registry_store.py +1
grew LintFindings:src/agent_comms/selected_tool_broker.py +1
grew LintFindings:src/agent_comms/turn_lease.py +1
grew LintFindings:src/agent_comms/typed_table.py +2
grew NoneAbsence:src/agent_comms/turn_lease.py +1
grew TypeErrors:src/agent_comms/bus_tables.py +2
grew TypeErrors:src/agent_comms/installer/upgrade.py +1
grew UncarriedRecordFormat +1
introduced NoneAbsence:src/agent_comms/turn_lease.py at lines [317]

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