Repository navigation
Stop three wrapper reads that failed from reading as no answer: lane's window-name check asks the unpushed local row too, lanes' hidden-row probe reads its whole stream, and lane-end --retire-dormant refuses without its stderr capture - #210
Merged
Merged
Conversation
…138 items 6, 13, 15 (not yet run against main); left: run them on main to prove they fail, then fix lane go_live, lanes hidden-row probe and lane-end sweep capture, push, open PR, watch CI Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…row as well, and refuses when that read fails (#138 item 13) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…o SIGPIPE under pipefail no longer reads as no hidden row (#138 item 15) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…t be made (#138 item 6) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ng tests Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
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 23 hours by commenting |
Reviewer's GuideHardens three wrapper reads to fail closed: File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… that cannot be read back refuses (#138 item 6, delta review of fe6e38a) The one trap 'cleanup EXIT INT TERM' ran cleanup and then resumed the script, so a TERM or INT to the shell alone during the sweep's read deleted the capture file (LE_TMP), the resumed script read it as an empty note, and --yes went on to retire rows over a fact that was never read. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…t none Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…le capture, and rows before an exit 8 (#138 delta review) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…f, and list exits 8, 130 and 143 in the --help table (#138 item 6, delta review) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… file behind (#138 delta review) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…--retire-dormant and for --retire alike (#138 delta review) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This was referenced Oct 18, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
This was referenced Oct 18, 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.



Refs #138 (items 6, 13, 15)
The three wrapper items of #138, each a read the wrapper could not make and then took for an answer, plus the older gaps beside two of them and the regression a review found in the first round of this PR's own fix (below). Each is re-proved on main
f87fd5a(none was already fixed; main's commits since do not touch these functions), fixed fail-closed the way the issue describes, and has a proving test in the newtests/test_wrapper_hardening_138.py. The other items of #138 (lanes-edit.sh, andlane-startitems 3, 14, 16) are not touched.Item 13:
lane,go_live, the window-name collision checklane:849-855asked onlyregister-row, which reads the PUBLISHED row, before renaming a lane-shaped window. A lane whoseadd-rowor session stamp is committed here and not pushed answers 8 there, and its window was renamed out from under it.lanego_live, lines 851-863: when the published read answers 8, askregister-row-localtoo. A local row refuses (same message as before), 8 passes, any other status refuses with "could not be read from the published or the local register (exit n)". The local read of the target lane just above already did this.test_live_attach_refuses_a_window_named_for_a_row_not_yet_pushed,test_live_attach_fails_closed_when_the_local_row_cannot_be_read(local exits 1 and 2), andtest_live_attach_renames_a_window_no_register_row_names(both reads 8 renames and attaches, and asserts that both registers were asked; it fails on main because the local read is not made there). The control istest_live_attach_still_refuses_a_window_named_for_a_published_row.Item 15:
lanes, the empty listing's hidden-row probelanes:566pipedlanes --closedintocommand grep -q .underset -o pipefail. Past a pipe buffer of rowsgrep -qexits on the first line, the producer takes SIGPIPE, the pipeline answers 141,e_hiddenstays 0, and the listing said "no lane ... is recorded" about a repository with hidden rows.lanes, thecase "$rc"arm8), lines 562-586: the probe reads the whole output into a variable, so nothing closes on the producer, and keeps the producer's status.--closedread FAILED (exit 1, with or without rows) main also said "no lane of ... is recorded". The status now decides. A status of 0 or 8 is an answer: any output on either is hidden rows (an 8 that printed rows is a contradiction and fails towards showing them), and no output on either is none. Any other status prints "whether closed or dormant lanes are recorded could not be read (lanes-edit.sh lanes --closedexited n)" and claims nothing. The exit stays 8, since nothing is listed.test_empty_listing_says_closed_rows_are_hidden_however_many_there_are(1 row, and 40000 rows, ~5 MB, far past a pipe buffer),test_empty_listing_claims_nothing_when_the_hidden_row_read_fails(exits 1 and 2, with 0 and 3 rows),test_empty_listing_treats_rows_printed_before_an_exit_8_as_hidden, and the controltest_empty_listing_of_an_empty_repository_still_says_none_is_recorded.Item 6:
lane-end --retire-dormant, the stderr capturelane-end:711-719: whenmktempcould not make the capture file the listing ran with its stderr uncaptured,rd_errstayed empty, "session records could not be read" never reached the check that refuses on it, and the sweep went on (dry run and--yesalike).lane-end, the--retire-dormantblock, lines 723-745: refuse (exit 1, "Nothing was written.") when the capture file cannot be made; the uncapturedelsebranch is gone.printf '%s' "$rd_err" >&2, and where that write fails (a closed or full stderr)set -eleft before thermand thelane-end-sweep.*file stayed in$TMPDIR. The capture file is now assigned toLE_TMP, which the EXIT trap'scleanupremoves, straight from themktemp(LE_TMP="$(mktemp ...)"), so no signal can land between the file existing andcleanupknowing its name.lane-endhad one trap,trap cleanup EXIT INT TERM, andcleanupreturns rather than exits, so a TERM or INT to the lane-end shell alone resumed the script after deletingLE_TMP. The resumed script read the deleted capture as an empty note, skipped the unread-records refusal, and--yescalledretire-rowsover a fact that was never read. Now (lane-end548-557)cleanupis the EXIT trap only, and INT and TERMexitwith 130 and 143, so the EXIT trap removes the file once. Nothing inlane-endrelied on resuming after a signal (this is its onlytrap). The same trap also stops a signal from resuming the ordinary path, whoselane-objectsread isLE_TMPtoo.--helpprints (lane-end238-242) now says what exit 8 means ("nothing there": for--retire-dormant, no lane of that repository recorded or none of its rows DORMANT; for--retire <pid|uuid>, nothing of any kind live for the lane) and lists the 130 and 143 above, beside 0, 1 and 2.rd_erris now read withcat -- "$rd_errf"and a failed read-back is a refusal ("could not be read back ... Nothing was written."), not an empty note, which is the one note that never refuses.test_sweep_refuses_when_the_stderr_capture_cannot_be_made(dry run and--yes, a failingmktempshim on PATH, assertingretire-rowsis never called),test_sweep_leaves_no_capture_file_when_its_note_cannot_be_relayed(stderr is/dev/full; skipped where there is none),test_sweep_does_not_resume_after_a_signal_during_the_read(TERM and INT to the shell alone, mid-read, with the unread-records note present: exits 143 and 130, 0retire-rowscalls, no capture file; INT skips where SIGINT is ignored at entry),test_sweep_refuses_when_the_capture_cannot_be_read_back(acatshim failing on the capture file),test_sweep_leaves_no_capture_file_when_a_signal_lands_as_it_is_made(amktempshim that sleeps after making the file; TERM and INT to the shell alone leave nolane-end-sweep.*), plus controls that the unread-records refusal and the ordinary dry run and act still behave.Lines touched, for lane openRepoTools-5
lane: insidego_live, 851-863 only.lanes: 562-586.lane-end: 238-242 (the help table), 548-557 (the trap) and 723-745 (outside the fenced 200-215 and 1971-2143, and clear of thecase "$rd_err"arms below, which #230 touches). No other file besides the new test.Verification
OPENREPOTOOLS_CODE_ROOTpointing at agit archive f87fd5acopy (the first round's 18 tests): 12 failed, 6 passed. The six that pass are the controls: the published-row refusal, item 15's 1-row case and its empty repository, item 6's two unread-records refusals, and the ordinary dry run and act. With that round's fix: 18 passed.git archive fe6e38acopy, a fake helper that prints the unread note and sleeps 3 s, and a TERM or INT to the shell alone at 1.2 s: at fe6e38a exit 0 and 1retire-rowscall; with this round's fix exit 143 and 130 and 0 calls.a564b18), same file, under the shared lock, against thegit archive fe6e38acopy: 3 failed, 18 passed, 1 skipped. The three are the TERM case, the unreadable capture and rows printed before an exit 8; the skip is the INT case, because a background job starts with SIGINT ignored and no script can trap it, so INT was driven by hand (exit 130 on the fix, exit 0 and 1retire-rowscall at fe6e38a). On the fix: 21 passed, 1 skipped. The bash lint tests oftests/test_repo_hygiene.pyselected with-k: 100 passed.git archive a564b18copy with amktempshim that sleeps after making the file and a TERM or INT to the shell alone: at a564b18 one emptylane-end-sweep.*file is left behind (exit 143 and 130, 0retire-rowscalls); with this round's fix none is.ae1b37b, on the merge of mainf5c3bcd), under the shared lock with everyLANES_*andTMUX*unset:tests/test_wrapper_hardening_138.pyandtests/test_repo_hygiene.pytogether, 213 passed, 0 skipped (the INT cases included), at80f46b2and again atae1b37b, which only rewords the help table.testsjob is the suite of record; the full suite was not run locally.🤖 Generated with Claude Code
Summary by Sourcery
Harden lane, lanes, and lane-end wrappers so failed reads and interrupted operations cannot be mistaken for safe answers or proceed to unintended changes.
Bug Fixes:
Enhancements:
Tests: