Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4420b6c
WIP: rescue the supervised-restart work whose writer died before it c…
Sep 16, 2026
01fe0ea
Merge main (#87) so the restart intent sits IN FRONT of both of lane-…
brettheap Sep 16, 2026
378c0b7
A restart that inherits the last restart's transcript cannot catch #9…
Sep 16, 2026
40ec662
WIP: the suite's /ctx and supervisor cases, rescued before a second r…
brettheap Sep 16, 2026
efeb52d
Merge main (#102, #101) — the installer's chmod and `wip init` fixes,…
brettheap Sep 16, 2026
37ecace
The flaky case was `lanes-edit.sh` refusing its own no-op, and a sixt…
brettheap Sep 16, 2026
d9b282e
The two spellings of six flags refuse the same empty value, and the c…
brettheap Sep 16, 2026
5e39026
Task 7.5 says why it is open, like the other five, rather than sittin…
brettheap Sep 16, 2026
c421426
A supervisor sent SIGTERM left the lane wedged, and a trap alone woul…
brettheap Sep 16, 2026
cd92599
The manual says what a signalled supervisor leaves, and whose keystro…
brettheap Sep 16, 2026
5164ce1
fix: fence supervised restart readiness and launch intent
brettheap Oct 3, 2026
33c97cd
Merge main and isolate supervised ctx restart as legacy compatibility
brettheap Oct 4, 2026
6d337d8
Report superseded restart attempts without offering a stale retry
brettheap Oct 4, 2026
7ae8b40
Serialize legacy restart handoff writes and guard each launch attempt
brettheap Oct 4, 2026
d0f712c
Correct restart regression fixtures and retain agent conflict diagnos…
brettheap Oct 4, 2026
f5f4a0c
Verify legacy restart pane, process state and complete binding
brettheap Oct 4, 2026
2daf66f
Keep supervisor retry prompts visible when saving stdin
brettheap Oct 4, 2026
0410888
Acknowledge held supervisor pane in interactive retry regression
brettheap Oct 4, 2026
a950672
Fence legacy restart inputs before pane replacement and reap observer…
brettheap Oct 4, 2026
6c5b741
Write each pending restart current launch facts explicitly
brettheap Oct 4, 2026
0e4b311
Reconcile signals across restart claims and lock handoff resolution
brettheap Oct 4, 2026
733557c
Require bound handoff identity and usable managed ownership readers
brettheap Oct 4, 2026
69838c5
Supply checkout facts to partial handoff failure fixtures
brettheap Oct 4, 2026
774d189
Reconcile supervised legacy restart with landed lane diagnostics
brettheap Oct 5, 2026
0bf6f0a
Fix macOS restart validation and terminal regression fixtures
brettheap Oct 5, 2026
6b92e35
Recover ordinary resume after aborted restart preparation
brettheap Oct 5, 2026
b20882a
fix(tests): drain supervisor PTY before macOS exit
brettheap Oct 5, 2026
fb28987
fix(lanes): retain restart authority across checkout recording
brettheap Oct 5, 2026
fd09e2d
fix: name state root override in restart refusal
brettheap Oct 5, 2026
f13d329
Merge main after PR #164 and retain both integration suites
brettheap Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -358,5 +358,10 @@ jobs:
'BEGIN { print "TAKEN" }' 2>&1 | head -n1
git --version; python3 --version

# Exercise tty teardown before the long suite so a macOS-only fixture
# regression fails promptly; the full suite remains the landing gate.
- name: restart terminal and control-root checks
run: python3 -m pytest tests/test_supervised_restart_regressions.py -q -k "interactive_retry_rechecks_holder_absence_before_launch or restart_root"

- name: tests
run: python3 -m pytest tests -q
68 changes: 58 additions & 10 deletions commands/ctx.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,55 @@ category: Lane
tags: [lane, ctx, handoff, swap, lane-collision-protocol]
---

**Invoke the `handoff` skill now with `--restart`, and follow every step of it, in order, to the end.**
**Invoke the `handoff` skill's legacy supervised restart path with `--restart`.**

Before any preservation write, resolve the lane read-only and run
`lanes-edit.sh legacy-restart-check <lane>`. Managed ownership or an unreadable
ownership store refuses this compatibility path. Collect writer briefs without
changing the canonical handoff, then call `lane-handoff --restart` once with
`--writers-file <temporary TSV>` when needed. The backend reserves `preparing`
before changing any window, handoff, row or log. Do not execute the skill's
ordinary steps 1–5 as a separate write sequence for `/ctx`.

This intent owns only the legacy restart operation. It neither reads nor advances
PR #97's lifecycle counters or feature 001's managed JSON ledger.

`/ctx` is `/handoff --restart` and nothing else. Lane-collision-protocol **Amendment 17 clause (f)**, folded
in on Brett Heap's word of 2026-09-14, verbatim: *"the /ctx should also run the first prompt and restart all.
so the user only does /ctx and it is all automatic from there"* — and ratified the same day as revision 2.

What that means in order, and the order is the rule:

1. the identity triple is fixed, the handoff file is refreshed with a fresh Rule 3 top block that **lists
1. the backend reserves the legacy restart operation as `preparing`, then the identity triple is fixed, the handoff file is refreshed with a fresh Rule 3 top block that **lists
every writer this lane has running** (its worktree, its branch, its brief, what it had committed), the
writers are polled, and `PAUSED` is written with Amendment 11(c)'s sub-fields, Amendment 17(b)'s `agent`
and `transcript`, and `clear` as its why;
2. **then** the lane's own pane is respawned through the launcher — `tmux respawn-pane -k`, so the act
survives the death of the session that started it — with a NEW session of the same agent whose FIRST
PROMPT is that top block, run without anyone typing it;
3. that session stamps `RESUMED by …` first, as Rule 3 requires, then **COUNTS the live writers** —
2. **then** that **RESTART INTENT** becomes `pending` under the lane's own control root — the lane, a generation, an
operation id, the transcript being paused, the agent, the profile, the checkout, the pane, the handoff's
path and its **digest**, and the launch mode `fresh-from-handoff` — and read back;
3. **then** the lane's own pane is respawned — `tmux respawn-pane -k`, so the act survives the death of the
session that started it — with the **pane supervisor** (`lane-handoff --supervise`, by absolute path). The
supervisor claims that intent, runs the launch as its **child**, and stays in the pane whatever happens;
4. the launch is a NEW session of the same agent whose FIRST PROMPT is the top block written in step 1, run
without anyone typing it;
5. that session stamps `RESUMED by …` first, as Rule 3 requires, then **COUNTS the live writers** —
`ListAgents`, the agent's equivalent elsewhere — because the block's `WRITERS` section is a list to COUNT
and not a list to relaunch (Amendment 17 Addendum 1 (i), in force 2026-09-14T20:59:31Z). A writer still
live OWNS its worktree and is told, not relaunched; only a writer that is NOT live is relaunched, from
where it stood.
where it stood;
6. only when the replacement is **positively confirmed** — a live child, exactly one holder of this lane, its
transcript the new one and not the paused one, in the pane the intent names — is the restart marked
`ready`. Until then it is not.

**Steps 2, 3 and 6 are new, and they are `opensoft/openRepoTools#94`.** On 2026-09-15 this command wrote its
record, respawned the pane with `LANE_START_FRESH=1 … lane <lane>`, and what came up was
`claude --name <lane> --resume <the uuid it had just paused>` — the same conversation, not a fresh one, with
no supervisor to notice. The seam was an **environment variable**, and the launcher re-creates its child
through `tmux new-session`, which hands that child the tmux **server's** environment and not the caller's.
An environment cannot cross that boundary; a **file** can, so the intent on disk is now the authority and
`LANE_START_FRESH` is a compatibility detail. And what tmux starts is a supervisor rather than the launch
itself, because a launch that ends takes the pane with it — Amendment 11's own invariant is *"No path the
launcher opened exits the pane"*, and this one did.

**`/ctx` says which it did** (Addendum 1 (j)). This one respawns the pane, so the process every writer was a
child of is gone, the record says `kind respawn`, and the count then finds none — which is what makes
Expand All @@ -33,8 +62,27 @@ one is `--in-process`, `kind in-process`, and its block says to EXPECT every wri
the kind says what to expect and never what to do.

**A `/ctx` whose record could not be written REFUSES before it kills anything.** A pane is never respawned
over an unrecorded lane — and the record is three writes, not one: if the `PAUSED` line did not land, or the
row was not flipped, or the handoff's top block (which is the new session's first prompt) could not be
refreshed, this pane stays exactly as it is and the reason is printed.
over an unrecorded lane — and the record is now FOUR writes, not three: if the `PAUSED` line did not land, or
the row was not flipped, or the handoff's top block (which is the new session's first prompt) could not be
refreshed, **or the restart intent could not be written and read back**, this pane stays exactly as it is and
the reason is printed. A pane respawned with no intent is a pane whose supervisor has nothing to launch from,
which is the same unrecorded restart one step along.

**A restart already in flight is not superseded.** A second `/ctx` on a lane whose intent is `preparing`, `pending` or
`starting` refuses before preservation writes or the kill — and names `lane-handoff --restart-status --lane <lane>`.

**When the launch fails, the pane is still yours.** The supervisor marks the intent `failed`, prints the
stage that failed and offers one line to retry the same operation. Nothing of the lane is lost: the handoff
it wrote is intact and its top block is still the first prompt the next session gets.

No picker, no title fallback, no second command: the one word is the whole act.

Every launch, retry and finalizer is fenced by operation, legacy generation and
attempt. The first child reserves its transcript once; repeated consumers refuse.
The Rule 3 stamp is published atomically, after recording the checksums of its
complete prior and stamped versions. A retry accepts those exact versions only.

A signal before the supervisor claim leaves the intent unchanged. A signal with
a live or unknown child leaves `starting` and reports `INDETERMINATE`; retry stays
blocked. Only a confirmed ended launch with no live holder becomes `failed`.
Ordinary `lane <name>` resume cannot recover a failed supervised restart.
Loading
Loading