Repository navigation
A one-line record can't take a newline: append-line and lane-handoff's why refuse it before writing, and the deferred lane-start command keeps --agent (#85, #72 items 5, 6) - #218
Merged
Conversation
…s why refuse it before writing, and the deferred lane-start command keeps --agent (#85, #72 items 5, 6) `append_text_line` appended its text and only then proved the file had grown by one line, and the `append-line` verb had no guard of its own. A `lane-handoff` why carrying a newline therefore refreshed and committed a handoff whose top block was split in two, had its object-log PAUSED refused by `write_event`, appended two lines to LANES.md, and exited 0 — and the row flip right after it committed those two lines as a peer's `pre-existing` edit. Measured in the suite's own sandbox at f87fd5a. A carriage return passed every proof, because `wc -l` does not count one. One policy across #85 and #72 item 6: the writers refuse before they write (`append_text_line` before its mktemp, so every caller has the gate, and the `append-line` verb before the lock, so `handle_preexisting` cannot commit anything on behalf of a refusal); a why carrying CR/LF is refused at the door, before the identity triple, the refresh and every write, which is the only point that covers all four places the why reaches; and `sanitize` spells any break `; ` as the second line of defence. #72 item 5: the command `lane-start` prints for the deferred first act dropped `--agent`, so a lane resumed `--agent codex` over a record naming claude was handed a first act that `lane-agent` would settle as claude. It now carries the agent wherever AGENT_EXPLICIT is 1, quoted, after the lane's own arguments. Part of #85. Part of #72. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThe PR closes newline and carriage-return paths that could write malformed one-line records or leave lane-handoff partially committed, by validating inputs before any write or lock and retaining defensive sanitization; it also fixes deferred lane-start commands to preserve an explicitly selected agent, with focused regression coverage. Sequence diagram for pre-write newline rejectionsequenceDiagram
participant Caller
participant LaneHandoff as lane-handoff
participant AppendLine as append-line
participant Writer as append_text_line
participant Register as Register
Caller->>LaneHandoff: lane-handoff --lane lane why
alt why contains CR or LF
LaneHandoff-->>Caller: exit 2
Note over LaneHandoff,Register: No refresh, lock, write, or commit
else append-line text contains CR or LF
Caller->>AppendLine: append-line text
AppendLine-->>Caller: exit 2
Note over AppendLine,Register: Lock is not taken
else one-line text
AppendLine->>Writer: append_text_line text
Writer->>Register: append one line
end
Sequence diagram for deferred lane-start agent preservationsequenceDiagram
participant User
participant LaneStart as lane-start
participant Deferred as Deferred lane-start
User->>LaneStart: lane-start --agent codex lane
alt transcript UUID unknown
LaneStart->>Deferred: lane-start --no-launch lane --agent codex
Deferred-->>User: First act preserves explicit agent
else default agent with no explicit selection
LaneStart->>Deferred: lane-start --no-launch lane
Deferred-->>User: First act uses recorded/default agent
end
Flow diagram for safe one-line record handlingflowchart LR
Input[Input text] --> Validate{Contains CR or LF?}
Validate -- Yes --> Refuse[Exit 2; nothing written]
Validate -- No --> Sanitize[sanitize replaces line breaks with ; ]
Sanitize --> Write[Write one-line record]
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
This was referenced Oct 13, 2026
…2, so CI tests this PR on today's main Main moved 15 commits past this branch's base f87fd5a, through the suite's machinery and pin (.github/workflows/tests.yml, contracts/openreposhape-pin.yaml, tests/conftest.py, tests/test_repo_hygiene.py, upstream/openRepoShape), after this PR's last green run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…2, so CI tests this PR on today's main Main moved 4 commits past this branch's base 2cb14a9, through the suite's machinery and pin (), after this PR's last green run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
brettheap
added a commit
that referenced
this pull request
Oct 18, 2026
…ane holds the object (#74) (#204) Landed on Brett Heap's word "land #204 #206 #208 #218 #220 #221 when green" (2026-10-18), by lane openRepoTools-5. Required Linux CI (guard-launch-mode, tests, tests-no-submodule; run 39137725493) was green on head af5b7ce with main 586dea2 in its tree. Independent review (lane 5, Opus, of 4996156; the merges of main add nothing to the PR's diff): LAND. Closes #74 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap
added a commit
that referenced
this pull request
Oct 18, 2026
…so a migrated PAUSED row stops showing NO LOG (#89) (#206) Landed on Brett Heap's word "land #204 #206 #208 #218 #220 #221 when green" (2026-10-18), by lane openRepoTools-5. Required Linux CI (guard-launch-mode, tests, tests-no-submodule; run 39137733527) was green on head cd3ced0 with main 586dea2 in its tree. Main 0a49697 merged with head cd3ced0 is tree f88c2f1, which queue run 39137981522 (workflow_dispatch of tests on 3f5f4b9) tested whole and green. Independent review (lane 5, Opus, of 00b75e6; the merges of main add nothing to the PR's diff): LAND. Brett Heap confirmed the unpickable (row) lanes as built (2026-10-18). Closes #89 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap
added a commit
that referenced
this pull request
Oct 18, 2026
…or binding log instead of guessing a branch (#60) (#208) Landed on Brett Heap's word "land #204 #206 #208 #218 #220 #221 when green" (2026-10-18), by lane openRepoTools-5. Required Linux CI (guard-launch-mode, tests, tests-no-submodule; run 39137741203) was green on head 3edaf0a with main 586dea2 in its tree. Main be82c20 merged with head 3edaf0a is tree cfd2ee6, which queue run 39137988005 (workflow_dispatch of tests on 9db4863) tested whole and green. Independent review (lane 5, Opus, of c252a3c; the merges of main add nothing to the PR's diff): LAND. Closes #60 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap
marked this pull request as ready for review
October 18, 2026 15:58
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 1 day and 9 hours by commenting |
brettheap
added a commit
that referenced
this pull request
Oct 18, 2026
…ane-handoff renames the window only once the handoff can no longer be refused (#80 slices B and C) (#220) Landed on Brett Heap's word "land #204 #206 #208 #218 #220 #221 when green" (2026-10-18), by lane openRepoTools-5. Required Linux CI (guard-launch-mode, tests, tests-no-submodule; run 39137756635) was green on head 0b531e5 with main 586dea2 in its tree. Main 01eff2c merged with head 0b531e5 is tree a388312, which queue run 39137998354 (workflow_dispatch of tests on f335793) tested whole and green. Independent review (lane 5, Opus, of 9f6fd2d; the merges of main add nothing to the PR's diff): LAND. Brett Heap confirmed the rename trade as built (2026-10-18). Part of #80 (slices B and C). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap
added a commit
that referenced
this pull request
Oct 18, 2026
…ession ids, so the dead-lane --force check and duplicate-holder fail closed (#67) (#221) Landed on Brett Heap's word "land #204 #206 #208 #218 #220 #221 when green" (2026-10-18), by lane openRepoTools-5. Required Linux CI (guard-launch-mode, tests, tests-no-submodule; run 39137764839) was green on head 14696c7 with main 586dea2 in its tree. Main be0743b merged with head 14696c7 is tree 0052eff, which queue run 39138003629 (workflow_dispatch of tests on 147cbf6) tested whole and green. Independent review (lane 5, Opus, of fbd2bb1; the merges of main add nothing to the PR's diff): LAND. Closes #67 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap
pushed a commit
that referenced
this pull request
Oct 19, 2026
…tized as lane-handoff's sanitize does, and a why carrying a line break is refused before step 1 (#80 item 7, #85) This commit and the WIP checkpoint before it (520b98f) are one slice. Main b707bd7 is merged in, so #260's sread 15(d) arm, its canon-lane read after step 1's rungs and its whole-string window checks sit in front of step 4's writes, unchanged. #80 item 7: step 4 of skills/handoff/SKILL.md computed why_text and then wrote neither record with it. The object log's PAUSED free text was the literal "on <operator>'s word: <sanitized verbatim>", and the register's file-level line carried that and "<what's open, or NOTHING CLAIMED>", so running the block as printed recorded the placeholders. Both writes now carry $why_text, and the register line is lane-handoff's own line word for word, which is what "the same act writes the same record" (Amendment 17(a)) asks. That drops the "<what's open, or NOTHING CLAIMED>" segment, which lane-handoff's line never carried. #85, the skill's half: #218 made append-line and lane-handoff refuse a why carrying CR or LF before anything is written, and made lane-handoff's sanitize spell every break "; ". The skill did neither. Its why_text was "$why" as typed, so it did not even replace " — ", though its own prose says it does. A why with a break would reach step 2's top block, and then both step 4 writers would refuse it after step 2 had already committed the handoff. The skill now refuses such a why before step 1, with the words lane-handoff uses at its own door. Step 4 then sanitizes the why as lane-handoff's sanitize does (CRLF, CR and LF each one "; ", and every " — "), with an empty why written "handoff". That is the second line of defence. tests/test_handoff_skill_80_85.py runs the door block and step 4's block under bash and zsh -f against a stub helper. It compares the skill's two writes with what lane-handoff's own sanitize, kind-word lines and register-line template produce, all cut out of lane-handoff. This commit makes the door-position case read only the lines that run. After the merge, #260's comments in the preamble's fence cite lane-handoff by name, and a comment that names an act is not that act. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



What was wrong
#85, and #72 item 6: a newline reached a one-line record.
lanes-edit.sh'sappend_text_lineappended its text with>>and only then proved the file had grown by one line. Theappend-lineverb had no guard of its own, so a text carrying a newline was written whole and refused afterwards (exit 5,append changed line count by 2).write_eventalready refused a newline before writing (the R21 guard), so the object log was safe and the register was not.lane-handoffputs itswhyinto four places, andsanitize(which only replaced—) covers only the last two:To measure it, I ran the suite's own sandbox (a trimmed
tests/test_lane_helpers.sh: setup plus the Amendment 17 section) against mainf87fd5awithlane-handoff --lane repoHF-7 "$(printf 'clear\nsecond line')":So the act exited 0, and this is worse than the issue described:
---line in the why would also end the first prompt early, because that is wherelane-start'shandoff_top_blockstops.append-linewrote two lines. The row flip right after it did not refuse the dirty register: itshandle_preexistingcommitted those two lines as a peer'spre-existingedit.lane-handoff's own note then said the line "was not appended".A carriage return was not refused anywhere on that path.
wc -ldoes not count one, soappend-line "LANDING — …\r…"was appended and committed with exit 0.#72 item 5: the deferred
lane-startcommand dropped--agent. Where no transcript uuid is knowable,lane-startprints the first act that will write the deferred records:$prog --no-launch $self_args.$self_argsis only the lane. Acodexlaunch never has a uuid here, so it always reaches that line, and the printed command dropped--agent codex. The person who ran it then hadlane-agentchoose the agent of the last PAUSED. ForrepoAG-4in the suite that isclaude. Measured atf87fd5a:lane-start: lane-start --no-launch repoAG 4.One policy across #85 and item 6, and why
The writers refuse before they write, a typed
whyis refused at the door, andsanitizeflattens as the second line of defence.append_text_linerefuses\nand\rbefore itsmktemp, so every caller has the gate. That coversappend-line,add-row,log's Rule 6 line and the rename's lines;append-lineproves its one line AFTER writing it, so alane-handoff --whycarrying a newline corrupts LANES.md and leaves the checkout dirty #85's own argument is one gate in the writer. Theappend-lineverb also refuses beforeacquire_lock. The function's gate is reached only afterhandle_preexisting, which can commit a peer's register edit, and a refusal that has committed something is a refusal that wrote. This is wherewrite_eventalready asks forlog.lane-handoff's why is refused right after the arguments are read, before the identity triple, the refresh and every write. This is the only point that covers all four destinations: two of them never pass throughsanitize, and the third (write_event) refused only after the handoff had been refreshed and committed, which is Ten hardenings Copilot rounds 4–6 raised on #47, filed rather than taken: the test lock's ownership, three signal/status gates, a deferred command that drops --agent, and the handoff skill catching up with its command #72 item 6's "handoff refreshed, record not written". It is also the rule this estate already keeps for every other free text that becomes one line:lane-end --reason,rename-lane's reason, andlog's free text.sanitizenow spells every line break;: CRLF, a lone CR or a lone LF each becomes one;. This isappend-lineproves its one line AFTER writing it, so alane-handoff --whycarrying a newline corrupts LANES.md and leaves the checkout dirty #85's first candidate and its "the caller spells the newline;so that the act still happens". Nothing reaches it with a break today. It is kept so that a later caller cannot turn a break into a refused write in the middle of the act.I chose refusing over flattening at the door because the person sees their own words refused with the reason and re-runs, and nothing is half-written. Silent flattening would also have worked; the trade is one re-run against the record differing from what was typed.
Note:
lane-handoffhas no--whyflag. The why is the positional[why …], and the tests drive it that way.What changed
lanes-edit.shappend_text_linerefuses CR/LF beforemktemp(exit 2, "Nothing was written").append-linearm refuses CR/LF before the lock (exit 2, "Nothing was written, and the lock was not taken").lane-handoffsanitizeflattens CRLF, CR and LF to;and still replaces—.lane-start$(quoted --agent "$AGENT")whereAGENT_EXPLICIT=1: an agent named on the command line, or a non-default one the record named.--no-launch <repo> <n>stays contiguous.--agent claude, the deferred--no-launchin the live window still takes the resume-by-id branch for that window's own transcript, which comes before theAGENT_EXPLICITnew-session branch.No doc quotes any message or flag I changed (checked
docs/README-lanes.md,skills/*/SKILL.md,commands/*.md).Functions and regions edited:
lanes-edit.sh: append_text_line(new guard at the top of the function, ≈L1679-1696)lanes-edit.sh: main dispatcher, append-line) arm(new guard after the usage check, ≈L13561-13571)lane-handoff: top level, right after the argument-parsing loop(new block ≈L296-313; thewhyaccumulation lines inside the loop are untouched)lane-handoff: sanitize(and its comment, ≈L2168-2186)lane-start: section 5b, the deferred-record note before if (( ! lane_sid_known ))(≈L3894-3910)tests/test_lane_helpers.sh: Amendment 17 section, after the repoHF-7 writer-validation block(≈L9474-9535)tests/test_lane_helpers.sh: section 4, lane-start --agent cases, after repoAG-1 and after repoAG-4(≈L9960-9968, ≈L10027-10039)Tests added, and fail-before / pass-after
In
tests/test_lane_helpers.sh:repoHF-7:append-linewith a newline is refused with exit 2. The register is byte-identical andgit status --porcelainis unchanged.append_text_line, lifted out of the helper the way thecut_to_linecases are, refuses a newline itself and leaves its file at its one line.lane-handoff --lane repoHF-7 "$(printf 'clear\nsecond line')"is refused with exit 2. The handoff, the object log and the register are byte-identical, no commit is made, and the checkout is as it was.sanitize, lifted into its own shell, turnsa\r\nb\rc\nd — eintoa; b; c; d; e.--agent:repoAG-1defers a first act carrying--agent codex.repoAG-4(recordagent claude, run--agent codex) defers exactlylane-start --no-launch repoAG 4 --agent codex.repoAG-10,LANE_START_SESSION_ID=0) defers--no-launch repoAG 10with no--agent.Proof. I did not run the full suite locally (box load, and the AGENTS.md ruling of 2026-09-16). I ran one trimmed copy of the suite: setup lines 1-971,
E/LOGD,write_record_a12, and the whole Amendment 17 section through section 4. I ran that same file against two exported trees,git archive f87fd5aand this branch's files, with everyLANES_*seam unset andTMPDIRin a scratch directory:The one failure in both runs is
…and a record written before Amendment 17 carries both EMPTY, which readsrepoSW-1, a fixture from a section the trim leaves out. The new assertions that pass in both trees are expected to:…the object log not written:write_eventalready refused.…and the checkout as it was: before the fix the damage was committed, not left dirty.repoAG-10negative.Also run locally:
bash -non all four files.python3 -m pytest tests/test_repo_hygiene.py(152 passed).testsjob is the suite of record for everything else.Remaining, not in this PR
append-lineproves its one line AFTER writing it, so alane-handoff --whycarrying a newline corrupts LANES.md and leaves the checkout dirty #85's skill half.skills/handoff/SKILL.mdstep 4 (≈L347, "sanitize the operator's words once") should flatten CR/LF too. That file is being edited by lane 4's writer for skills/handoff/SKILL.md: the skill supplies the uuid (R-A11-5) but has no preemptive refusal for a lane that has never had a session recorded #98-skills/handoff/SKILL.md:39 hard-codes lanes-edit.sh's path; every sibling artifact (including skills/restart/SKILL.md) resolves it PATH-first #100 and by Handoff skill: commit the handoff in the checkout it was published in, by its own path, and refuse foreign staged entries and in-progress operations (#197) #199/Make every shell block in the handoff skill parse, and stop calling /rename the only act there is (#78, #79) #200, so it waits for lane 4'sSKILL.mdPR to land. The skill'sappend-linecall (≈L552) already gets the new writer-side refusal.tests/run.sh's lock ownership) are done: they landed on main in tests/run.sh refuses to start when it cannot record its lock's owner, and its cleanup removes only a lock that is still its own (#72 items 1-2) #232 as19c2e25on 2026-10-18, so they no longer wait for Follow up #190's test roots: name a refused root's value, check the pin's commit, find paired feature worktrees, show redirected roots under -q, run the lane manual sections, and restate the installer-source test #195.SKILL.md≈L85) waits for lane 4'sSKILL.mdPR.log PAUSEDnot stopping the register writes, in the command and in the skill) wait for Brett's ruling.f87fd5a:lane-handoff's INT/TERM traps nowexit 130/exit 143(L1926-1927).publish-handoff --expect-digest, notcat > file./ctxis nowlane-handoff --restart,SKILL.md≈L684.Part of #85
Part of #72
🤖 Generated with Claude Code
Summary by Sourcery
Prevent multiline lane records from being written and retain the intended agent when deferring lane-start records.
Bug Fixes:
Enhancements:
Tests: