Skip to content

Session relaunch: lock the roster against the timer snapshot; carry a partial run's unfinished rows (#1116 follow-ups b, c) - #1141

Merged
seathatflowsinourveins merged 7 commits into
mainfrom
claude/cc-native-practice-relaunch-roster-carry-20261015
Oct 16, 2026
Merged

seathatflowsinourveins merged 7 commits into
mainfrom
claude/cc-native-practice-relaunch-roster-carry-20261015

Conversation

@seathatflowsinourveins

@seathatflowsinourveins seathatflowsinourveins commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Scope

  • What this PR changes, in one or two sentences: Session relaunch (Session relaunch after an unplanned WSL restart: a roster timer and a boot oneshot on the systemd user manager #1116) holds a short roster.lock around the timer snapshot's carry read and roster write, and around a run's state save and seed roster. It also keeps carrying a partial run's unfinished rows in this boot's roster.
  • Base commit: fa6cf1a2360bbd1be810045819f222e4432e12bc (main). The branch was rebuilt onto it on 2026-10-16 (see Rebuild 2 below). Its earlier bases were 5bab63233 (Rebuild 1) and, originally, 0f4ee075b.
  • Lane: lane:foundation
  • Owned paths touched:
    • tools/session-relaunch/session_relaunch.py and tools/session-relaunch/README.md;
    • tests/test_session_relaunch.py;
    • docs/decisions/2026-10-14-session-relaunch-after-wsl-restart.md (the tests line);
    • evidence/artifacts/session-relaunch-20261014/README.md (the test count) and evidence/artifacts/session-relaunch-20261014/receipts/sources.json (the flock sources);
    • manifests/evidence.json.

This is #1116 follow-ups (b) and (c), in the command center's order after (a) (#1140). Both concern what this boot's roster carries, so they share one PR. Source: the local review's P2s at 7aad5a0 (review_output.json).

(b): line 496 at 7aad5a0, the seed roster write at c8d7fa9 line 514.

  • Problem: a timer snapshot could read its carry before a starting relaunch saved its state, then write after the relaunch's seed roster. The seed was replaced with the live list only, possibly empty. A crash before the next snapshot then lost every pending lane.
  • Fix:
    • roster_lock() takes flock on roster.lock in the state directory.
    • The snapshot (snapshot_live) holds it from its carry read to its roster write.
    • The relaunch holds it while it writes the seed roster and then saves its in_progress state (seed first, since fix round 1).
    • Both holders are short; relaunch.lock stays the run's own lock.

(c): line 222 at 7aad5a0, carried_rows at c8d7fa9 lines 221-223.

  • Problem: only an in_progress state carried its rows. The first snapshot after a partial run dropped the lanes the relaunch had not brought back, and a second crash lost them.
  • Fix: a partial state of this boot carries them too, minus the ones listed live.
  • Scope: the carry stays limited to the boot that ran the relaunch. The next boot's relaunch starts a fresh state file.

SOTA sources

Evidence-class table

Claim Evidence class Command / receipt
A snapshot paused between its carry read and its write replaced a starting relaunch's seed with an empty roster at c8d7fa9 synthetic test_a_timer_snapshot_never_overwrites_the_seed_roster_of_a_starting_relaunch: a thread runs main snapshot with a 1 s pause inside the carry; the relaunch starts in that pause. It failed on c8d7fa9 with the roster set empty.
The lock, not the reorder, closes the race synthetic The same test with roster_lock replaced by a no-op and the reorder kept: FAILED
A partial run's rows were dropped by the next snapshot at c8d7fa9 synthetic test_a_partial_relaunch_keeps_its_unfinished_rows_in_this_boots_roster. It failed on c8d7fa9: the roster held only the live trading-cc.
All pass after the fix synthetic python3 -m unittest tests.test_session_relaunch: 53 tests OK; the race test passed in three repeated runs

Local commands run

$ python3 -m unittest tests.test_session_relaunch -k never_overwrites_the_seed -k keeps_its_unfinished_rows   # at c8d7fa9e
FAILED (failures=2)
$ python3 -m unittest tests.test_session_relaunch
Ran 53 tests; OK
$ python3 scripts/validate.py
rc 0; {"components": 71, "hashed_files": 12040, "profiles": 4, "receipts": 240, "status": "passed"}

Decision record

docs/decisions/2026-10-14-session-relaunch-after-wsl-restart.md: its tests line gains these follow-ups and the count of 53.

Fix round 1 (d9c6dd0)

From GPT full1 at 474cf2b (full1-pr1141/GPT-VERDICT-474cf2b65.md) and the local review at 474cf2b (review_output.json). The command center released this commit at 2026-10-15T10:49Z.

Finding Fix Fail-first test (failed at 474cf2b, passes at d9c6dd0)
GPT blocker 1 (P2, reproduced): a boot stop between save() and the seed roster left the timer's empty roster of this boot as the newest, and the next boot opened nothing cmd_snapshot() (the seed) now runs before save(), both still under roster_lock; this is the reviewer's seed-first control test_a_boot_stop_right_after_the_state_write_still_relaunches_from_the_seed (the reviewer's retained crash case): Lists differ: [] != ['cc', 'trading-cc', 'coop']
Local P2 (:244-245): the carry was taken before any tab opened, so a lane the run brought back stayed carried and reappeared after it exited on purpose retire() drops a row from the carry once the run sees its session running, after each tab and in the final list test_a_lane_brought_back_by_a_partial_run_leaves_the_carry (the fake PowerShell gained a per-launcher failure, FAKE_PS_FAIL_NAMES): {'wsl-architecture-design', 'ns2604-coop'} is not false
Local P2 (:153-156): the lock carried no pinned upstream citation roster_lock and the tool README cite fcntl.flock at python/cpython v3.13.16 (cbc944f4), Doc/library/fcntl.rst:176-183, and release-on-close at Linux man-pages tag man-pages-6.9.1, man/man2/flock.2:69-72 (sha256 ccd3962b…, fetched 2026-10-15T10:44:33Z); both are added to receipts/sources.json text

python3 -m unittest tests.test_session_relaunch: 55 tests OK; the evidence README's count changed from 53 to 55. scripts/validate.py: rc 0.

Rebase onto #1145, and fold (07ea567)

Resolution (rebase onto 5937c61c): main's completion rule meets this PR's carry retirement in the final pass. A session seen running and then missing from the final list is absent, and its row re-enters the carry. test_a_session_that_registers_and_then_exits_is_carried_again covers this. (Corrected by the fold below: at 07ea567 the test did not fail without the re-carry line, which the retire rule had made redundant.)

Fold: the command center's FULL read at b2755a0 (PASS; one P2).

Finding Fix Fail-first test
P2: a row left the carry the first time its session was seen running. If the command center registered and then exited while the next tab opened, and the boot stopped before the final pass, that lane was dropped silently. The per-tab retire() is removed. Only the final pass retires, and only for sessions its live list shows running. test_a_session_that_registers_and_exits_before_a_crash_stays_carried: failed at b2755a0 ('wsl-architecture-design' not found in the roster)
P3: the README carry text, and the test count The tool README states the final-list rule and the re-carry; the decision's count (:63) and the evidence README's count are 66 —

python3 -m unittest tests.test_session_relaunch: 66 tests OK. validate rc 0.

Rebuild and fold (aedf5549, 2026-10-16)

Rebuild. At the command center's ruling of 2026-10-16, the branch was rebuilt onto main after #1145 landed. Its earlier
history held two commits of another change in an earlier form; it now holds only this change's own three commits, with their
messages unchanged, and the fold. The earlier head is kept as a local backup ref and was never pushed again. GitHub's
timeline for this pull request may still reach the two earlier commits.

Fold: the GPT fix2 read at 07ea567 (coordination/cc-reads-20261005/fix2-pr1141/GPT-VERDICT-07ea56748.md; one P2 blocker).

Finding Fix Check
P2: the claim that the carry test fails without the re-carry line was false at 07ea567; the final read's retire rule (running only) already kept an absent session's row carried, so each covered the other The redundant re-carry line is removed; the retire rule is the one mechanism The carry test fails with the retire rule mutated to retire absent rows too (a mutated copy in a scratch directory) and passes on this tree

After: tests.test_session_relaunch 66 OK; validate rc 0; tests.test_pre_push_gate 29 OK on the committed tree. History and tree
scanned for the earlier review digest and any prefix of it: 0 hits.

Rebuild 2 and fold (ca8913985, 2026-10-16)

Rebuild 2. At the command center's ruling of 2026-10-16T04:39Z, the branch was rebuilt onto main fa6cf1a2:

  • The blocker. The GPT read res1 at aedf554 (coordination/cc-reads-20261005/res1-pr1141/GPT-VERDICT-aedf55498.md,
    blocker 1) found that commit 1's message cited a private local review with a prefix of that file's digest.
  • Commit 1 (5aabeb40f) now names the review only.
  • Commits 2 to 4 (860c7f36e, 9cf1239dd, 7e52143f3) carry their messages byte for byte.
  • Patches: all four patches are unchanged. The range-diff shows one message-only change and three =. The full trees
    differ only by main's own changes between the old base and fa6cf1a2.
  • The old head, aedf5549, is kept as a local backup ref and was not pushed again.

Custody scan, counts only. It searched every prefix of 7 or more hex digits of the digests of 15 private files on this
host, in four classes: the co-op's local reviews of #1141 (5) and of #1116 (5), the GPT reads of #1141 (4) and the command
center's reads of #1141 (1).

Surface Hits at ca8913985
this branch's own commit messages 0
the head tree 0
every reachable tree (git log -S) 0
this PR's body 0

Residuals that this rebuild cannot reach, by count and class:

Fold (ca8913985): the co-op's local review at aedf554 (pr1141-aedf5549/review_output.json, one P2) and GPT res1's
follow-ups 2 to 4, joined as one family by the ruling, with P3s 5 and 7.

Finding Fix Check (fails on aedf554's code)
Local P2 and GPT 3 (:255-256): a lane that recovered after a partial run was carried again once it stopped A snapshot after a partial run records the lanes it lists as retired and drops their rows from the carry (retire_recovered, retire_into) test_a_lane_that_recovers_after_a_partial_run_and_then_stops_on_purpose_is_not_relaunched: the next boot planned a relaunch of trading-cc
GPT 4 (:545, :552): a same-boot rerun reopened lanes that had recovered and then stopped A rerun leaves retired lanes out of its launch set and its carry; a --reopen of one is refused, and its own launcher starts it again test_a_same_boot_rerun_does_not_reopen_lanes_that_recovered_and_then_stopped: the rerun opened cc, trading-cc and coop
GPT 2 (:626, :179, :605): a snapshot sampled before the run's final publication wrote that sample with the carry read after it The snapshot samples under roster.lock (:176-187), and the run publishes its final state under the same lock (:635) test_a_snapshot_sampled_before_the_final_publication_keeps_the_recovered_rows: the roster lost all three recovered lanes
GPT P3 5 (test :419): the race test relied on a one-second sleep The snapshot's pause ends when the relaunch reaches the lock Passes on aedf554 and here
GPT P3 7: README :118 "re-enters", and this body's :598 locator README carry paragraph rewritten; the stale follow-up is closed —

After: tests.test_session_relaunch 69 OK; validate rc 0; tests.test_pre_push_gate 29 OK on the committed tree.

Commit on ca891398 (3436a5b03, 2026-10-16)

From the co-op's local review at ca89139 (pr1141-ca891398/review_output.json): two P2s in the relaunch-lifecycle
family. The command center ruled one normal commit at 2026-10-16T05:47Z, with no rebuild.

Finding Fix Check (fails on ca89139's code)
P2 (:217-220): plan() read the state before roster.lock, and record_refusal() never took it, so a snapshot's retirement was erased by a rerun's stale save, or a snapshot saved over a refusal record prepare_run() holds roster.lock from plan()'s read to the seed roster and first save, with a refused --reopen recorded inside it. A rerun retires the carried lanes it finds live at its start, as a snapshot would. A dry run takes no roster.lock. test_a_snapshot_during_a_rerun_never_erases_a_retirement; test_a_snapshot_never_saves_over_a_refusal_record (the refusal lost, 0 != 1)
P2 (:647-649): a rerun that brought back every owed lane stayed partial while the lanes it retired earlier were stopped, so main() exited 1 A retired session counts as satisfied, and is not marked absent at the final read test_a_rerun_that_brings_back_every_owed_lane_completes: it stayed partial

After: tests.test_session_relaunch 72 OK; validate rc 0; tests.test_pre_push_gate 29 OK on the committed tree. Any
findings from GPT's res2 at ca89139 join the next micro.

Commit on 3436a5b0 (8ee195a3c, 2026-10-16)

This commit answers GPT res2 at ca89139 (GPT-VERDICT-ca8913985.md, cited by name): its blocker (:548, :588, :617) and its refusal-path follow-up (:526, :532, :538).
It also folds the FRONT micro at 3436a5b (GPT-VERDICT-3436a5b03.md, ACK, 0 blockers): P3 3 in the README, and P3 2 in
this body.

Res2's retained probe (review-results/test_relaunch_adjacent.py), run before this commit:

Probe case At 3436a5b0 At 8ee195a3c
completion with retired lanes OK OK
roster.lock exclusion and release OK OK
a refusal keeps a concurrent retirement FAIL: the refusal rewrote a stale state, because only its caller held the lock OK
a snapshot retirement survives the rerun's plan HANG (60 s): a snapshot inside plan(), in the same thread, waited on the lock the run holds OK
Fix Check (fails on 3436a5b's code, as a failure, not a hang)
roster_lock is reentrant within one thread (a thread-local depth); another thread or process still waits test_the_roster_lock_is_reentrant_in_one_thread_and_exclusive_across_threads
prepare_run reconciles its plan with the retirements on disk before its first save test_a_retirement_saved_while_the_rerun_plans_is_honoured
record_refusal holds the lock itself and appends to the state as read at the write test_a_refusal_keeps_a_retirement_saved_meanwhile
The README's completion paragraph (:77) counts a retired session as satisfied (micro P3 3) -

After: tests.test_session_relaunch 75 OK; validate rc 0; tests.test_pre_push_gate 29 OK on the committed tree.

Follow-ups

  • GPT res1 P3 6: some cited source captures remain unavailable, and citation-binding coverage is incomplete
    (receipts/sources.json:66; README, "Mechanisms", :141).
  • The four residual hits above stay where they are (main's history, this body's edit history).
  • From the command center's micro at 8ee195a (CC-DELTA-8ee195a3.md, PASS): P2, cite the basis of the reentrant
    roster_lock (flock locks belong to the open file description, so a second descriptor in the same process
    conflicts) at a pinned primary source; P3, its test notes.

Notes for the reads

Checklist

  • No GitHub Actions changed.
  • No workflows changed.
  • No secrets are printed, logged or committed.
  • No new paid hosting, subscription or billing surface.
  • Peer-owned untracked files and worktrees were preserved.

🤖 Generated with Claude Code

@seathatflowsinourveins seathatflowsinourveins added the lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers label Oct 15, 2026
seathatflowsinourveins added a commit that referenced this pull request Oct 15, 2026
…ied rows seen running; cite flock

Fix round for #1141 at 474cf2b:
- GPT read (P2, reproduced): a boot stop between save() and the seed roster left the timer's empty roster
  of this boot as the newest, and the next boot opened nothing. cmd_snapshot now runs before save(), both
  still under roster_lock (the reviewer's seed-first control).
- Local review P2 (:244-245): the carry was taken before any tab opened, so a lane the run brought back
  stayed carried and was listed again after it exited on purpose. retire() drops a row from the carry once
  the run sees its session running (after each tab and in the final list).
- Local review P2 (:153-156): roster_lock and the README cite fcntl.flock at python/cpython v3.13.16
  (cbc944f4) Doc/library/fcntl.rst:176-183 and release-on-close at Linux man-pages-6.9.1
  man/man2/flock.2:69-72; both sources added to receipts/sources.json.

Fail-before (base code, new tests): test_a_boot_stop_right_after_the_state_write_still_relaunches_from_the_seed,
Lists differ: [] != ['cc', 'trading-cc', 'coop']; test_a_lane_brought_back_by_a_partial_run_leaves_the_carry,
{'wsl-architecture-design', 'ns2604-coop'} is not false. After: tests.test_session_relaunch 55 tests OK
(the evidence README's count updated from 53); validate rc 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
seathatflowsinourveins added a commit that referenced this pull request Oct 15, 2026
…ied rows seen running; cite flock

Fix round for #1141 at 474cf2b:
- GPT read (P2, reproduced): a boot stop between save() and the seed roster left the timer's empty roster
  of this boot as the newest, and the next boot opened nothing. cmd_snapshot now runs before save(), both
  still under roster_lock (the reviewer's seed-first control).
- Local review P2 (:244-245): the carry was taken before any tab opened, so a lane the run brought back
  stayed carried and was listed again after it exited on purpose. retire() drops a row from the carry once
  the run sees its session running (after each tab and in the final list).
- Local review P2 (:153-156): roster_lock and the README cite fcntl.flock at python/cpython v3.13.16
  (cbc944f4) Doc/library/fcntl.rst:176-183 and release-on-close at Linux man-pages-6.9.1
  man/man2/flock.2:69-72; both sources added to receipts/sources.json.

Fail-before (base code, new tests): test_a_boot_stop_right_after_the_state_write_still_relaunches_from_the_seed,
Lists differ: [] != ['cc', 'trading-cc', 'coop']; test_a_lane_brought_back_by_a_partial_run_leaves_the_carry,
{'wsl-architecture-design', 'ns2604-coop'} is not false. After: tests.test_session_relaunch 55 tests OK
(the evidence README's count updated from 53); validate rc 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/cc-native-practice-relaunch-roster-carry-20261015 branch from d9c6dd0 to b2755a0 Compare October 15, 2026 14:12
seathatflowsinourveins added a commit that referenced this pull request Oct 15, 2026
… its session running

The command center's FULL read of #1141 at b2755a0 (P2, a lane can be dropped silently): a row left the carry the
first time its session was seen running, so when the command center registered and then exited while the next tab
opened, and the boot stopped before the final pass, every snapshot wrote this boot's roster with it neither live
nor carried. The per-tab retire is removed; the final pass retires only sessions its live list shows running, and
an absent session re-enters the carry (the rebase resolution).

test_a_session_that_registers_and_exits_before_a_crash_stays_carried, fail-before at b2755a0:
'wsl-architecture-design' not found in the roster. P3s: the tool README states the final-list rule and that an
absent row re-enters the carry; the decision's and the evidence README's test counts are 66.
After: tests.test_session_relaunch 66 tests OK; validate rc 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
seathatflowsinourveins added a commit that referenced this pull request Oct 16, 2026
…ied rows seen running; cite flock

Fix round for #1141 at 474cf2b:
- GPT read (P2, reproduced): a boot stop between save() and the seed roster left the timer's empty roster
  of this boot as the newest, and the next boot opened nothing. cmd_snapshot now runs before save(), both
  still under roster_lock (the reviewer's seed-first control).
- Local review P2 (:244-245): the carry was taken before any tab opened, so a lane the run brought back
  stayed carried and was listed again after it exited on purpose. retire() drops a row from the carry once
  the run sees its session running (after each tab and in the final list).
- Local review P2 (:153-156): roster_lock and the README cite fcntl.flock at python/cpython v3.13.16
  (cbc944f4) Doc/library/fcntl.rst:176-183 and release-on-close at Linux man-pages-6.9.1
  man/man2/flock.2:69-72; both sources added to receipts/sources.json.

Fail-before (base code, new tests): test_a_boot_stop_right_after_the_state_write_still_relaunches_from_the_seed,
Lists differ: [] != ['cc', 'trading-cc', 'coop']; test_a_lane_brought_back_by_a_partial_run_leaves_the_carry,
{'wsl-architecture-design', 'ns2604-coop'} is not false. After: tests.test_session_relaunch 55 tests OK
(the evidence README's count updated from 53); validate rc 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/cc-native-practice-relaunch-roster-carry-20261015 branch from 07ea567 to aedf554 Compare October 16, 2026 03:06
seathatflowsinourveins added a commit that referenced this pull request Oct 16, 2026
… its session running

The command center's FULL read of #1141 at b2755a0 (P2, a lane can be dropped silently): a row left the carry the
first time its session was seen running, so when the command center registered and then exited while the next tab
opened, and the boot stopped before the final pass, every snapshot wrote this boot's roster with it neither live
nor carried. The per-tab retire is removed; the final pass retires only sessions its live list shows running, and
an absent session re-enters the carry (the rebase resolution).

test_a_session_that_registers_and_exits_before_a_crash_stays_carried, fail-before at b2755a0:
'wsl-architecture-design' not found in the roster. P3s: the tool README states the final-list rule and that an
absent row re-enters the carry; the decision's and the evidence README's test counts are 66.
After: tests.test_session_relaunch 66 tests OK; validate rc 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
seathatflowsinourveins added a commit that referenced this pull request Oct 16, 2026
…st guards it

The GPT fix2 read of #1141 at 07ea567 (2026-10-15, CHANGES_REQUESTED, one P2 blocker): the claim that the carry
regression fails without the re-carry line was false at that head. The fold at 07ea567 made a row leave the carry only
when the final list shows its session running, so an absent session's row never left, and the re-carry line that added
it back had become redundant: removing either the line or the retire rule's running-only selection left the test
passing, since each covered the other.

- tools/session-relaunch/session_relaunch.py: the redundant re-carry line is removed; the final read's retire rule
  (running only) is the one mechanism that keeps an absent session's row carried.
- tests/test_session_relaunch.py: test_a_session_that_registers_and_then_exits_is_carried_again now states what it
  guards. With the retire rule mutated to retire absent rows too, it fails; on this tree it passes (a mutated copy
  in a scratch directory, no repository file changed).

The branch is rebuilt onto main at the command center's ruling of 2026-10-16: the earlier history held two commits that
landed as #1145 in another form; this branch keeps only its own three commits, whose messages are unchanged, and this
fold. The GPT read's adjacent P2s (a stale live sample after finalization, a manually recovered lane carried again, a
same-boot retry reopening recovered lanes) and its P3s are Follow-ups in the body.

After: tests.test_session_relaunch 66 OK; validate rc 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
… partial run's unfinished rows

(c8d7fa9 :512 and :221-223; the co-op's local review of 7aad5a0, by name only). One PR, since
both concern what this boot's roster carries.

- (b) A timer snapshot that read its carry before a starting relaunch saved its
  state, and wrote after the relaunch's seed roster, replaced the seed with the
  live list only. roster_lock() (flock on roster.lock in the state directory)
  is now held by the snapshot from its carry read to its roster write
  (snapshot_live) and by the relaunch while it saves its state and then writes
  the seed. Both holders are short; relaunch.lock stays the run's own lock.
- (c) carried_rows() carried only an in_progress state's rows, so the first
  snapshot after a partial run dropped the lanes the relaunch had not brought
  back and a second crash lost them. A partial state of this boot carries them
  too (minus the ones listed live).
- README: the roster paragraph. The decision's test line and the evidence
  README count: 53.

Fail-first: both new tests failed on c8d7fa9 (the seed replaced by an empty
roster; the partial run's rows missing); with the lock disabled and only the
reorder kept, the race test still fails. The module runs 53 tests OK. Own
logic; flock(2) semantics as the existing relaunch.lock uses them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ied rows seen running; cite flock

Fix round for #1141 at 474cf2b:
- GPT read (P2, reproduced): a boot stop between save() and the seed roster left the timer's empty roster
  of this boot as the newest, and the next boot opened nothing. cmd_snapshot now runs before save(), both
  still under roster_lock (the reviewer's seed-first control).
- Local review P2 (:244-245): the carry was taken before any tab opened, so a lane the run brought back
  stayed carried and was listed again after it exited on purpose. retire() drops a row from the carry once
  the run sees its session running (after each tab and in the final list).
- Local review P2 (:153-156): roster_lock and the README cite fcntl.flock at python/cpython v3.13.16
  (cbc944f4) Doc/library/fcntl.rst:176-183 and release-on-close at Linux man-pages-6.9.1
  man/man2/flock.2:69-72; both sources added to receipts/sources.json.

Fail-before (base code, new tests): test_a_boot_stop_right_after_the_state_write_still_relaunches_from_the_seed,
Lists differ: [] != ['cc', 'trading-cc', 'coop']; test_a_lane_brought_back_by_a_partial_run_leaves_the_carry,
{'wsl-architecture-design', 'ns2604-coop'} is not false. After: tests.test_session_relaunch 55 tests OK
(the evidence README's count updated from 53); validate rc 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
… its session running

The command center's FULL read of #1141 at b2755a0 (P2, a lane can be dropped silently): a row left the carry the
first time its session was seen running, so when the command center registered and then exited while the next tab
opened, and the boot stopped before the final pass, every snapshot wrote this boot's roster with it neither live
nor carried. The per-tab retire is removed; the final pass retires only sessions its live list shows running, and
an absent session re-enters the carry (the rebase resolution).

test_a_session_that_registers_and_exits_before_a_crash_stays_carried, fail-before at b2755a0:
'wsl-architecture-design' not found in the roster. P3s: the tool README states the final-list rule and that an
absent row re-enters the carry; the decision's and the evidence README's test counts are 66.
After: tests.test_session_relaunch 66 tests OK; validate rc 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…st guards it

The GPT fix2 read of #1141 at 07ea567 (2026-10-15, CHANGES_REQUESTED, one P2 blocker): the claim that the carry
regression fails without the re-carry line was false at that head. The fold at 07ea567 made a row leave the carry only
when the final list shows its session running, so an absent session's row never left, and the re-carry line that added
it back had become redundant: removing either the line or the retire rule's running-only selection left the test
passing, since each covered the other.

- tools/session-relaunch/session_relaunch.py: the redundant re-carry line is removed; the final read's retire rule
  (running only) is the one mechanism that keeps an absent session's row carried.
- tests/test_session_relaunch.py: test_a_session_that_registers_and_then_exits_is_carried_again now states what it
  guards. With the retire rule mutated to retire absent rows too, it fails; on this tree it passes (a mutated copy
  in a scratch directory, no repository file changed).

The branch is rebuilt onto main at the command center's ruling of 2026-10-16: the earlier history held two commits that
landed as #1145 in another form; this branch keeps only its own three commits, whose messages are unchanged, and this
fold. The GPT read's adjacent P2s (a stale live sample after finalization, a manually recovered lane carried again, a
same-boot retry reopening recovered lanes) and its P3s are Follow-ups in the body.

After: tests.test_session_relaunch 66 OK; validate rc 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
… and the final publication share roster.lock

The fold of the rebuild of #1141 on main fa6cf1a (the command center's rulings of 2026-10-16T03:41Z and 04:39Z). It
answers the co-op's local review of #1141 at aedf554 (P2, :255-256) and the GPT read res1 at aedf554
(GPT-VERDICT-aedf55498.md: follow-ups 2 to 4 joined as one family, and P3s 5 and 7).

- One persisted retirement. A run's final read, and a snapshot after a run that ended partial, record the lanes they see
  running as `retired` in the relaunch state and drop their rows from its carry (retire_into, retire_recovered). A
  retired lane is not carried again (follow-up 3), and a rerun of the same relaunch neither opens it nor carries it: plan()
  leaves it out of the launch set, and the rerun's carry skips it (follow-up 4). A --reopen of a retired lane is refused;
  its own launcher starts it again. A session that registered and then went missing from the final list (absent) is not
  retired and stays carried, as before.
- One synchronization point (follow-up 2). The timer's snapshot samples the live list under roster.lock, and a run
  publishes its final outcomes, retirement and status under the same lock. So a snapshot never writes a list sampled
  before that publication with the carry read after it.
- The roster race test waits until the relaunch reaches the lock, not a one-second sleep (P3 5). The README carry
  wording is updated (P3 7).

Tests: tests/test_session_relaunch.py, 69 (2 new, 2 changed). On aedf554's production code
(fail-first-r2.txt), each of these fails on behaviour:
- the snapshot sampled before the final publication loses the three recovered lanes;
- a rerun opens cc, trading-cc and coop instead of trading-cc;
- the next boot relaunches a lane that recovered late and then stopped.
The handshake race test passes there and on this head. validate rc 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/cc-native-practice-relaunch-roster-carry-20261015 branch from aedf554 to ca89139 Compare October 16, 2026 05:31
… roster.lock; a retired session is satisfied

The commit on ca89139 for the co-op's local review at ca89139 (pr1141-ca891398/review_output.json, two P2s in the
relaunch-lifecycle family; the command center's ruling of 2026-10-16T05:47Z: one normal commit, no rebuild).

- P2 (session_relaunch.py:217-220), every state read-modify-write. plan() read the state before roster.lock, and
  record_refusal() never took the lock, so two interleavings lost a write:
  - a snapshot's retirement was erased by a rerun's stale save;
  - a refusal record was saved over by a snapshot.
  The fix:
  - prepare_run() runs a run's decision and first writes under roster.lock, from plan()'s read to the seed roster and
    the first save; a refused --reopen is recorded inside that span;
  - a dry run writes nothing and takes no roster.lock;
  - a rerun retires the carried lanes it finds live at its start, as a snapshot after that run would, so the retirement
    no longer depends on which of the two ran first.
- P2 (:647-649), completion. A rerun that brought back every lane still owed stayed partial while the lanes it had
  retired earlier were stopped on purpose; main() then exited 1, and further reruns spent the crash-loop budget. A
  retired session now counts as satisfied, and is not marked absent at the final read.

Tests: tests/test_session_relaunch.py, 72 (3 new). Each fails on ca89139's code (fail-first-r3.txt):
- a snapshot started right after plan()'s read: the command center's retirement is erased;
- a snapshot holding its copy while a --reopen is refused: the refusal record is lost (0 != 1);
- a rerun that brings back trading-cc: it stays partial.
validate rc 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
… reconciled with retirements; refusals locked

The commit on 3436a5b. It answers GPT res2 at ca89139 (res2-pr1141-ca8913985/GPT-VERDICT-ca8913985.md): its blocker
(:548/:588/:617) and its refusal-path follow-up (:526/:532/:538). It folds the FRONT micro at 3436a5b
(fix3-pr1141-3436a5b03/GPT-VERDICT-3436a5b03.md, ACK, 0 blockers): P3 3 here, and P3 2 in the body.

Before this commit, res2's retained probe (review-results/test_relaunch_adjacent.py) ran against 3436a5b as follows:
- completion and lock exclusion: OK;
- the refusal case: FAIL. record_refusal() called on its own rewrote a stale state, because only its caller held the
  lock;
- the stale-plan case: HANG at 60 s. A snapshot injected inside plan(), in the same thread, waited on the
  roster.lock that the run already holds.

The fix:
- roster_lock is reentrant within one thread (a thread-local depth). A nested hold takes no second flock, while another
  thread or process still waits.
- prepare_run reconciles the plan with the state on disk: lanes retired while it planned are dropped from the launch,
  and the run continues from that state.
- record_refusal holds roster_lock itself and appends to the state as read at the write.
- The README's completion paragraph (:77) now counts a retired session as satisfied, as the rule and code do.

The probe at this head: 4 of 4 OK.

Tests: tests/test_session_relaunch.py, 75 (3 new). Each fails on 3436a5b's code as a failure, not a hang
(fail-first-r4.txt):
- the rerun waits on its own roster.lock;
- the refusal loses trading-cc's retirement;
- a nested hold in the same thread waits on its own lock.
validate rc 0. The source-capture gap (micro P3 1, res2 finding 4) stays a Follow-up.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@seathatflowsinourveins
seathatflowsinourveins merged commit 0b12eff into main Oct 16, 2026
34 checks passed
@seathatflowsinourveins
seathatflowsinourveins deleted the claude/cc-native-practice-relaunch-roster-carry-20261015 branch October 16, 2026 09:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant