Skip to content

The handoff names every inventory record that is not a whole schema-1 one, and a leg directory this account cannot search, or a dependency nested in one, is unreadable and never absent (fixes #255) - #271

Merged
brettheap merged 6 commits into
mainfrom
fix/255-unreadable-inventory-and-leg-pr
Oct 20, 2026

Conversation

@brettheap

@brettheap brettheap commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

A follow-up to #246 (T018 of feature 004, the triad lane tooling) that must land before the triad cutover (#186). It fixes the two sections of #255.

The handoff names every inventory record that is not a whole schema-1 one, and a leg directory this account cannot search is unreadable, never absent. Both gaps broke #246's own rule, Amendment 7(d): a read that failed is never an empty one.

These readers run only in an assembly, a checkout whose project.yaml declares a leg, so main is not exposed before the cutover.

What it fixes

  • Section 1, the handoff's tree inventory (lane-handoff, paired_inv and pi_bad):
    • Before: a lane-trees row counted as unreadable only where its path was empty. A record of another schema keeps its path, so it read as a record with an empty checkout, and the paired tree it names was polled as this lane's writer, with no NOT POLLED note.
    • Now: the row's schema, field 13, decides. A row whose schema is not 1 is named in the NOT POLLED note by its id, its schema and any path it keeps, the lane stays SWAPPING, and the row never reaches paired_inv. The lane-trees row format is untouched.
  • Section 2, a leg directory with no search permission:
    • Before: its .git could not be tested, and each reader's .git test failed there exactly as for a leg that is not checked out. The sweep listed it absent and exited 0. lane-reconcile gave no TREE row. The handoff said nothing and re-recorded the leg's tree under the assembly. lane-end ENDED the lane over the lane's unpushed commit in the leg.
    • Now, in one set of words ("its directory cannot be searched, so whether it has a .git of its own could not be tested, and git could not read it: <git's first line>"):
      • lane-worktrees' probe_leg asks lstat for <leg>/.git. Only ENOENT or ENOTDIR is a leg with no .git, and any other failure sets read_error, so the leg counts unreadable: the sweep's exit 3, lane-end's refusal, and the report's legs section.
      • lane-handoff and lanes-edit.sh's lane_reconcile take their unreadable-leg branch where the .git test fails and the leg directory fails -x. The handoff gets an UNREADABLE LEG line, a count and a withheld tree. The reconciliation gets a TREE row unreadable that requires recovery.
      • A leg directory that is not there at all is still absent.

What the fold-in adds. Lane 3's independent review found the same failure in a reader the issue does not name. After the cutover, code/upstream/openRepoShape is exactly this kind of dependency.

  • A dependency nested in an unsearchable directory (aae85a2, the review's finding 1):
    • Before: git lists a dependency nested in a leg as - (not initialized) and exits 0, not only where it was never initialized but also where it cannot search the directory: at mode 000, at read-without-search, or under a directory it cannot search. assembly_checkouts skipped every - line before it probed anything, so such a dependency got no row, the code leg read clean, the sweep exited 0, and lane-end ended the lane over the lane's unpushed commit in it.
    • Now: a - line is parsed as an initialized one is, and probed (probe_uninitialized). It becomes a row only where the read failed, an unreadable row, counted, in the words probe_leg already gives. A directory that is not there, or one that is empty and can be searched, is a dependency never initialized and still adds none.
  • A dependency cloned by hand (a16e534, i): a clone made into the directory of a deinitialized dependency is still listed -, so the sweep skipped it and lane-end ended the lane over the lane's unpushed commit in it (measured). It is now read like an initialized dependency: a row, unpushed there, counted, exit 3.
  • The describe-suffix trim (a16e534, ii): git prints a describe suffix only on a or + line, never on a - line. A dependency never initialized at upstream/dep (1) was read as upstream/dep, which made a second row for the initialized dependency beside it and left dep (1) unread. The trim now applies to the initialized branch only.
  • The review's two test gaps (c67f7a6):
    • MS6: the unknown-schema test now runs for schemas 2 and 10, so a rule that matched the schema by its first character reads 10 as a schema-1 record and fails.
    • MS5: a record that files a tree id and no path is named in the NOT POLLED note by its id and schema, its tree is no writer of the lane's, and nothing is filed over it. A record this account cannot read (<unreadable>, no path) is named the same way.
  • The docs (c67f7a6, a16e534): docs/README-lanes.md says what the sweep does with a dependency git lists as not initialized, and The handoff's tree inventory names a record it could not read whatever its schema, and a leg directory it cannot search is never read as absent (#246 follow-up, before the cutover) #255's own added text is reflowed where its insertion left ragged lines.

The branch. fix/255-unreadable-inventory-and-leg-pr is a16e534 on main 55aae16: 7 files, 674 insertions and 30 deletions.

Tests. There are 11 test_255_ functions: lane 2's four, and the fold-in's seven. Under Runs, six of the seven fail under a mutant of the code they hold. The seventh, the unreadable record, needs no #252.

  • Lane 2's four, each failing on 5dd0df0's tools and passing here:
  • The fold-in's seven:
    • tests/test_triad_lane_tooling.py: a nested dependency's directory at mode 000 and at 0600, and beneath a directory that cannot be searched, each holding the sweep and lane-end; a dependency never initialized, which adds no row; a dependency cloned by hand, read as unpushed; and a dependency at upstream/dep (1), read by that path;
    • tests/test_triad_lane_tooling_handoff.py: the tree id with no path, and the unreadable record.
  • Where they live: the issue asked for them beside T018's cases in tests/test_lane_helpers.sh. T018's fixtures live in the two Python modules, so the tests are there (the review's ruling).

The order with #252 (the review's NIT 5). <malformed> is #252's row, so the handoff names a truncated record only where lane 4's #252 is on main. The review asked for #252 first, and that is what happened: #252 merged as 00d52ae at 2026-10-19T18:14Z, and q6b is built on it. With this PR on today's main, <malformed> records are named (the review measured it with #252 and #255 together, under Review).

Follow-ups, not in this PR:

Runs

  • The record, q6b (55aa3db, branch lane3-queue/q6b-255-on-q5b: q5b dd2b9f0 + this head a16e534, on main 00d52ae, tree f5aa12a): tests.yml run 39302719561, pending. This is the run of record for the landing.
  • An early read, q3a (dce6ffb, branch lane3-queue/q3a-255-on-q2f: PR 2's prefix q2f, aeadabe, + lane 2's two commits alone, at e678c61; tree 47413c4; on main 55aae16): run 39289273676, green on every job, the shell suite included.
  • The fold-in writer's selection at a16e534: -k "test_255 or triad or lane_worktrees or repo_hygiene" gave 421 passed (logs in briefs/ort3-255-should1-work/ in lane 3's tree).
  • The fold-in's mutants: the writer ran seven and each fails a test_255_ test.
    • The finding-1 fix reverted (FX0), and two variants of it: every - line a row (FX1), and a - line asked of probe_leg alone (FX2).
    • The two later rulings reverted one at a time: the hand-cloned case (RV1) and the describe-suffix trim (RV2).
    • The reviewer's MS5 and MS6.

Review. Lane 3's independent reviewer gave LAND on 523b381 (briefs/rv-255-final-20261019.md in lane 3's tree, posted on #255 as 6272494888). Both fixes do what sections 1 and 2 ask, in every reader named.

Landing: Brett Heap's decision 4 on #228 ("Yes, merge when green (Recommended)"), and his word of 2026-10-18T20:3xZ, "use this as approval to merge when green for all merges thru v1.1.0".

Lane: openRepoTools-3

🤖 Generated with Claude Code

Summary by Sourcery

Prevent failed reads from being treated as empty state during lane sweeps and handoffs, preserving safety for unreadable legs, dependencies, and inventory records.

Bug Fixes:

  • Treat leg directories and nested dependencies that cannot be searched or read as unreadable rather than absent or clean, preventing unsafe lane completion and handoff updates.
  • Identify every inventory record that is not a complete schema-1 record in handoff output and exclude its associated tree from polling.
  • Handle manually cloned and path-suffixed uninitialized dependencies correctly while continuing to ignore genuinely empty or missing dependencies.

Enhancements:

  • Expand lane documentation to describe unreadable legs, dependency handling, and non-schema-1 inventory records.

Documentation:

  • Document the updated sweep, handoff, reconciliation, and dependency behavior for unreadable paths and malformed inventory records.

Tests:

  • Add coverage for unreadable legs and nested dependencies, manually cloned dependencies, unusual dependency paths, and malformed or unreadable inventory records.

Brett Heap and others added 5 commits October 19, 2026 16:46
… readers, and the handoff names every inventory record that is not a whole schema-1 one (#255)

Both gaps broke #246's own rule, Amendment 7(d): a read that failed is
never an empty one. Both readers run only in an assembly (a checkout
whose project.yaml declares a leg), so main is not exposed before the
triad cutover (#186).

1. A leg directory with no search permission (openRepoShape-2's SHOULD
   on #246, comment 6253899644; SHOULD 1 of the final review 6266578777).
   Its `.git` could not be tested, and each reader's `.git` test failed
   there exactly as for a leg that is not checked out: the sweep listed
   it `absent` and exited 0, lane-reconcile gave no TREE row, the handoff
   said nothing and re-recorded the leg's tree under the assembly, and
   lane-end ENDED the lane over the lane's unpushed commit in the leg.
   Each reader now routes it through the path T018 built for a leg git
   cannot read, in one set of words ("its directory cannot be searched,
   so whether it has a .git of its own could not be tested, and git could
   not read it: <git's first line>"):
   - lane-worktrees `probe_leg` asks lstat for `<leg>/.git`; only ENOENT
     or ENOTDIR is a leg with no `.git`, and any other failure sets
     `read_error`, so `decide_leg` counts it `unreadable` (the sweep's
     exit 3, lane-end's refusal, and the report's legs section). This is
     lane openRepoTools-3's partial patch, reviewed and kept as it was.
   - lane-handoff and lanes-edit.sh `lane_reconcile` take their
     unreadable-leg branch where the `.git` test fails and the leg
     directory fails `-x`: an UNREADABLE LEG line, a count and a withheld
     tree in the handoff, and a TREE row `unreadable` that requires
     recovery in the reconciliation.
   A leg directory that is not there at all is still `absent`.

2. The handoff's inventory read (lane-handoff, `paired_inv` / `pi_bad`)
   counted a `lane-trees` row as unreadable only where its path was
   empty. A record of another schema keeps its path (`${ltl_s:-<none>}`),
   and so will #252's `<malformed>` row, so either was read as a record
   with an empty checkout: the paired tree it names was polled as this
   lane's writer, with no NOT POLLED note. The row's schema, field 13,
   now decides, as lane-worktrees' `_discover` does: a row whose schema is
   not `1` is named in the NOT POLLED note by its id, its schema and any
   path it keeps, the lane stays SWAPPING, and it never reaches
   `paired_inv`. The lane-trees row format is untouched.

Tests (each fails on 5dd0df0's tools, then passes here):
- tests/test_triad_lane_tooling.py:
  test_255_a_leg_directory_that_cannot_be_searched_is_unreadable_and_holds_lane_end
- tests/test_triad_lane_tooling_handoff.py:
  test_255_a_leg_directory_that_cannot_be_searched_is_unreadable_in_the_handoff_and_reconcile
  test_255_an_unknown_schema_record_is_named_in_the_handoffs_not_polled_note
  test_255_a_malformed_record_is_named_in_the_handoffs_not_polled_note
  (the `<malformed>` row comes from a fake lanes-edit.sh, so it does not
  wait for #252)

Lane: openRepoTools-2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit ba13d21)
…66, and lane-handoff's T018 section is 3b (#255)

- docs/README-lanes.md (T018 section): a leg directory this account
  cannot search is `unreadable` in the sweep, the handoff and the
  reconciliation, a leg directory that is not there at all is `absent`,
  and an inventory record that is not a whole schema-1 one is named in
  the handoff's NOT POLLED note.
- docs/triad-lane-tooling-analysis.md §7 item 11: "workBenches issue: to
  be linked" now names opensoft/openBenches#166 (NIT 4 of #246's final
  review, 6266578777). One line.
- lane-handoff: T018's section heading is `3b`, so main's "The rename is
  section 3a below" (#220) finds the one section numbered 3a once #246
  merges (NIT 3 of the same review). One line, a comment.

Lane: openRepoTools-2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 523b381)
…rectory cannot be searched, so the sweep and lane-end hold over its unpushed commit instead of ending the lane (#255)

git lists a dependency nested in a leg as `-` (not initialized) and exits 0
not only where it was never initialized but also where it cannot search its
directory: at mode 000, at read-without-search, or under a directory it
cannot search. assembly_checkouts skipped every `-` line before it probed
anything, so such a dependency got no row, the code leg read clean, the
sweep exited 0, and lane-end ended the lane over the lane's unpushed commit
in it (lane 3's review of #255, finding 1; not a regression, and outside the
three readers the issue names, but after the cutover code/upstream/
openRepoShape is exactly this kind of dependency).

A `-` line is now parsed as an initialized one is, and probed
(probe_uninitialized): it is stat'ed first, because Leg.exists is
os.path.isdir, which says False for a path it cannot reach exactly as for
one that is not there, and then probe_leg's own lstat of its .git decides.
It is appended ONLY where read_error is set, so it is an `unreadable` row,
counted, in the words probe_leg already gives a leg directory it cannot
search; a directory that is not there, or one that is empty and can be
searched, is a dependency never initialized and still adds none. A `-` line
whose directory holds a checkout git can read is left as it was: no row.
The initialized branch and every declared leg are unchanged. The docstring
of assembly_checkouts now says which `-` lines are read.

tests/test_triad_lane_tooling.py, beside the leg-directory test:
 - a nested dependency holding the lane's unpushed commit, its directory at
   mode 000 and at 0600: an `unreadable` row, the sweep exits 3, lane-end
   refuses and the lane is not ended; searchable again it is `unpushed`;
 - the same with the dependency's parent (code/upstream) unsearchable and
   the dependency left as it is: the sweep and lane-end hold, either as an
   `unreadable` row or, where git submodule status itself fails, as the
   existing "could not be listed" refusal;
 - a dependency never initialized, its directory empty and searchable or
   not there at all: no row, the sweep exits 0 and lane-end passes.

Lane: openRepoTools-3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…nd README-lanes says what the sweep does with a nested dependency git lists as not initialized (#255)

tests/test_triad_lane_tooling_handoff.py (lane 3's review of #255, finding 6):
 - the unknown-schema test runs for schema 2 and for schema 10, so a rule
   that matched the schema by its first character (`10` begins with `1`)
   reads the record as a whole schema-1 one and fails it;
 - a record that files a tree id and no path is named in the NOT POLLED
   note by its id and its schema, its tree is no writer of the lane's, and
   nothing is filed over it. The real lanes-edit.sh on this tree prints it
   as a whole schema-1 row with an empty path - the row only pi_bad's
   `$2 == ""` arm names - and as `<malformed>` once #252 is in, so the case
   takes the schema from the real row and holds both ways;
 - a record file this account cannot read (`<unreadable>`, no path) is named
   the same way. It needs no #252: it passes on this tree.
 The assertion helper takes the path a record keeps, `""` for none.

docs/README-lanes.md:
 - the sweep's leg bullet says what the code does for a nested dependency
   git lists as not initialized: no row where its directory is missing, or
   empty and searchable; an `unreadable`, counted row where its directory,
   or one above it, cannot be searched (finding 1);
 - #255's own added text is reflowed where its insertion left ragged lines
   (the sweep bullet's "the assembly / homes", the one 141-column prose line
   in lane-handoff's section, and the reconciliation's `TREE` row sentence).
   No other text changes.

Lane: openRepoTools-3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…kout git can read stands in its directory, and its path on that line is not trimmed of a describe suffix git never prints there (#255)

Lane 3's rulings on the round's report, both measured and both the same
failure class as the unsearchable directory.

(i) A `-` line is also a row where probe_uninitialized leaves `checkout` set:
a clone made by hand into the directory of a deinitialized dependency. git
does not count it initialized and still lists it as `-`, so the sweep skipped
it, exited 0 and lane-end ended the lane over this lane's unpushed commit in
it (measured: the handcloned-dependency probe of the previous round). It is
read like an initialized dependency now - a row from read_leg, `unpushed`
here, counted, exit 3. A missing directory and an empty searchable one still
add none, and an unreadable one is still `unreadable`.

(ii) The ` (...)` trim applies to the initialized branch only. git's
print_status prints a describe suffix only on a ` ` or `+` line, never on a
`-` line, so a dependency never initialized at `upstream/dep (1)` was read
as `upstream/dep` - a second row for the initialized dependency beside it,
with `dep (1)` unread. The initialized branch's trim is unchanged.

tests/test_triad_lane_tooling.py, beside the never-initialized test (which
still passes):
 - a deinitialized dependency with a hand clone holding the lane's unpushed
   commit: a row (`unpushed`, branch in-dep), the sweep exits 3, lane-end
   refuses and the lane is not ended;
 - a dependency never initialized at `upstream/dep (1)`: no second row for
   `upstream/dep` while its directory is empty and searchable, and the
   `unreadable` row for `dep (1)` itself at mode 000.

docs/README-lanes.md: the sweep bullet says a `-` dependency directory
holding a checkout git can read is read like an initialized one.

Lane: openRepoTools-3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Oct 19, 2026

Copy link
Copy Markdown

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 2 days by commenting @sourcery-ai review. Upgrade to get a review now.

@sourcery-ai

sourcery-ai Bot commented Oct 19, 2026

Copy link
Copy Markdown

Reviewer's Guide

The PR closes #255 by making permission and inventory-read failures explicit and fail-safe across all lane readers, while distinguishing genuinely absent or uninitialized paths from unreadable checkouts and adding focused regression tests and documentation.

Sequence diagram for unreadable dependency handling

sequenceDiagram
    participant Sweep as lane-worktrees
    participant Git as git
    participant Probe as probe_uninitialized
    participant LaneEnd as lane-end
    Sweep->>Git: list dependencies
    Git-->>Sweep: dependency status line
    alt dependency line is -
        Sweep->>Probe: probe_uninitialized
        Probe->>Git: read dependency path
        Git-->>Probe: read failure
        Probe-->>Sweep: unreadable
        Sweep-->>LaneEnd: exit 3
        LaneEnd->>LaneEnd: refuse lane end
    else dependency is missing or empty and searchable
        Sweep-->>LaneEnd: no dependency row
    else dependency contains a readable hand clone
        Sweep-->>LaneEnd: unpushed row
    end
Loading

Flow diagram for unreadable leg classification

flowchart TD
    A[probe_leg] --> B{Leg directory exists?}
    B -->|No| C[absent]
    B -->|Yes| D{Can lstat leg/.git?}
    D -->|ENOENT or ENOTDIR| E[No .git: continue normal probing]
    D -->|Other failure| F[read_error]
    D -->|Readable| E
    E --> G{Can search leg directory?}
    G -->|No| F
    G -->|Yes| H[Read leg state]
    F --> I[unreadable]
    I --> J[lane-worktrees exit 3]
    I --> K[lane-end refuses]
    I --> L[lane_reconcile emits TREE unreadable]
    I --> M[lane-handoff emits UNREADABLE LEG]
Loading

Flow diagram for non-schema-1 inventory records

flowchart TD
    A[lane-trees] --> B[Read record schema field 13]
    B --> C{Schema is exactly 1?}
    C -->|No| D[Name record in NOT POLLED note]
    D --> E[Include id, schema, and retained path]
    D --> F[Keep lane SWAPPING]
    D --> G[Do not pass record to paired_inv]
    G --> H[Do not poll its paired tree as this lane's writer]
    C -->|Yes| I[Pass record to paired_inv]
    I --> J[Normal paired-tree inventory handling]
Loading

File-Level Changes

Change Details Files
Classify unreadable legs and nested dependencies as unreadable rather than absent or clean, and propagate that status through sweep, reconciliation, handoff, reporting, and lane-end safety gates.
  • Probe .git with distinct handling for missing paths versus permission or read failures.
  • Inspect - submodule entries so inaccessible directories and hand-cloned repositories are reported, while genuinely uninitialized dependencies remain absent.
  • Preserve and report the shared unreadable-leg wording, recovery requirements, counts, and refusal to end affected lanes.
  • Document the updated leg and dependency semantics.
lane-worktrees
lanes-edit.sh
docs/README-lanes.md
Make lane handoff inventory parsing reject every record that is not a complete schema-1 record, preventing unknown, malformed, pathless, or unreadable records from being polled as writers.
  • Use the complete schema field to classify inventory rows, including schemas 2 and 10 without prefix matching.
  • Name each rejected record by id, schema, and retained path where applicable in the NOT POLLED note.
  • Keep the lane SWAPPING, avoid overwriting records, and exclude trees named only by rejected records from this lane’s writers.
  • Add coverage for malformed, unknown-schema, pathless, and unreadable inventory records.
lane-handoff
tests/test_triad_lane_tooling_handoff.py
Add regression coverage for permission failures, dependency discovery edge cases, and handoff/reconciliation behavior.
  • Test unreadable declared legs and nested dependencies at multiple permission modes, including inaccessible parents.
  • Verify absent and empty searchable dependencies remain uninitialized and uncounted.
  • Verify hand-cloned dependencies are scanned and classified as unpushed.
  • Preserve dependency paths containing parenthesized suffixes and validate lane-end refusal and exit statuses.
tests/test_triad_lane_tooling.py
tests/test_triad_lane_tooling_handoff.py
Record the associated tooling analysis and user-facing behavior changes.
  • Link the completed workBenches issue in the triad analysis.
  • Describe unreadable and not-initialized dependency semantics and handoff safeguards in the lane documentation.
docs/triad-lane-tooling-analysis.md
docs/README-lanes.md

Possibly linked issues


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

@brettheap

Copy link
Copy Markdown
Contributor Author

Lane openRepoTools-3, 2026-10-19T21:0xZ: the independent re-check of #271's fold-in round, e678c61..a16e534, is LAND. There is no MUST and no SHOULD, and four NITs, all left for v1.2.0.

Finding 1's failure is gone. In every case the setup is this lane's unpushed commit in code/upstream/dep. The probes ran with the real tools, on main 00d52ae + a16e534 and on a16e534 alone:

Case Result
the dependency's directory at mode 000 or 0600 unreadable retire, the sweep exits 3, lane-end exits 2 "NOT ended" (at e678c61: exit 0, and it ENDED)
code/upstream at 000 or 0600 git still prints - and exits 0. The row is unreadable ("a directory above it cannot be searched…"), the sweep exits 3, lane-end exits 2
a hand clone on a - line unpushed retire on branch in-dep, the sweep exits 3, lane-end exits 2

Nothing else changes behaviour:

The mutants:

The NITs, all going to v1.2.0:

  1. NIT 8 is confirmed on 00d52ae, and it is old code.
    • What happens: over a malformed own-root record, set-lane-tree refuses with exit 1 and says "its sidecar … is not a record this helper can read" (lanes-edit.sh:18313). lane-handoff:2123 drops that message and prints the exit-7 fence wording.
    • Weight: it fails safe.
  2. N8's wording is untested (tests/test_triad_lane_tooling.py:777).
  3. The parent test accepts exit 2 or exit 3 (:772). On a git that fails git submodule status under an unsearchable parent, two mutants would survive. Git 2.43 prints - here, so the test bites on Eagle.
  4. MS5 is equivalent on the landing tree.

The landing: #271 lands last in lane 3's queue, on prefix q6c (f4af690, tree ca46e88, run 39322404772). That is the main #264 and #266 leave, plus #228's series.

Lane: openRepoTools-3

…ueue prefix q6k-255-on-q5k (#255)

The tree is q6k-255-on-q5k's (1149aeb), which tests.yml run 39373762988 tested green on lane3-queue/q6k-255-on-q5k.

Lane: openRepoTools-3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@brettheap
brettheap merged commit f18b818 into main Oct 20, 2026
6 checks passed
@brettheap
brettheap deleted the fix/255-unreadable-inventory-and-leg-pr branch October 20, 2026 06:38
brettheap added a commit that referenced this pull request Oct 20, 2026
…runs the fetch, pipx, approval, tag, ref, clean, stamp, dist and digest steps instead of reading them, and release.yml changes only in comments (#228, PR 3b) (#284)

Part of #228: **pull request 3b** of feature `006-release-pypi`, stacked on #268 (PR 3, head `9682f0b`). It is the fix round for lane openRepoTools-2's verdict on PR 3 (#228 6273189381) and its re-check (6277890612, 6278141437). Those mutants of `release.yml` survived at `9682f0b` because the steps they changed were read as text, not run.

**Nothing changes in what the workflow does.** `release.yml` changes in 9 comment lines only, and `spec.md` in 10. `tests/test_release_workflow.py` grows from 66 test functions to 79. The diff against `9682f0b` is 3 files, 1023 insertions and 90 deletions. No tag is pushed and nothing is dispatched (plan.md gate G8).

**The nine commits**
- `4daaeda` (SH2, mutants I11 and I12): the install check's fetch step is run on a tag with `curl` faked.
  - It passes the one file TestPyPI's JSON lists under the wheel's name, at the recorded digest, over https from test-files.pythonhosted.org.
  - It refuses before fetching anything: another host, plain http, a file listed twice, another digest, another file at the digest, and nothing listed.
  - The pipx step is run with pipx faked. It passes only when pipx's bin directory holds the one link `openRepoTools`.
- `2f274c4` (SH3, mutants E2 to E4): a fake `gh` answers the forge's REST reads. Under it are run:
  - the build's environments step;
  - each upload job's approval re-check;
  - each upload job's tag re-check, with the tag moved, deleted, made lightweight, or naming a tag.
- `9762479` (SH5, mutants RF1 and RF2): the ref step is run on tags that a forge fake and a `file://` origin hold, so only the step's own comparisons can refuse.
- `2a83a61` (SH7, mutants W9 to W12): one synthetic wheel each, made from HEAD's blobs:
  - METADATA naming a dependency;
  - a second console script;
  - a sixth `.dist-info` file;
  - no `licenses/LICENSE`.

  Each refusal must name what is wrong.
- `0cf8142` (NITs S7, S8, T5, BLD1, H5 and N5): a case for each:
  - a TAG of another version;
  - a read-back one character short;
  - a forge that ignores `head_sha=`;
  - the build step run with the backend faked;
  - the "STAYS PUBLIC" bullet;
  - every job's whole step list, pinned in order (`STEP_ORDER`).
- `76dd73c` (SH8, mutant N3): the clean step is run. The test file's header lists which decisive steps are run and which are still read as text.
- `5ccd66e`: the doc NITs, in comments only.
  - One spelling for the rehearsal's row.
  - FR-029's additions are named in the header and in FR-028.
  - Step 1's comment is made true.
  - US5's tenth scenario is numbered.
- `7376b34` (SH6, S5 and S5b, and NITs 1 and 3 to 6 of the PR 3 re-check):
  - the stamp step is run whole with `refs/heads/release/v1.1` and with `refs/tags/v1.1.0`, and both must stamp `TAG v1.1.0`;
  - each upload job's dist step is run whole, and so is the install check's digest step, on five cases each;
  - the fake index polls every 0.01 s;
  - `PYTHONDONTWRITEBYTECODE=1` is set in `run_index_step`;
  - a served-check case's first read is a connection closed with no response.
- `2ff7094` (the fix-up for lane 2's re-check, #228 6284132726: its N1, its question 1 and its NIT 3):
  - the test file's header names the dist step among the steps run, and says six are read only as text. Two more header sentences, untrue at `7376b34`, are corrected: how the build's record of the wheel is held, and which steps read `GITHUB_SHA` and `GITHUB_REPOSITORY`;
  - `STEP_ORDER` finds the build's guard by `guard_step()`, each upload job's guard as its first step, and the macOS and 3.9 legs by `condition_kind()`, as HOW A STEP IS FOUND has it. The count and order are unchanged;
  - W9's naming check reads only a refusal's `::error::` line.

**`7376b34`'s subject says "WIP … written and not yet run".** It was pushed at Brett Heap's account swap, before any test ran. Every result below was then taken at that exact head, on a clean tree, with nothing changed. Its message cannot be corrected without a force-push, so the squash at landing carries an accurate one.

**Results at `7376b34`**
- **Mutants:** all 37 run are killed. They ran in four batches, and each kill is credited to a test that runs only that mutant's step. The 37 are:
  - I11 and I12;
  - all 17 of group 4's survivors;
  - I3, I10, I13, I14, I17, I18, X5, X6, G7 and S4;
  - seven new ones: S5b, X8 to X12, and I20.
- `-k test_release_workflow`: 499 passed.
- `-k "test_release_ or test_tests_yml_ or repo_hygiene"`: 733 passed. That is lane 2's 646 at `9682f0b`, plus 87 new tests.
- actionlint 1.7.12 is clean.
- `git diff 9682f0b 7376b34 -- .github/workflows/release.yml` changes comment lines only.

**Results at `2ff7094`.** `-k test_release_workflow` gives 499 passed, 0 failed and 0 skipped. A step inserted after the stamp writer is killed by the pin. The macOS rerun or the build's guard, renamed, passes it. W9's refusal without its `::error::` naming line is killed. `release.yml` is byte-equal to `7376b34`.

**Review.** Lane openRepoTools-2's re-check of `9682f0b..7376b34` is LAND, with no MUST and no SHOULD (#228 6284132726): all 113 mutants are killed. Its re-check of the fix-up, `7376b34..2ff7094`, asked for in 6284529659, is LAND (#228 6288385122).

**CI record.** `lane3-queue/q7k2-228pr3b-on-q6k` (`4d6581d`, tree `7ad2bbe`) puts this PR on the main that lane 3's queue k leaves: main `677acc0`, then #262 and #267 to #271. Its `tests.yml` run 39378128111 is green.

**Left for v1.2.0** (lane 2's NITs, not taken here, filed on Brett Heap's word of 2026-10-20T09:40Z): WX1, the wheel check's bash line (#285); a `URLError` without a status (#286); the `--depth` pin on the ref step's tag fetch (#287); and a test that runs the stamp step followed by an inserted step (#288).

**Landing.** It lands after #271 in lane 3's queue, on Brett Heap's word of 2026-10-18T20:37:07Z: "use this as approval to merge when green for all merges thru v1.1.0". When #268 lands, the landing retargets this PR to main.

Lane: openRepoTools-3

Landed by lane openRepoTools-3 as queue prefix q7k2 (tree 7ad2bbe, tests.yml run 39378128111 green), on Brett Heap's word of 2026-10-18T20:37:07Z: "use this as approval to merge when green for all merges thru v1.1.0". Lane openRepoTools-2's re-check of 9682f0b..7376b34: LAND (#228 6284132726); its re-check of the fix-up 7376b34..2ff7094: LAND (#228 6288385122). 7376b34's subject says WIP because it was pushed at an account swap before its tests ran; every result above was taken afterwards, at 7376b34 or at 2ff7094.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap pushed a commit that referenced this pull request Oct 20, 2026
…271) into act 2's slice 1a (#198)

One conflict, in lanes-edit.sh's lane_state_follow, at the check of a
snapshot this helper cannot read: this branch releases the workstation's
sidecar lock (sidecar_release); main released W's mutex where the follow
took it (lsf_own) and gave a new note naming $LSS_WHY. Resolved as the PR
says: keep sidecar_release, take main's note. No line of main's that merged
on its own uses lsf_own or release_lock inside lane_state_follow, and main's
lanes-edit.sh changes since 55aae16 add no lock call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap pushed a commit that referenced this pull request Oct 20, 2026
Main moved from 677acc0 to 90eed74: the #228 release series (the
openrepotools package, the package job in tests.yml, the release
workflow and its docs) and #271 (#255: the handoff names every inventory
record that is not a whole schema-1 one, and a leg directory this
account cannot search is unreadable, never absent). The merge is
textually clean. Of this branch's files, #271 changes lanes-edit.sh only
in lane_reconcile's unreadable-leg branch and docs/README-lanes.md only
in the triad section's leg, handoff and reconcile rows; this branch
changes the register's rewrites, retire-rows, the archive scan and
add-row's after-rebase take-back, and the README's write-failure,
sweep and archive rows. Neither reads or writes the other's state, and
no refusal code or README row both touch now contradicts the other.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap pushed a commit that referenced this pull request Oct 20, 2026
Main moved from this branch's base 21e0829 by nine commits: lane-handoff's
lifecycle read (677acc0), #228's release series (c82578f to 234b670), #271
(f18b818), #284 (90eed74) and #265 (a42946e). Of this PR's files, only
lanes-edit.sh (lane_reconcile's leg sweep) and docs/README-lanes.md (the
triad section) changed there, away from its hunks, and the merge is clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap pushed a commit that referenced this pull request Oct 21, 2026
…rea, and add the lines for #263, #289, #294 and #296, the changes after a42946e (#228)

`docs/release-notes/v1.1.0.md` takes the fixes of lane 3's review of d8a349d (#293 comments 6314185715 and 6314529807, the second of which replaces three of the corrected texts of the first) and the lines for the changes after a42946e.

The review's fixes:
- Line 45 (#255, #271) no longer says that `lane-reconcile` and `lane-handoff` read a dependency nested in a leg: only the sweep and `lane-end` do. It says that a leg directory this account cannot search is unreadable and never `absent`, that a nested dependency is unreadable to the sweep and `lane-end` too, and that the sweep still reads a declared leg beneath such a directory as `absent` (#272, open). It also says NOT POLLED where the tool does.
- Line 46 (#280, #283): an `awk` exits, it does not return, and the sentence on a status of 0 or 8 is gone.
- Line 61 (#214): the fence never checks whose a local commit is, only that it adds or changes a register or handoff file.
- The `<other-changes>` item counts 44 of the 53, because the openRepoShape pin's line (#233) is in the list, and the other 9, not 43 and 10. The milestone item names #247 and lists the six entries of 2026-10-20.
- #209 is not tests-only: its `-e` fixes `lanes`' stderr filter under macOS's BSD grep, so it moves from the test suite to the lane area, and its one line there carries its test changes too. It is cited once.

The lines for the changes after a42946e, each in its area and worded from its squash commit and description: #263 (d9065df) and #289 (fb2cf36) under the register and `lanes-edit.sh`, #294 (acc89ea, which closes #292) under `lane`, `lane-start`, `lane-end` and the name guard, and #296, which pins every Ubuntu runner to ubuntu-24.04, under the test suite and its checks. #296 has not landed yet: lane 3 lands it right before #293. The `<other-changes>` item says the lines were added on 2026-10-21 and are not in its counts, and the placeholder stays for #293 itself.

Not touched: lane 3's optional sentence on #271 and its NIT on the tag.

Lane: openRepoTools-2
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
brettheap added a commit that referenced this pull request Oct 21, 2026
… 1.1.0 and list, by area and from the squash titles of aaa7bc8..a42946e, what else changed on main since v1.0.0 (#228) (#293)

Part of #228. It brings `docs/release-notes/v1.1.0.md` up to date on main: first on `a42946e`, as lane 3 asked on Brett Heap's word (#228 comment 6303608345, the section for lane openRepoTools-2, task 1), then with lane 3's review of `d8a349d` (comments 6314185715 and 6314529807 on this pull request), with the lines for the three changes that landed after `a42946e`, and with the line for #296, which landed right before this pull request. It is one file, changed in two commits (`d8a349d` and `bebb77d`), 59 insertions and 4 deletions against main.

Lands last before rehearsal D's freeze; lane 3 reviews and merges it (#228 6303608345)

**What changed**
- **Lane 3's review is applied,** in the second commit:
  - **The line on #255 and #271** no longer says that `lane-reconcile` and `lane-handoff` read a dependency nested in a leg, since only the sweep and `lane-end` do. It says that a leg directory this account cannot search is unreadable and never `absent`; that a dependency nested in a leg whose directory, or one above it, it cannot search is unreadable to the sweep and `lane-end` too; and that one known gap remains, #272, where the sweep reads a declared leg beneath such a directory as `absent`, so `lane-end` does not hold on it. It also says NOT POLLED where the tool prints it, and it says that the item closes two gaps in #246, where it said "both gaps" with nothing before it to refer to.
  - **The line on #280 and #283:** an `awk` exits and does not "return", and the sentence on a status of 0 or 8 is gone, because a reader of the notes cannot act on it.
  - **The line on #214:** the fence passes a local commit only when it adds or changes a register or handoff file, and never checks whose it is, so the line no longer says "the lane tooling's own".
  - **The counts in the `<other-changes>` item:** the pin's line (#233) is in the list, so the list carries 44 of the 53, and the other 9 have their lines under "What 1.1.0 adds", not 43 and 10. The item says "the openRepoShape pin" where the runner pin of #296 could be taken for it.
  - **The milestone item** names #247, so it lists the six entries the milestone held on 2026-10-20.
  - **#209 is not tests-only.** Its `-e` fixes `lanes`' stderr filter under macOS's BSD `grep`, so it moves from the test suite to the lane area, after #208's, and its one line there also carries its test changes: the `repoG#8` assertion and the count of bash files in prose. It is cited once.
- **Lines for the changes after `a42946e`:** #263 (`d9065df`), #289 (`fb2cf36`) and #294 (`acc89ea`, which closes #292) landed on 2026-10-21, from 11:28Z to 11:30Z. Each has a line in its area, worded from its squash commit and its description: #263 and #289 under "The register and `lanes-edit.sh`", and #294 under "`lane`, `lane-start`, `lane-end`, the name guard and their helpers".
  - **#296 has its line too,** last under "The test suite and its checks": CI and the release workflow run on `ubuntu-24.04`, because `ubuntu-latest` is now Ubuntu 26.04, and `macos-latest` is unchanged. It landed right before this pull request, as `5afba4a`.
- **"What 1.1.0 adds" gains two lines,** after the annotated-tags item. They are for the three entries that closed in the v1.1.0 milestone after the draft was written:
  - **#255,** fixed by #271: the lane tools name what they could not read in a triad's legs. `lane-handoff` names every tree-inventory record that is not a whole schema-1 one, and a leg directory this account cannot search is unreadable and never `absent`. Only the sweep and `lane-end` also see a dependency nested in such a leg, since `lane-reconcile` and `lane-handoff` read declared legs only, and #272 stays open as the known gap.
  - **#280,** fixed by #283: `lane-handoff` reads the lane's lifecycle whole and parses it afterwards, so an `awk` that exits early can no longer turn `lane-state`'s SIGPIPE into a lifecycle that could not be read.
  - #283 is #280's pull request, so the three numbers make two lines. Each line cites the issue and its pull request, as the hook-pair line does for #237 and #247.
- **"What else changed since v1.0.0" is drafted** from `git log aaa7bc8..a42946e`, which is 53 squash commits, one pull request each and no merge commit. It is grouped by area, one line each, and each line ends with its pull request. It lists 44 of the 53: the pin's line (#233) at its head and 43 by area, as 4 on the estate commands, 10 on the register and `lanes-edit.sh`, 11 on `lane`, `lane-start`, `lane-end` and the name guard, 6 on the handoff and its skill, 1 on the lane tools in a triad's legs (#246), 9 on the test suite and its checks, and 2 on the triad migration's records.
  - **The other 9 are accounted for elsewhere:** the package series of #228 (#262, #267, #268, #269, #270 and #284) is covered by #228's lines, and #247, #271 and #283 have the lines above.
  - **The lines added on 2026-10-21** are for the four changes after `a42946e`: #263 and #289 on the register and `lanes-edit.sh`, #294 on `lane`, `lane-start`, `lane-end` and the name guard, and #296 on the test suite and its checks. The areas now hold 47 lines, one for each of 47 pull requests, and #233's line at the head of the list makes 48.
  - **The list is redone at the tag.** It is drafted from the squash titles only.
- **"Before the tag" is updated in three items:**
  - `<pr-count>` gives the count at `a42946e`, which is 53;
  - `<other-changes>` says what was drafted, which 9 pull requests have their lines under "What 1.1.0 adds", that the lines for #263, #289, #294 and #296 were added on 2026-10-21, and what is left to do at the tag; the placeholder's own line now stands for the changes after `a42946e` that have no line above;
  - the milestone item names the six entries the milestone held on 2026-10-20: #228 still open, #237 closed by its pull request #247, #255 closed, and #280 closed by its pull request #283.

**Not added:** nothing that has landed. #296 landed right before this pull request and has its line. This pull request itself, and whatever lands after it, is what the `<other-changes>` placeholder stands for. None of #263, #289, #294, #296 and #292 is in the v1.1.0 milestone, which still holds the six entries of 2026-10-20.

**How this keeps the draft's rules**
- **One-line paragraphs:** every added line is its own list item, and none is wrapped.
- **References:** every reference above "Before the tag" is spelled `opensoft/openRepoTools#N`, with no bare `#N`, since the notes are published from another repository after the migration.
- **Placeholders:** `<other-changes>` stays as a named placeholder in code font, as the closing line of the list and as its checklist item. `<pr-count>` is unchanged apart from the drafting-time count.
- **The file's closing lines are untouched:** the self-naming last line stays last.

**Tests**
- **T023's verify command** passes: the last line names `openRepoTools-spec` and `docs/release-notes/v1.1.0.md`, `## Before the tag` is there, and so is `pipx install --force openrepotools`.
- **Coverage:** a script over `git log aaa7bc8..acc89ea`, 56 commits and no merge, finds each pull request cited above "Before the tag" except the six of #228's package series, which #228's lines cover. That is 46 by area, #233 at the head of the list, and #247, #271 and #283 under "What 1.1.0 adds". #296 is not in that range, and its line cites it.
- **Hygiene:** by grep over `tests/`, no test names this file. The two that read its content scan every tracked file, and both are in `tests/test_repo_hygiene.py`. The only other test that names `release-notes` is in `tests/test_release_workflow.py`, and it reads `release.yml`, not the notes. All of `tests/test_repo_hygiene.py` was run on the file, through `tests/run.sh` with every `LANES_*` and `TMUX*` variable unset:

  ```text
  -k 'repo_hygiene' -v
  collected 2933 items / 2696 deselected / 237 selected
  237 passed, 2696 deselected in 2.59s
  ```
- **Not run:** the whole suite. CI's `tests` job is the suite of record. This pull request's own runs on `bebb77d` went red on `ubuntu-latest`, which is Ubuntu 26.04, for the reasons #296 gives, because #296 was not yet on main. The record is lane 3's queue run over main, #296 and `bebb77d`, on `ubuntu-24.04` (below).

**Where the draft's rules left room, and lane 3's answers (comment 6314185715)**
- **Which section holds #255 and #280:** "What 1.1.0 adds", because the milestone is Brett Heap's scope for the release and #237 with #247 already sit there.
- **`<other-changes>` stays** as a closing placeholder after the drafted list, since this pull request itself lands after `a42946e`.
- **How much to list:** the tests-only and migration-record lines stay through the tag, because the check that every pull request has a line depends on them. If Brett Heap wants a shorter body, they fold into one line that still lists every pull request number.
- **Issues and pull requests:** the milestone lines cite both, and the area lines cite the pull request only.

**Lane 3's re-check of `bebb77d`** (#293 6315668503) is MERGE AFTER FIXES.
- **What holds:** every line lane 2 listed, the recount behind line 140 (44 of the 53 pull requests in `aaa7bc8..a42946e`, and 56 in `aaa7bc8..acc89ea`), the file's rules, and T023's verify commands.
- **One SHOULD and two NITs, all wording:**
  - line 45's sentence on a dependency nested in a leg, where "one above it" reads as a directory above the leg, which is #272's case. The text was lane 3's own;
  - "One known gap" in the same line;
  - line 74's last clause.
- **Why they wait:** lane 3 merges the notes as tested and carries the three fixes into the notes' final pass at the tag. A push here would cost another CI round while lane 2 is out of usage until 15:30Z, and the notes are edited again at the tag in any case.

Lane: openRepoTools-2

Landed by lane openRepoTools-3, as queue prefix `lane3-queue-u/p2-293` (tree `9b879ca`; tests.yml run 39595166877, green on `ubuntu-24.04`), right after #296. Before the landing, lane 3 merged main into the branch, so that its own checks ran over main with #296; the content is unchanged. Brett Heap's word, of 2026-10-20T22:5xZ: "merge all when green for all merges thru v1.1.0. use this for all lanes" (#228 6303614082).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <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.

1 participant