Skip to content

Pin openRepoShape at 39d5c98 — the standard's docs and templates name $AGENT_PROTOCOL_ROOT (opensoft/openRepoShape#161); digest recomputed, gitlink and pin in one commit - #46

Merged
brettheap merged 1 commit into
mainfrom
chore/shape-pin-39d5c98
Sep 14, 2026
Merged

brettheap merged 1 commit into
mainfrom
chore/shape-pin-39d5c98

Conversation

@brettheap

@brettheap brettheap commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Lane: openRepoTools-3

Pins upstream/openRepoShape to opensoft/openRepoShape's new main tip,
39d5c98, landed as PR opensoft/openRepoShape#161 (squashed): docs and
templates now name $AGENT_PROTOCOL_ROOT instead of hard-coding
~/.agents/workspace.yaml.

Compare check (rule 2 of AGENTS.md's "The pinned standard at
upstream/openRepoShape"):

gh api repos/opensoft/openRepoShape/compare/main...39d5c986fcfac1a160474bfe91c5f1c37fccc72c --jq .status
identical

Independently cross-checked: opensoft/openRepoShape PR #161's merge commit
is this same sha, confirming it is the tip of main and not an orphaned
branch commit.

Bump command run exactly as contracts/openreposhape-pin.yaml states:

git -C upstream/openRepoShape fetch origin
git -C upstream/openRepoShape checkout 39d5c986fcfac1a160474bfe91c5f1c37fccc72c
python3 -c "import sys; sys.path.insert(0, 'upstream/openRepoShape/scripts'); \
  from repo_shape import tree_digest; print(tree_digest('upstream/openRepoShape', 'HEAD'))"
commit tree_sha256
old ba14e1d9308cb912e32f5d20e097bec89d9455a7 26a314f34239dd314c0cb7eab2da2915fc2d4bd27ae9c81349052fc4e6b4355e
new 39d5c986fcfac1a160474bfe91c5f1c37fccc72c a44c0165beb9d3b6ddfce46925bf04b8d19934d5327b5a304b5f3f11af699b02

The digest was recomputed by the bump command above against the pinned
checkout's own scripts/repo_shape.py, never hand-edited, and the pin file
and the gitlink move together.

git status after staging showed only contracts/openreposhape-pin.yaml
and the upstream/openRepoShape gitlink changed.

Suite: python3 -m pytest tests -q → 571 passed in 1080.08s (0:18:00),
run serialized behind the shared pytest lock (sibling worktrees were
building concurrently). tests/test_upstream_pin.py and the estate tests
that read the eight files out of the submodule are included and pass.

Lane: openrepotools-3 (openRepoTools-3)

🤖 Generated with Claude Code

Summary by Sourcery

Pin upstream/openRepoShape to the latest verified standard revision and update its integrity metadata.

Enhancements:

  • Update the pinned openRepoShape standard to the latest main commit, including its refreshed tree digest.

Tests:

  • Verify the updated upstream pin and dependent estate files with the full test suite.

…rogress)

Compare check: gh api repos/opensoft/openRepoShape/compare/main...39d5c986fcfac1a160474bfe91c5f1c37fccc72c --jq .status -> identical.
Independently confirmed: opensoft/openRepoShape PR #161's merge commit is
this same sha (39d5c986fcfac1a160474bfe91c5f1c37fccc72c), on that
repository's main.

Bump command run exactly as contracts/openreposhape-pin.yaml states:
  git -C upstream/openRepoShape fetch origin
  git -C upstream/openRepoShape checkout 39d5c986fcfac1a160474bfe91c5f1c37fccc72c
  python3 -c "import sys; sys.path.insert(0, 'upstream/openRepoShape/scripts'); \
    from repo_shape import tree_digest; print(tree_digest('upstream/openRepoShape', 'HEAD'))"

Old commit:      ba14e1d9308cb912e32f5d20e097bec89d9455a7
Old tree_sha256: 26a314f34239dd314c0cb7eab2da2915fc2d4bd27ae9c81349052fc4e6b4355e
New commit:      39d5c986fcfac1a160474bfe91c5f1c37fccc72c
New tree_sha256: a44c0165beb9d3b6ddfce46925bf04b8d19934d5327b5a304b5f3f11af699b02

`python3 -m pytest tests -q` was still running under the shared pytest lock
(five sibling worktrees build concurrently) when this WIP commit was pushed
ahead of a context swap. Suite counts to follow once it completes; this
commit/PR is not to be merged until the run is confirmed green.

Lane: openrepotools-3 (openRepoTools-3)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 14, 2026 19:55
@sourcery-ai

sourcery-ai Bot commented Sep 14, 2026 •

Copy link
Copy Markdown
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Pins upstream/openRepoShape to the verified 39d5c986fcfac1a160474bfe91c5f1c37fccc72c main tip, updates its recomputed tree digest, and validates the synchronized pin with the full 571-test suite.

Flow diagram for verified openRepoShape pin update

flowchart LR
    OldPin["Old commit ba14e1d"] --> Fetch["Fetch and checkout verified main tip"]
    Fetch --> NewPin["New commit 39d5c98"]
    NewPin --> Digest["Recompute tree_sha256"]
    Digest --> Contract["Update contracts/openreposhape-pin.yaml"]
    NewPin --> Gitlink["Move upstream/openRepoShape gitlink"]
    Contract --> Tests["Run tests/test_upstream_pin.py and full test suite"]
    Gitlink --> Tests
Loading

File-Level Changes

Change Details Files
Advance the pinned openRepoShape submodule and synchronize its contract metadata.
  • Move the gitlink from the previous upstream commit to main tip 39d5c98.
  • Update the declared commit and recomputed tree SHA-256 digest.
  • Keep the gitlink and pin contract changes together and verify them with the existing pin and estate test suites.
contracts/openreposhape-pin.yaml
upstream/openRepoShape

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sonarqubecloud

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot 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.

Hey - I've reviewed your changes and they look great!

Sourcery assessment

Approved.


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

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.

🟢 Approval recommended

The verified pin and digest updates are synchronized and the full test suite passes.

Pull request overview

Updates the pinned openRepoShape standard to commit 39d5c98 and refreshes its tree digest.

Changes:

  • Advances the upstream submodule pin.
  • Recomputes the recorded tree digest.
  • Keeps the manifest and gitlink synchronized.
File summaries
File Summary
contracts/openreposhape-pin.yaml Records the updated commit and tree digest.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@brettheap
brettheap merged commit f279423 into main Sep 14, 2026
7 checks passed
@brettheap
brettheap deleted the chore/shape-pin-39d5c98 branch September 14, 2026 20:21
brettheap added a commit that referenced this pull request Sep 14, 2026
…back

`git add -A` while resolving the merge staged the submodule at the commit this
worktree happened to have checked out (`ba14e1d`, this branch's base), which
reverted `main`'s own pin bump to `39d5c98` (#46) in the gitlink while
`contracts/openreposhape-pin.yaml` carried main's. The pin file and the gitlink
move in ONE commit and never apart — AGENTS.md rule 3 of the pinned standard —
so the gitlink is put back where main has it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UFwD7bCS73jaUMJ2vuo5m
brettheap added a commit that referenced this pull request Sep 14, 2026
…d) into the Amendment 12 branch — three conflicts, all of them the two rounds writing about the same `--install`, and the macOS job's three red lines taken with them

MERGE AND NEVER A REBASE: this branch is pushed and reviewed.

THE THREE CONFLICTS, and each resolution keeps BOTH sides rather than picking:

1. `openRepoTools`'s `jq`-missing refusal. This branch made it name the PAIR of
   entries and print both blocks; `main` added a REGULAR-FILE check for
   `~/.claude/settings.json` immediately under it (#44 round 1: `chmod` follows
   a symlink and `mv -f` swallows one). Both are kept, and that check's own
   refusal is respelled for the pair — it merges two entries, not one.

