From 0b6f2a0be00a5de3e9b1235b5bbb41cbe0a6453e Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Sat, 31 Oct 2026 19:58:41 +0000 Subject: [PATCH] A lane control root, or a directory above it, that cannot be searched is read as not read instead of as no trees: lane-state and lane-trees exit 10 instead of 8, the listing says UNREAD, and an inventory sync keeps the lane's rows This is the T017 continuation, in this leg (opensoft/openRepoTools-spec), of the original opensoft/openRepoTools branch fix/control-root-unsearchable-291 at 3e97ae51445291fc3efdd36da5709edee3103549, issue opensoft/openRepoTools#291. The original branched from main 50d05d4 and its head merges 50d05d4, so its own change is its head against 50d05d4 with that merge left out (the merge's tree equals git's automatic merge). Spec-leg part of its own change: docs/README-lanes.md (+25 -8: the exit-code row for 10, the unreadable row of the verdict table, a paragraph under Where it lives on when a rung is passed over, the lane-trees paragraph, the inventory index's path row and marker-row paragraph, and the named-read paragraph). Its other paths (lanes-edit.sh, lanes-index, lanes and tests/test_unsearchable_control_root_291.py) belong to the code leg and are the paired commit there; the leg table sends docs/ to this leg with no rename. This continuation stacks on the one for opensoft/openRepoTools#301, as ORDER.md's chain has it; the two touch different hunks of the manual and the apply was clean. The subjects below are quoted from the originals; a bare #N in them is opensoft/openRepoTools#N. Old-to-new receipt, the original's own non-merge commits in opensoft/openRepoTools (50d05d4..3e97ae5): a431854c42c4 Test that a lane control root nobody could search is never read as a lane with no trees, and that an inventory sync keeps that lane's rows (#291) 02571b4ade4f A lane control root that cannot be searched is read as not read, never as no trees, so an inventory sync keeps the lane's rows (#291) af4d132af4c7 A lane whose inventory could not be read is no longer said to have one that is there, because a blocked lookup never saw whether it was (#291) 124b509eb6f0 Test that a write under a lane control root nobody could look for fails and is filed nowhere else, and that a .lane-state link whose target is gone is unread, never none (#291) b0a631baa46c lane_generation's docstring no longer says that a snapshot it could not read is there, because a blocked lookup never saw whether it was (#291) 7db059f458a7 The README's rung section says that a rung is passed over only where its directory is established absent, and that a rung nobody could look for keeps its root, so its reads are 10 and its writes fail (#291) 468b20e6c411 lane-reconcile no longer says that a trees directory nobody could look for is there, and says instead that whether it is there could not be read, and why (#291) 3af5006f565f The lanes-index marker comment, the second exit-10 header in lanes-edit.sh and the README's unreadable verdict row no longer say that a record nobody could look for is there (#291) a3ea2af1946a Only the #291 cases that set a mode skip as root, so the links that cannot be followed, which set none, are proved there too (#291) 82ac6b742392 lookup_blocked's comment names its limit: a denial that the mode bits do not show still reads as absent (#291) 3e97ae514452 The README's rung paragraph now names what each surface says where a rung could not be looked for, instead of calling every read 10 (#291) Lane: openRepoTools-4 Co-Authored-By: Claude Sonnet 5.5 --- docs/README-lanes.md | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/docs/README-lanes.md b/docs/README-lanes.md index c2c3c72..a529a4b 100644 --- a/docs/README-lanes.md +++ b/docs/README-lanes.md @@ -1229,7 +1229,7 @@ else. | 6 | `git add` / `commit` / `push` failed | | 7 | another act got there first and this one wrote nothing: `claim`'s `CLAIM-LOST` (another lane's claim landed first), and `set-lane-state` / `set-lane-tree`'s lifecycle fence (openRepoTools#91, below) | | 9 | `CLAIM-LOST` — issue #30's own dead-lane verdict could not be reconfirmed before a `--force` takeover's push landed: the source lane resumed, a live session now backs it up, or that could not be read at all (`claim` only). Never 7 — that code is a RIVAL's claim, and this is the same lane the takeover was granted over | -| 10 | `lane-state`: the lane's lifecycle snapshot is there and could not be read, or is of this schema and not a whole record, so nothing can be read out of it as a snapshot (openRepoTools#91 and #115, below) — never the 8 that means it has none. It was 9 until #61 spent 9 on `claim`. `lane-trees`: the lane's `trees` directory is there and could not be listed (#157 item 4e) — never the 8 that means it recorded no tree | +| 10 | `lane-state`: the lane's lifecycle snapshot is there and could not be read, or is of this schema and not a whole record, so nothing can be read out of it as a snapshot (openRepoTools#91 and #115, below) — never the 8 that means it has none. It was 9 until #61 spent 9 on `claim`. `lane-trees`: the lane's `trees` directory is there and could not be listed (#157 item 4e) — never the 8 that means it recorded no tree. Both spend it, too, where the record could not even be looked for: its control root, or a directory above it, cannot be searched, or a link on the way cannot be followed (#291) | | 8 | no record — and no other meaning | | 64 | `swapped`'s own usage error — never the dispatcher's 2 — and the lifecycle arms' own: `set-lane-tree`'s partial observation, a count that is not one, or a field that is only blanks (below) | @@ -3342,7 +3342,7 @@ RUNNING --/handoff begins--> SWAPPING --record + row + handoff all landed--> SWA | `SWAPPED` | yes | inconsistent — a swapped lane has no holder, and neither side is overwritten | | `CLOSED` | — | the lane is finished; a dirty or unpushed tree under it is a closure inconsistency and no cleanup is made | | any | **unreadable** | `indeterminate`. A holder that could not be established is NOT "no holder" (`R22`, Amendment 7(d)), and no crash is pronounced on a read nobody got. That includes the ids the holder is looked up by: a register row that could not be read — the published one, or this checkout's copy — is never a row naming no session (opensoft/openRepoTools#113). | -| **unreadable** | — | `indeterminate` again, and for the same rule read one file earlier: a snapshot that IS THERE and cannot be opened is not a lane that has none. `lane-state` exits **10** for it, never the **8** that means *this lane has no snapshot, go on*. | +| **unreadable** | — | `indeterminate` again, and for the same rule read one file earlier: a snapshot that IS THERE and cannot be opened is not a lane that has none, and nor is one that could not even be looked for — its control root, or a directory above it, cannot be searched, or a link on the way cannot be followed (#291). `lane-state` exits **10** for both, never the **8** that means *this lane has no snapshot, go on*. | | **`MALFORMED`** | — | `indeterminate`: a snapshot of this schema that is not a WHOLE record — cut short, or edited by hand (opensoft/openRepoTools#115). `lane-state` exits **10** for it too, and every writer refuses to replace it. | | any | — and a worktree read **failed** | `indeterminate`, whatever the snapshot and the holder say: an inventory directory, the checkout's registrations or a lane root that could not be listed hides trees the report never saw (opensoft/openRepoTools#113). | | any | — and the lane is **bound elsewhere** | `indeterminate`. Amendment 18(b): liveness is pronounced only from inside the binding's own host and container, and from anywhere else a binding is UNKNOWN, never dead — and this workstation's snapshot is this workstation's alone. The one exception is clause (b)'s own: a binding whose window is gone from this host's tmux is dead, and the local read decides. | @@ -3403,6 +3403,13 @@ No rung answering is **8**, *this lane has no control root* — the ordinary answer for a lane that has not started under Amendment 11(c), and not a failure. Nothing is backfilled (Amendment 7(i)). +A rung is passed over only where its directory is established absent. Where it +could not be looked for (#291) — a directory on the way cannot be searched, or a +link on the way cannot be followed — its root is the answer: `lane-state` and +`lane-trees` exit **10** there, never the **8** that means *none*; the listing +names the lane `UNREAD`; `lane-reconcile` pronounces `indeterminate`; and every +write there fails rather than filing under the next rung. + Because the snapshot is filed under NOTHING — never committed, never leaving the machine that wrote it — `R-A11-14` does not reach it: a container with no `$LANES_WORKSTATION` still keeps a lifecycle it can recover itself from, while @@ -3424,9 +3431,15 @@ lanes-edit.sh lane-tree-now A `trees` directory that is there and cannot be listed is `lane-trees`' exit **10**, never the **8** of a lane that recorded no tree (opensoft/openRepoTools#157 -item 4e). Every lane's records on this workstation, one row per tree, are `lanes ---worktrees`; the derived index keeps them as its `worktrees` table, which no -act reads (*The derived index*, below). +item 4e). So is one that could not even be looked for (#291): under a directory +this account cannot search, stat fails as it does for a name that is gone, so +the read walks up to the deepest name that is there and answers **8** only where +that name could answer for the one below it. `lane-state` answers for the +snapshot the same way, and a recorded `dir` whose parent could not be looked for +keeps its own control root rather than falling to `$PROJECTS_ROOT`'s. Every +lane's records on this workstation, one row per tree, are `lanes --worktrees`; +the derived index keeps them as its `worktrees` table, which no act reads (*The +derived index*, below). The full `head` and the `upstream` are why this is not the prose section one more time: `%h` is an abbreviation that lengthens as a repository grows, and @@ -3833,7 +3846,7 @@ lanes-index reconcile --source inventory [--dry-run] |---|---| | `workstation`, `lane` | the workstation's name (`lanes-edit.sh workstation`) and the lane's canonical one | | `owner` | `legacy`, `managed ` or `unknown` — the read `managed-projection` makes, never a parser of the indexer's | -| `path`, `branch`, `dirty_count`, `unpushed_count`, `last_seen_utc` | the tree's sidecar: the last observation, `detached ` for a detached head, an `unknown` count as NULL. A sidecar that could not be read is `unreadable sidecar`, one of another schema `unknown schema `, each with the record's id where its path would be | +| `path`, `branch`, `dirty_count`, `unpushed_count`, `last_seen_utc` | the tree's sidecar: the last observation, `detached ` for a detached head, an `unknown` count as NULL. A sidecar that could not be read is `unreadable sidecar`, one of another schema `unknown schema `, each with the record's id where its path would be; the lane of a sidecar that could not be read is `UNREAD` beside it, so a sync keeps that lane's rows rather than replace the tree's row with one keyed by the record's id (#291) | | `base`, `last_commit` | two git reads of the RECORDED head, in the tree or, once it is gone, its recorded checkout: its merge-base with `origin/main` (else `main`, else `origin/HEAD`), and ` ` | | `lifecycle`, `generation`, `operation` | the lane's snapshot (`lane-state`): `RUNNING`, `SWAPPING`, `SWAPPED` or `CLOSED` for a **legacy** lane; `INDETERMINATE` for a managed or unknown one, whose lifecycle this tooling does not pronounce on, and for a snapshot that is missing, unreadable or of another schema | | `writer_live` | the listing's own LIVE — a session record on this workstation naming one of the row's ids — and NULL where those records could not be read or the lane is bound on another host, from where liveness is unknown and never dead (Amendment 18(b)) | @@ -3858,7 +3871,8 @@ the inventory no longer names — removed at landing, or by a sweep's dispositio `sync` that reads a lane's lifecycle generation below the one its rows were written at (a snapshot moved aside and begun again) writes nothing for that lane and says so, and `reconcile --source inventory` is the act that takes the lane -as it now stands. A lane whose `trees` directory is there and cannot be listed +as it now stands. A lane whose `trees` directory is there and cannot be listed, +could not even be looked for, or holds a record that could not be read (#291) keeps its rows — it is not a lane with no worktrees — and gets ONE MARKER ROW, keyed `:unread`, with no `path` and the `branch` `unreadable inventory ()` — the key is what makes it one, because a tree's path can be empty (a @@ -3904,7 +3918,10 @@ could not name would have needed a marker for each of them, one for the listing itself — a first sync has no rows to hold — and a one-lane `--index` read that knew of it. An indexer from before #173's item c1 refuses the line too, as one it does not read. A read of one NAMED lane goes by that name, which no listing -gives, and never prints `PARTIAL`. +gives, and never prints `PARTIAL`. Nor does a lane the listing DID name and +could not read — its `trees` directory, its control root or a directory above +it cannot be listed or searched, or a record of its trees cannot be read: that +is the lane's own `UNREAD` line, and only that lane's rows are held (#291). Where those reads succeed, the listing makes each of them once, except the list of the logs, which it makes twice (the review of #289). The register, the