2. `openRepoTools`'s `present` arm. This branch split it in two, one line per
   entry, and made the WRITE conditional on either being absent, so a person's
   settings never pass through a state carrying one of the pair; `main` gave the
   arm a `chmod 600` for the mode a `present` run never applied (#40 finding 3).
   Both: the `SessionStart` arm keeps `main`'s mode restoration verbatim — its
   test asserts the sentence — the guard arm says what it found and does NOT
   chmod a second time (the mode belongs to the FILE, not to either entry), and
   the one write follows them.

3. `README.md`'s install paragraph and the cap that counts it. Both rounds
   rewrote the same sentences: two merged entries with the name guard's own
   description (here) and the regular-file rule with the modes `--install`
   stamps (`main`). The merged paragraph carries both, which is longer than
   either half, so the cap is the count of WHAT MERGED — 417 and 428 -> 436 —
   with both dated entries kept and a third naming the merge, which is the rule
   the 2026-09-13 entry states in its own words.

AND THREE COUNTS `main` STATES THAT THE SECOND HOOK ENTRY MOVES: "eleven of the
eighteen artifacts", "seventeen of the eighteen artifacts", and the comment
calling `settings.json` "the eighteenth artifact" — it carries the eighteenth
AND the nineteenth since adoption act 3. `HOOK_ENTRIES = 2` already derives the
total in `tests/test_openrepotools_command.py`; these are the prose beside it.

THE macOS JOB'S THREE RED LINES AT `6cf5359`, WHICH ARE THIS BRANCH'S AND NOT
THE MERGE'S. `tests-macos` failed where every other job passed, on the one case
round 2 added for the lock's pane fallback: the fixture made the live record's
pid a CHILD of the fake pane's, and `pid_under` climbs `/proc` to answer that —
which macOS does not have. The record was "here" on Linux and nowhere on the
macOS runner, so the guard refused with "no live session record in THIS window
carries this session id" and the lock never ran. The fixture now gives the pane
the record's OWN pid, which `pid_under` answers at its first step with no walk
at all, and the case says why in its own comment. THE BEHAVIOUR WAS NEVER
PLATFORM-SPECIFIC: `guard_type`'s fallback asks tmux for `#{pane_id}`, and the
same is true on both.

The submodule is checked out at the pin `main` brought (`39d5c98`).

Lane: openrepotools-3 (openRepoTools-3)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UFwD7bCS73jaUMJ2vuo5m
brettheap added a commit that referenced this pull request Sep 15, 2026
…ndoff` on PATH, `/handoff` with `/swap`, `/lane-swap` and `/ctx`, the record naming the agent and its transcript, and `lane-start --agent` resuming a lane from any agent the table carries (#47)

* WIP (swap-imminent commit, pushed unverified): a handoff is the swap under one name — `lane-handoff` on PATH, `/handoff` with `/swap` and `/lane-swap` as aliases, `/ctx` = `/handoff --restart`, the record naming the agent and its transcript, and `lane-start --agent` resuming the lane from any agent the launcher table carries

WIP, AND THE WORD IS IN THE SUBJECT BECAUSE THE PROOF IS NOT IN YET. Every file
parses (`bash -n` on the four commands and the suite, `py_compile` on the three
pytest files) and the bash suite was green through every pre-existing section on
the run before this one; the Amendment 17 section this commit adds had NOT yet
been reached when the swap was called, and `python3 -m pytest tests -q` has not
been run at all. Nothing here is merged on that basis: the next session re-runs
both suites and takes what they say.

lane-collision-protocol **Amendment 17** (ratified 2026-09-14T09:45:33Z,
revision 2 with clause (f)), **Amendment 18(d)** and **Addendum 2**;
#36, acts 1-8.

THE ACT HAS ONE NAME AND THREE DOORS. `lane-handoff` on PATH (new file,
`INSTALLABLES` 11 -> 12) is the `/lane-swap` skill's steps as one command: the
identity triple fixed through `lanes-edit.sh`, the handoff file refreshed with a
fresh Rule 3 top block, every running writer polled — `git status --short` and
`git log @{u}..` of every worktree under the lane's checkout `.claude/worktrees/`
and `<projects>/.lane-worktrees/<lane>/` — `PAUSED` written with Amendment
11(c)'s payload plus Amendment 17(b)'s `agent <name>; transcript <id|none>`, and
the one restart line printed. The top block's template gains a WRITERS section
(worktree, branch, brief, last commit, dirty and unpushed counts) and the first
line *"relaunch every writer below from where it stands"*; it is PREPENDED and
the file is never rewritten, so what was there stays below a `---` rule as
history. `skills/handoff/SKILL.md` carries the steps with their history (a
rename, so `git log --follow` reads straight through); `skills/lane-swap/SKILL.md`
and `commands/swap.md` name it and add nothing, and `commands/handoff.md` and
`commands/ctx.md` are new.

`--exit` (Amendment 18(d)) types `/exit` into the lane's own pane — the pane from
the LIVE record's own `tmux` field — because a handoff to another place is a
handoff and not a restart. `--restart` is `/ctx` (clause (f)):
`tmux respawn-pane -k` on that same pane through the launcher, with
`LANE_START_FRESH=1 pclaude --lane <lane> <profile>` — never `restart <lane>`,
which Addendum 2 takes off the person's PATH. BOTH REFUSE BEFORE THEY TOUCH THE
PANE where the record could not be written: *"a pane is never respawned over an
unrecorded lane"*.

`lanes-edit.sh`, ADDITIVELY, because #41 is landing in the same file:
`pause_subfields_check` validates the two sub-fields in the WRITER
(`write_event`, from any caller) and reads them with `all` so that
`agent not a key` is refused rather than read as `not`; `swapped` gains a sixth
and a seventh field, EMPTY for every record written before the amendment;
`window_lane`'s reader names that row's fields in full so a short list cannot
swallow them; four new reads (`lane-agent`, `lane-transcript`, `lane-last`,
`workspace-root`); and `log … --utc <UTC>` dates the late record by its own
field.

`lane-start --agent <name>` (clause (c)): the default is the agent of the last
`PAUSED`, else `claude`. `claude` with its transcript here resumes by id exactly
as Amendment 6 has always done; otherwise a launcher table — `claude` takes a
named new session with the handoff's TOP BLOCK as its first prompt, `codex` takes
the top block positionally, and a name the table does not carry is a refusal
naming the two it knows. The Rule 3 stamp and the row's session cell are written
BEFORE the launch, the stamp naming the agent (`RESUMED by codex <id>`) and the
cell in that agent's own spelling (`Codex <id>`).

ACT 6, the transcript follows the lane, shrunk to what the measurement of
2026-09-14T12:35Z left of it: `lane-start` SAYS which projects directory it found
the transcript in and whether that directory is SHARED (`readlink -f` equal
across profiles), and MOVES the `<uuid>.jsonl` and its sibling `<uuid>/` only
where another profile's `projects/` really is another directory, leaving the
pointer `<uuid>.jsonl.moved-to-<profile>-<UTC>` where they were. A uuid whose
holder is LIVE in another profile is a refusal naming the pid, its window or
`bg`, the profile and the retire act (Amendment 18(h)) — never a move, because a
copy leaves two live transcripts of one uuid to diverge.

ACT 7, `--late`, AND THE RULE IT IS BUILT ON. A lane's state is its log's last
lane-kind line IN FILE ORDER and the log is append-only, so a late `PAUSED`
appended after the relaunch's `RESUMED` would make a lane that is RUNNING read as
paused. The simplest rule that keeps every `binding` and state read correct is to
refuse to write a line that can no longer be last: `--late` REQUIRES `--at <UTC>`
— a late line is dated by its own field — and is written only where the lane's
last lane-kind line is a `STARTED` or `RESUMED` older than that instant. Every
other case is a refusal that writes nothing, made BEFORE the writers are polled
and before the handoff is refreshed, so a refused `--late` leaves the lane
exactly as it found it. Stated in the code, in the skill and in the manual.

Docs: `docs/README-lanes.md` gains "The handoff (Amendment 17)" — the act under
its three names, the two sub-fields and the reads that answer for them, the
launcher table, `/ctx`, `--late` with its rule, and the transcript rule;
`README.md`'s install section moves to twelve files, three skills, three command
files and twenty-five artifacts, with the cap raised in a dated entry.

Lane: openrepotools-3 (openRepoTools-3)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* WIP (second swap-imminent commit, unverified): the anchor `lane-start` appends a non-Claude agent's id to is the PUBLISHED row's last id, and four Amendment 17 cases in the suite ask what they say they ask

WIP FOR THE SAME REASON THE FIRST ONE IS: this is the predecessor's working
tree at the moment the swap was called, committed so that nothing is lost and
so that the next run's diff is against a tree that parses. The Amendment 17
section had not been through a green run when these lines were written; the
suites decide what survives.

`lane-start`: the id `append-session-id` is anchored on for an agent that is
not Claude is `$row_sid`, the LAST id of the published session cell, which is
the same anchor the minted-uuid append uses and for the same reason —
`append-session-id` refuses unless its anchor occurs exactly once in that cell,
so a stale copy of the row is a refusal that prints the act rather than a
corruption. `$cell_anchor` is not that id: it is set only where a session is
live in this window whose uuid the cell lacks, which is never the case for the
launch of an agent that is not Claude, so the branch as written took the
empty-anchor door every time.

The suite, four cases that were asking something other than what they read:
`lane-handoff`'s stdout is kept in `hf1_out` because a `swapped` read stands
between the run and the assertions that quote it; the unwritable-record `/ctx`
case is asked from a window NO live record names, so that there is no transcript
uuid anywhere and `write_event` really does refuse (Amendment 11(e)) rather than
being handed one by the window read; the shared-`projects/` case gets its own
lane `repoSH-1`, because `repoFR-1`'s cell has since gained the uuid the
`LANE_START_FRESH=1` run minted and that one has no transcript at all — the
other branch; and the live-holder record is named for ANOTHER lane, which is the
shape Amendment 18(h) is written about: a second process on one transcript that
the lane's own liveness read does not see.

Lane: openrepotools-3 (openRepoTools-3)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* `lane-handoff` is a command in the index and not only after `--install` chmods it: the executable bit, the macOS parse step and the mode of every placed file are each derived from a list the next file joins by existing

THE PROOF THE TWO WIP COMMITS SAID WAS NOT IN YET. `bash tests/test_lane_helpers.sh`
is **1630 passed, 0 failed, 0 skipped, 0 pending**, with the 129 cases of
`== Amendment 17: the handoff is the swap ==` among them — the section that had
not been reached when the swap was called. Nothing in the amendment's own code
needed changing for that; what this commit carries is what reading the file
alongside the suite found.

`lane-handoff` WAS COMMITTED 100644. Every other file `--install` places is
100755 in the index and `repos.tsv`, which is data, is not. The installed copy
runs anyway — `--install` stamps 755 on everything it places — so the defect has
no symptom there; what it costs is `./lane-handoff` from a clone, and the `cp -p`
this suite copies it with, which takes the mode git recorded. The suite's own
case did not see it because the fixture ran `chmod 755` on the copy. That line is
gone: the case now reads back the mode THE REPOSITORY carries, which is the only
mode anyone else gets.

AND THE THREE LISTS IT WAS MISSING FROM, each closed so that the next file joins
by existing rather than by being remembered:

* `ALL_BASH` gains it through a new `HELPER_BASH`, with the reason it is in
  neither list beside it stated where the other two state theirs — not
  `SHIPPED_BASH`, because `R-A11-11` (*a swap is never left unwritten*) is why it
  does not `set -e`; not `LANE_BASH`, because it carries no copy of Amendment
  9(a)'s workspace resolver, resolving no workspace and reading the one path it
  needs back from `lanes-edit.sh workspace-root`. So 837 lines of shipped bash
  now take the parse gate, the LF index, the flag-spelling rule and the four
  bash-3.2 text rules; it passes all of them as written.
* the macOS job's `/bin/bash -n` step, which is a HAND-WRITTEN LIST and the only
  place the README's bash-3.2 claim is asked of bash 3.2. `lane-handoff` was not
  in it and nothing said so: the job goes green and proves one file less than it
  claims. `test_the_macos_job_parses_every_bash_file_this_repository_ships` reads
  that step and holds it against `ALL_BASH`, per name, because `bash -n a b`
  parses `a` and hands `b` to it as `$1`.
* the index mode, read out of the installer's OWN `INSTALLABLES=( … )` rather
  than a list in the suite — 755 for a command, 644 for the `.tsv` that is read
  and never run. That is the test that would have caught this the moment
  `lane-handoff` joined the installer's list, with nobody coming here first.

THE COUNTS THAT STATE THEMSELVES, finished: `AGENTS.md` and `docs/README-lanes.md`
still said eleven files, two skills and eighteen artifacts — twelve, three, three
and twenty-five — and four docstrings in the installer's tests counted the same
way. `README.md`, the installer's prose and every derived count moved with the
first WIP commit; these are the ones that state rather than derive.

lane-collision-protocol **Amendment 17** (in force 2026-09-14T09:45:33Z),
**Amendment 18(d)** and **Addendum 2**; #36.

Lane: openrepotools-3 (openRepoTools-3)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* The `/ctx` respawn types the word Amendment 18 Addendum 2 gives it — `lane <lane>` where that word is on PATH, the launcher where it is not, and never a `lane` this estate did not place

Addendum 2 clause (i-8), in force 2026-09-14T16:50:32Z, says the respawn after
`/ctx` *"relaunches the lane's own pane with `lane <name>` (its parked branch is
exactly Amendment 11(i)'s act), or through the launcher directly"*, and its
adoption line names this issue: *"#36 (`/ctx`) respawns
with `lane <name>`"*. What this branch had was the second half of that sentence
and never the first: every respawn was `pclaude --lane <lane> <profile>`.

So `lane-handoff --restart` now asks PATH for the word and types it —
`LANE_START_FRESH=1 lane <lane>`, with NO profile argument, because `lane
<name>`'s parked branch reads the record this command has just written for the
lane's directory and profile and asks nothing. The launcher line stays as the
door the same clause leaves open, for the workstation where `lane` is not
installed yet: it arrives with #43 and this act ships
first, and a respawn is the one act no later refusal can undo — the pane's
process is already dead when the new command fails — so the word is used only
where this command can SEE it.

AND NOT EVERY `lane` ON A PATH. `pclaude` is nobody else's name; `lane` is an
ordinary English word, and a pane respawned over somebody else's `lane` is a
pane the person cannot get back. The file is read for the string every bash file
this toolset ships carries (`lane-collision-protocol`, in all six of them), and
one that carries none is passed over for the launcher with a line saying which
file was passed over — the safe side of the two, because it still starts the
lane.

The skill and the manual say the same thing in the same order, and the skill's
snippet is now the choice the command makes in code rather than one branch of
it.

TESTS, in `tests/test_lane_helpers.sh` § "Amendment 17", all three on a PATH
THIS FILE BUILDS: `a17_path_without_lane` drops every directory holding a `lane`
from the section's PATH, so the cases ask their own question on a workstation
that has the word (#43 places it in `~/.local/bin`) and on one that does not.
`repoHF-8`, with a fake `lane` carrying the marker, asserts the respawn line is
that file and `repoHF-8`, with the FRESH seam still on it, with no profile
argument, and never the launcher; `repoHF-9`, with a fake `lane` carrying none,
asserts the launcher line and the sentence naming what was passed over;
`repoHF-5`, on a PATH with no `lane` at all, keeps every assertion it had.

VERIFIED HERE: `bash -n lane-handoff`, `bash -n tests/test_lane_helpers.sh`.
Suite before this commit, at e25b54c: 1630 passed, 0 failed, 0 skipped. THE RUN
OF THE SUITE WITH THESE CASES IN IT FOLLOWS THIS COMMIT — it is pushed now
because a context clear of the driving session is imminent and an unpushed
change is a change the next session cannot see.

Lane: openrepotools-3 (openRepoTools-3)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* The `/ctx` respawn reads the `lane` it found through a redirect, because the job that runs this suite under BSD `head` is the macOS one and a portability question is not one to answer inside the act that kills a pane

`lane-handoff --restart` reads the head of the `lane` it found on `PATH` for the
string every bash file this toolset ships carries, and read it as
`head -c 8192 -- "$lane_word_path"`. The macOS job runs `python3 -m pytest tests
-q` — which is this shell suite, through `tests/test_lane_helpers_suite.py` —
under BSD `head`, whose option handling is its own; `head -c 8192 < "$file"`
hands the file to the utility as a file descriptor and asks nothing of its
argument parsing. Same bytes, same guard, one fewer question.

Parsed: `bash -n lane-handoff`. The suite at f8a4ce6, with the three PATH cases
this guard exists for, was 1821 passed, 0 failed, 0 skipped; the run over this
line follows.

Lane: openrepotools-3 (openRepoTools-3)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* A handoff names the KIND of clear it was written for, because an in-process clear does not kill this lane's writers — the top block runs `ListAgents` first and relaunches only what it does not name; and the two stale tests CI found in the same breath

THE MEASURED FACT, in this lane on 2026-09-14: a harness `/clear` mints a NEW
TRANSCRIPT ID IN THE SAME PROCESS. Every subagent the lane has running survives
it — only the tool calls they had in flight die, a `Bash` killed that way exiting
137 — and the handoff written before that clear said *"every agent dies with this
context; relaunch every writer"*, which was FALSE. A next session that believed
it would have put a SECOND writer on a worktree the first one still held, and
two writers on one worktree is how the work in it is lost.

Only a NEW PROCESS takes the writers with it: the pane respawned (`--restart`,
which is what `/ctx` does here), the session ended (`--exit`), or the launcher
run after a plain handoff. So the act now records WHICH IT WAS and the top block
tells the next session what to do about it:

* `lane-handoff --in-process` — the clear that keeps this process. Refused with
  `--restart` and `--exit`, because each of those replaces the process every
  writer is a child of. Default, and every other form: `respawn`.
* the `PAUSED` payload carries `kind <in-process|respawn>` after `agent` and
  `transcript`, on the same argument those two were added on: a reader written
  before the field stops at the sub-field it knows. It is in the RECORD and not
  only in the handoff because the record is what a later reader and a launcher
  have.
* the top block's act (3) is now, in both kinds: **RUN `ListAgents` FIRST. A
  WRITER STILL LISTED IS ALIVE AND OWNS ITS WORKTREE: DO NOT RELAUNCH IT.
  Relaunch only writers `ListAgents` does NOT name, and only after
  `git status --short` and `git log @{u}..` in that worktree say where it
  stood.** The `WRITERS` section repeats the rule where the list is, and the
  kind adds its own sentence — *expect `ListAgents` to name none of them* for a
  respawn, *expect it to name every one, and relaunch NONE* for an in-process
  clear.

The skill, `commands/ctx.md` and the manual say the same in the same order, and
the skill carries the table of the two kinds so that a session writing the block
by hand writes the same two texts.

AND THE TWO TESTS CI FOUND, both of which were green here for the wrong reason:

* `test_the_swap_skill_snippet_and_its_prose_spell_the_same_command` read
  `skills/lane-swap/SKILL.md`, which Amendment 17(a) turned into an ALIAS that
  restates no step — so it was asserting over an emptiness. It now reads the
  installer's own `SKILL_NAME`, which is where the canonical skill is declared
  once, and every assertion it makes lands on the file that carries the act
  (2 `restart_cmd=` branches, no `pclaude run` unexplained, `--lane` leading).
* `test_no_committed_file_names_a_host_absolute_path` caught three `/home/b/`
  spellings in the manual's examples. They are `~/projects/repoHF` and
  `~/.claude/projects` now, which is what every other example in that file uses.

AND ONE SUITE AT A TIME, in `AGENTS.md` where an agent is sent to read it: six
lanes build in sibling worktrees of this checkout and FOUR SUITES RAN AT ONCE
under the guard they had been given — `pgrep -af 'python3 -m pytest' | grep -v
pgrep >/dev/null` never waits where `grep` is the harness's own shell function
(status 1 when its stdout is `/dev/null`), and its unanchored pattern matches the
guard's own command line. The working form is an anchored `pgrep -fc` with its
pattern split so it cannot match itself, then `flock` so the waiters cannot all
start together. The cap entry for those nineteen lines is in the test that holds
it.

SUITE: `bash tests/test_lane_helpers.sh` — 1821 passed, 0 failed, 0 skipped at
`f8a4ce6` (the merge of Amendment 15); **1844 passed, 0 failed, 0 skipped** with
the `kind` cases of this commit (+23: the record's kind, the block's ListAgents
rule and its two texts, the `--in-process` record and block, the pane untouched,
and both refusals). `bash -n` on `lane-handoff` and the suite. The pytest run
that would have covered the two test files above is NOT started: this workstation
has three suites running under other lanes and this lane is shutting down on
Brett Heap's word; CI on #47 is the authority and has already run these two
tests red on their pre-fix bytes.

Lane: openrepotools-3 (openRepoTools-3)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* A pane is never respawned over a record that is only part written, a transcript never half-moves between profiles, and the launch line is quoted by something bash 3.2 and BSD `sed` both read — Copilot rounds 1 and 2, Amendment 17 Addendum 1, and the suite's own lock

THE macOS JOB'S THREE RED CASES WERE THIS BRANCH'S OWN, and the defect is one
line that has been in `lane-start` since before this amendment:

    quoted() { printf '%q ' "$@" | sed 's/ $//'; }

Two defects in it, and only their combination is fatal. `printf '%q '` renders
differently under BASH 3.2 — what Apple ships, and what `tests-macos` runs
(`/bin/bash` 3.2.57 on macOS 26.6.2): bash 4.4 and later give a multi-line
argument back as `$'…\n…'`, one readable and runnable word, while 3.2 has no
such form and escapes every space and every byte of every multi-byte character
with a backslash of its own. And the PIPE made that fatal rather than ugly:
under `set -o pipefail` the function's status is the pipeline's, and BSD `sed`
in a UTF-8 locale (`LC_ALL=en_US.UTF-8` there) refuses bytes that are not a
valid sequence — `RE error: illegal byte sequence` — which is exactly what
3.2's `%q` makes of the em dash every document in this estate is written with.
`quoted` is a BARE command in the `--no-launch` branch, so `set -e` ended
`lane-start` there: exit 1, empty stdout, three assertions red and no other
platform able to see it. The one caller that passes a multi-line argument is
this amendment's own — the `--no-launch` print of a launch carrying the
handoff's top block as its first prompt — which is why it went red here and
nowhere before. It is now POSIX quoting of the file's own, in `lane-start` and
`lane-handoff`: safe words bare, anything else in single quotes with `'\''`
for its own. `restart` carries the same line and is untouched — #43 deletes
that file in flight and a conflict there costs it a round.

COPILOT ROUND 1 (`db206db`), the four inline findings:

* `lane-handoff:842` — the `--restart`/`--exit` gate tested only the
  object-log write. The record is THREE writes: the ROW is what every other
  lane reads this lane's state from, and the HANDOFF's top block is literally
  the respawn's first prompt. It now gates on all three and each refusal names
  the act that clears it.
* `lane-start:1692` — `readlink -f` is not stock macOS userland, so `real_of`
  fell back to the UNRESOLVED path, two spellings of one directory compared as
  two directories, and the shared-`projects/` case — every case on a
  launcher-configured workstation — would have moved a transcript that never
  needed moving. Now `( CDPATH=''; cd -P -- "$1" && pwd -P )`.
* `lane-start:1781` and `:1788` — the sibling `<uuid>/` move's status was
  discarded and the pointer's write was suppressed. The `.jsonl`, its sibling
  and the pointer are ONE TRANSACTION now: each step is checked, a step that
  cannot complete puts back what the steps before it moved (`ft_rollback`) and
  refuses, and the refusal names anything the rollback itself could not undo.
* DECLINED, both mode findings (`lane-handoff:1`, `tests/test_repo_hygiene.py:198`):
  `git ls-tree db206db lane-handoff` → `100755`, and `git ls-files -s` says the
  same today. What they read is that test's docstring describing the FIRST
  Amendment 17(a) commit; it now names `e25b54c` as the commit that fixed the
  mode and points at `git ls-files -s`. The same stale rationale in the A17
  fixture is corrected.

From round 1's SUPPRESSED list: `lane-start:1776` (`--dry-run` moved the
transcript before reaching the exit that says it moved nothing),
`lane-handoff:612` (`handoff_written=1` set whether or not the write
succeeded), `lane-handoff:494` (a failed `mktemp` gave a WRITERS section with a
count and no entries), `lane-handoff:395` (the record's `transcript` came from
`$sess_uuid`, not the resolved `$uuid`, so a handoff from a bare shell wrote
`transcript none` about a conversation whose id the same line carries),
`lane-start:1687` and `:1719` (`$CLAUDE_PROFILES_HOME` ignored by both profile
walks, though this repository's own README documents it), `lane-start:1881`
(a failed `lane-agent` read launched the default agent, which is the one thing
that read's own refusal forbids — it now refuses and names `--agent`), and
`lane-start:1997` (a lane with NO ROW wrote `RESUMED` as its object log's first
line).

COPILOT ROUND 2 (`ac0c697`):

* `lane-handoff:303` — the read fence treated exit 2 as "no answer", but
  `canon_lane` spends 2 on a register holding two rows that differ only by
  case. `lane-handoff` fell through to the next rung and would have renamed a
  window, written a record and respawned a pane for whichever lane it answered
  with. The refusal is now asked for by its own words, as `lane-start:755-767`,
  `lane-end` and `restart` ask for it, and relayed.
* `lane-handoff:360` and `:403` — the lane is canonicalised through
  `canon-lane` BEFORE the window rename, so the row's spelling is what the
  rename, the top block, the payload, the log's lane field and the restart line
  all carry; and a `--lane` that is not a lane name is refused there rather
  than after the window has been renamed.
* `lanes-edit.sh:3946`/`:3963` — `pause_subfields_check` accepted a payload
  carrying exactly one of `agent`/`transcript`, and an empty value parsed like
  an absent one. Both are refused now, with the value judged before the pair so
  a malformed one is still refused for the malformation.
* `lanes-edit.sh:5723` — `--utc` was taken for every verb and only its shape
  was checked; it is the late `PAUSED`'s seam and is refused for everything
  else, because file order is what every state read means by "last".
* `lane-start:1899` — `lane-transcript` answers for the LANE, so `--agent
  codex` over a record that says `agent claude` would have appended
  `Codex <claude uuid>` to the row's cell. The id is carried only where the
  recorded agent IS the agent being launched.
* `lane-start:1982` — the launched agent's name is exported, so a `codex`
  session's own handoff does not record `agent claude`.

FILED, NOT FOLDED IN: #55 (`swapped`'s first field is the
LOG's spelling and not the register row's — Amendment 15's claim, landed on
`main` with #41, so it predates this diff) and #56 (the
cross-profile transcript-holder scan tests only `kill -0`, where
`lanes-edit.sh`'s one liveness contract tests three things). Both claimed by
this lane.

AMENDMENT 17 ADDENDUM 1, in force 2026-09-14T20:59:31Z (brettheap/new-workstation#40,
`26a0d5b`), whose adoption line names this issue:

* (h) the record names the KIND, and there are THREE: `--in-process` writes
  `in-process`, `--restart`/`--exit`/`--late` write `respawn`, and a PLAIN
  handoff writes `unknown` — the act that follows it is not this command's to
  know. The word is in the payload (`; kind <k>`) and in the line's FREE TEXT
  after the why (`clear in-process`, `clear respawn`, `kind unknown`), which is
  the addendum's own spelling.
* (i) the WRITERS section is a list to COUNT, not a list to relaunch, and its
  first line is the clause's own words under EVERY kind.
* (j) the kind says what to EXPECT and never what to do, so `relaunch every
  writer below` and `relaunch NONE of them` are both gone from the template.
* (k) one worktree, one writer, in the first acts, with the retire act for a
  second one found.

AND THE SUITE'S OWN LOCK (#51). Four `python3 -m pytest
tests -q` runs were live at once on this workstation on 2026-09-14, every one
past a guard copied into four briefs: `pgrep -af '…' | grep -v pgrep
>/dev/null` never waits, because `grep` under the harness is a shell FUNCTION
whose status is 1 when its stdout is `/dev/null`, and its pattern is unanchored
besides. `tests/run.sh` is that guard written ONCE — wait, take
`${TMPDIR:-/tmp}/openrepotools-pytest.lock` (`flock`, or a `mkdir` lock on
macOS, which has none), wait again inside it, then run the suite — named in
`AGENTS.md` and `docs/README-lanes.md` as the way to run it, parsed by the
macOS job with every other bash file here, and pinned by a hygiene test that
holds the lock path in both places.

Closes #51 (with #36).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UFwD7bCS73jaUMJ2vuo5m

* A lock nothing can validate is stale and not eternal, a transcript never moves onto an occupied name, and the two acts that reach a handoff through a symlink resolve it the way macOS can — Copilot round 3

The round before this one went green on all four CI jobs, macOS included, which
is what made these findings readable rather than hidden behind three red cases.

`tests/run.sh` — THE LOCK THIS PULL REQUEST SHIPS AS THE WAY TO RUN THE SUITE,
and three things a `mkdir` lock has to get right that the first shape of it got
wrong (`tests/run.sh:109`, `:113`, `:120`):

* A LOCK WITH NO PID IN IT IS STALE, NOT ETERNAL. The pid is written the instant
  after `mkdir` returns, and a run killed in that instant left a directory
  nothing could validate — which the loop waited on for ever, blocking the
  estate's one test entry point for everybody.
* A TAKEOVER IS A CLAIM, NOT A DELETE. Two waiters that both saw a dead holder
  both `rm -rf`'d the name, and if one had already re-made it the other deleted
  the NEW holder's lock and both suites ran. It is a `mv` now — one rename, one
  winner, the loser loops — with the claimed directory RE-READ after the rename,
  because the only way a live holder can be behind that name is if it appeared
  between the read and the rename, and then it is put straight back. A stale
  state must also hold for three polls before it is claimed at all.
* THE SIGNAL HANDLERS EXIT. `trap 'rm -rf …' EXIT INT TERM` released the lock on
  a Ctrl-C and then carried on running the suite, which is the collision the
  lock exists for.

`lane-start:1878` — AN OCCUPIED DESTINATION IS REFUSED BEFORE ANYTHING MOVES.
`mv <dir> <existing dir>` does not fail: it moves the source INSIDE it. A
`<uuid>/` already in this profile would therefore have taken the other
profile's sidecar as `<uuid>/<uuid>/`, been marked moved, and left the lane
resuming with the transcript's state split from the transcript. Which of two
`<uuid>` directories is this lane's is not a thing to guess at, so the read
names what is there and stops — and a name that is NOT a directory still falls
to the transaction below, where `mv` really does fail and the rollback puts the
transcript back.

`lane-handoff:812` and its twin in `lane-start` — `readlink -f` IS NOT IN THE
STOCK macOS USERLAND, which is the same rule `real_of` took last round. Where it
is absent the fallback hands the path back UNRESOLVED, so `hs_repo` — which git
answers with a PHYSICAL path — and `hs_rel`, derived from the symlinked
spelling, disagree; `git add` then refuses the pathspec and the handoff this act
has just written, or the Rule 3 stamp it has just spliced, is left uncommitted.
A handoff is reached through the estate's `handoffs` symlink on every
workstation that has one. Both now resolve the PARENT with `cd -P`/`pwd -P`.

`lane-handoff:1052` — `head -c 8192 … | grep -q` TAKES SIGPIPE UNDER `pipefail`.
`grep -q` exits the instant it matches, `head` writing the rest of a word larger
than 8192 bytes dies with 141, and the pipeline's status is that — so the
estate's OWN `lane` was classified as somebody else's and `/ctx` respawned
through the launcher instead of the word. `grep` reads all of the bounded input
now, and the suite's fake `lane` is padded past 8192 bytes so the three PATH
cases prove it.

`lane-start:2132` — A LAUNCHER THIS WORKSTATION DOES NOT HAVE IS REFUSED BEFORE
ANY WRITE. The row, the object log and the Rule 3 stamp are all written before
the `exec`, so a `codex` that is not installed recorded the lane as RESUMED and
then exited 127: a false transition in two append-only files that a person has
to repair by hand.

`lane-handoff:860` — the `window` sub-field's fence gains `; ` and `"`, the two
its own comment already claimed it dropped. `window` is two space-separated refs
in ONE sub-field, so a `; ` inside it is read back as the end of the sub-field
and every reader recovers a window ref that is not the lane's.

FILED, NOT FOLDED IN: #65 — the `/ctx` respawn recognises
this estate's `lane` by a string in the word's own head, which is a heuristic and
not provenance: any executable can carry it. Tightening that to the directory
`--install` places these commands in is a change to the behaviour Amendment 18
Addendum 2 (i-8) adopted, and to three of this suite's PATH fixtures with it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UFwD7bCS73jaUMJ2vuo5m

* The codex-absent case asks its question on a PATH this file built, because the workstation it was written on HAS codex installed

Four assertions of the round-3 case `--agent codex on a workstation with no
codex is refused` went red on Eagle and green on every CI runner, which is the
shape of a fixture asking a different question rather than of a defect: the
workstation carries `~/.npm-global/bin/codex`, so the refusal correctly did not
fire and the launch went ahead.

`a17_path_without <word>` is the generalisation of the `a17_path_without_lane`
the three `lane` cases already use, and for the reason that helper's own comment
gives — *a case that asked "what happens where there is no `lane`" against a
workstation that has one would go green on the answer to a different question*.
The codex case now builds its PATH the same way.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UFwD7bCS73jaUMJ2vuo5m

* The codex-absent case is not asked, and the test file says why: this estate installs that launcher in /usr/bin, so "a PATH without codex" is a PATH without bash

The round-3 fix at `lane-start:2132` stands — a launcher the table knows and
this workstation does not have is refused in 5a, before the row, the object log
and the Rule 3 stamp are written, rather than after `exec` exits 127 and leaves
a false PAUSED→RESUMED transition in two append-only files.

Its case cannot be asked here. `a17_path_without codex` is how the three `lane`
cases build their PATHs, and on this workstation `codex` is `/usr/bin/codex`,
`/bin/codex` AND `~/.npm-global/bin/codex` — the launcher is installed globally
on every lane workstation — so subtracting every directory that holds it returns
the EMPTY string and takes `bash`, `git`, `awk` and `sed` with it: the run then
dies at its own shebang with `/usr/bin/env: 'bash': No such file or directory`.
That is why the four assertions were green on every CI runner (which has no
codex) and red at the desk, which is the exact failure mode the
`a17_path_without_lane` comment exists to prevent.

So the reason is written where the case would have been, with what a fixture
would have to express to ask it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UFwD7bCS73jaUMJ2vuo5m

* The merge takes main's openRepoShape pin, which `git add -A` had put back

`git add -A` while resolving the merge staged the submodule at the commit this
worktree happened to have checked out (`ba14e1d`, this branch's base), which
reverted `main`'s own pin bump to `39d5c98` (#46) in the gitlink while
`contracts/openreposhape-pin.yaml` carried main's. The pin file and the gitlink
move in ONE commit and never apart — AGENTS.md rule 3 of the pinned standard —
so the gitlink is put back where main has it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UFwD7bCS73jaUMJ2vuo5m

* The re-mode case names the skill it actually chmods, because Amendment 17(a) renamed that act and the case came from a branch written before the rename

Both Linux CI jobs went red at `5c162df` — the merge of `main` at `b5244e2` —
on one assertion of `test_a_skill_whose_bytes_are_right_and_whose_mode_is_not_is_re_moded`,
and the defect is a name spelled twice in one file.

`skill_paths()` DERIVES its two paths from `SKILL_NAMES`, so on this branch it
hands back `handoff`'s — Amendment 17(a) renamed the act from `lane-swap` to
`handoff` and kept `lane-swap` as an alias file — while the case that #40 and
#44 added on `main` asserts the printed line with the literal `lane-swap:`. The
installer was right: it chmod'ed the path it was given and named the skill that
owns it (`handoff: … (mode restored to 644)`). The merge that brought a case
written before a rename on to the branch that made it is exactly where a
spelled name goes stale, and the file's own header says so five lines above the
constant it should have used.

Both assertions take `{SKILL_DIR_NAME}` now, which is the same derivation the
paths take.

Merges `origin/main` at `a954435` (#50, `commit_push`'s conflict die and its
`tests/test_lane_helpers.sh` § #32) on top of `b5244e2` (#44, the installer's
mode stamping and unwritable-target refusal; #53's `skills/restart/SKILL.md`).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UFwD7bCS73jaUMJ2vuo5m

* The `--in-process` help says what to EXPECT, `--late --in-process` is refused as the contradiction it is, and a comment stops claiming a refusal `canon_lane` does not make — Copilot rounds 4 to 6

Three findings taken; the other ten are #72, filed with
what each would cost, because every one of them hardens an act that already
refuses rather than fixing something that writes the wrong thing.

* `lane-handoff:65` and its `--in-process` usage line still told the reader the
  top block would say *relaunch NONE of them* — the instruction Amendment 17
  Addendum 1 (j) removed from the template two commits ago. `usage()` PRINTS
  that comment block, so the command was handing a person the opposite of the
  adopted rule while generating the right block. Both now say the block tells
  the next session what to EXPECT, and that what to DO is clause (i)'s count.
* `--late --in-process` was not refused, and the kind block takes the
  `in-process` arm first — so a late record could have said `kind in-process`
  and told the next session to expect writers live that died with the process
  the record is about. A late record is written for a session that has ALREADY
  ENDED; that is what makes it late.
* And the comment over the `canon-lane` read claimed it refuses "a `--lane`
  this estate has no name for". It does not: `canon_lane` answers with the
  TYPED spelling where no row matches, deliberately, because `lane-start` uses
  the same resolver for a lane it is about to create. What that read refuses is
  an AMBIGUOUS register (15(d)) and a value that is not a lane name at all; a
  well-formed lane with no row is named by the writes below, and the comment
  says so now rather than promising a check that is not there.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UFwD7bCS73jaUMJ2vuo5m

* The documented guard counts the way the wrapper counts, and `pytest_live` stops reporting a failure in the same breath as a zero — Copilot rounds 7 and 8

Three threads on one inconsistency and one on its neighbour, all of them this
branch's own:

* `AGENTS.md` and `docs/README-lanes.md` showed the guard with `pgrep -fc`,
  which `tests/run.sh` deliberately does NOT use — `-c` is not in every `pgrep`
  this repository runs under, and it is the COUNT and never `pgrep`'s exit
  status that decides. A document that shows a person a non-portable form of
  the very guard the wrapper exists to make portable is worse than one that
  shows none. Both snippets count with `awk` now, as the wrapper does, and both
  say the wrapper is the canonical implementation and why the lines are
  repeated at all.
* `tests/run.sh:66` — `pgrep` exits 1 when it matches nothing and `set -o
  pipefail` makes that the pipeline's, so `pytest_live` printed `0` and
  returned a FAILURE together. Every caller today reads the printed count,
  which is what its own comment says; the explicit `return 0` is what keeps
  that true for the first caller that uses it in a condition.

The two remaining findings of those rounds — parametrising the skill-placement
assertions over `SKILL_NAMES`, and an `INSTALLABLES=( … )` regex that tolerates
a wrapped array — are #72's kind and are noted there.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UFwD7bCS73jaUMJ2vuo5m

* The skill records the KIND its command records, and asks `window-session` the question that read answers — Copilot round 9

Two conformance gaps between the two halves of ONE act, which is the thing
Amendment 17(a) exists to prevent: `skills/handoff/SKILL.md` is `/handoff` and
`/ctx` for a session with no `lane-handoff` on `PATH`, so a fact the command
writes and the skill does not is a record whose shape depends on which door the
person came through.

* `skills/handoff/SKILL.md:476` — the skill's `PAUSED` payload carried Amendment
  17(b)'s two sub-fields and not Addendum 1 (h)'s `kind`. A record from the
  skill was therefore one no reader could tell a surviving writer from a dead
  one by, while the top block it writes in the same act states the kind. The
  payload gains `; kind <k>` and the line's free text gains the same word after
  the why — `clear in-process`, `clear respawn`, `kind unknown`.
* `skills/handoff/SKILL.md:612` — the `/ctx` respawn asked `window-session` with
  `#{window_id}` alone (`@21`). That read matches a live record on the
  HARNESS's own `tmux` field, `<session>:<@id>`, and the three spellings of one
  window are never derived from each other — `lane-start:975-984` says so and
  `lane-handoff` reads it that way. The skill's `/ctx` therefore found no
  record, left `$pane` empty, and respawned something that was not the lane's
  pane. It asks with `#{session_name}:#{window_id}` now.

Three cases in `tests/test_lane_helpers.sh` § "Amendment 17", beside the ones
that already hold the skill to the command's words.

DECLINED, from the same round: `lane-start:2177`, that `--agent claude` should
skip the recorded transcript. Amendment 17(c) is explicit the other way —
*"`claude` with its transcript here resumes by id exactly as Amendment 6 has
always done; every other case launches the named agent with the handoff's top
block as its first prompt"* — and the suite's `--agent claude with no
transcript here` case is the branch that takes a new session. `LANE_START_FRESH=1`
is the seam for *a new session even though the transcript is here*, and it is
what `/ctx` sets.

FILED: `lane-handoff:924`'s late-record race is #72's
kind and is noted there — the preflight reads the lane's last line before the
poll and the refresh, and a `RESUMED` appended in that window would be
overtaken by the `PAUSED` this writes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UFwD7bCS73jaUMJ2vuo5m

* The skill's row flip reads BOTH state-cell shapes, because an ordinary fresh row is the one it did not know — Copilot round 10

`| WORD ·` is a state cell with history after it and `| WORD |` is one with
none, and a fresh row from `add-row` is the second. The skill's parser knew
only the first, so for those rows `$state` was empty, `replace-in-row` had no
anchor, and `/handoff` through the skill left the lane RUNNING in the register
with its handoff already refreshed and its PAUSED line already written — the
one state this act exists to make impossible. `lane-handoff` has read both
shapes all along and keeps whichever punctuation the row carries; the skill
does the same now, including in the replacement it hands the writer.

Two cases in `tests/test_lane_helpers.sh` § "Amendment 17", beside this round's
other two.

The round's other finding — a multi-line `why` reaching `append-line` and the
top block before the event writer validates it — is #72's
finding 6, filed in round 4 and noted again there with what `append-line` adds
to it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019UFwD7bCS73jaUMJ2vuo5m

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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