You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Derived index: a worktrees table mirroring every lane's #97 inventory under inventory:<Workstation>, nudged by every inventory write, and lanes --worktrees to read it with or without --index - #171
The derived index (Amendment 14, #160/#164) gains its second source: inventory:<Workstation>, this workstation's #97 worktree inventory, copied into the worktrees table schema 1 already created with this issue's columns — so no store needs a new schema version. A new read, lanes --worktrees [<lane> | --all], lists every open worktree; with --index it reads the table, which holds every workstation syncing into the same store, so Raven sees Eagle's leftovers.
lanes-edit.sh worktrees [<lane> | --all] [--index] [--fetch] — one row per tree, the ONE implementation both the listing and the indexer read. Lanes are the listing's own (lanes_rows --all --closed, so a closed lane's leftovers are not hidden); trees are lane_trees_list's sidecars; owner is lane_is_managed_owned (the read managed-projection makes); lifecycle is lane_state_read's word for a legacy lane; writer liveness is the listing's LIVE. No directory is walked for a tree no sidecar names, and nothing is recomputed: the only git reads are of the RECORDED head (its subject, and its merge-base with origin/main, else main, else origin/HEAD). With --index it goes through index_open worktrees → lanes-index export --worktrees, setting its own export directory and never LANES_IDX_DIR, so no register read is answered out of the index by it.
lanes-index sync|reconcile --source inventory — reads lanes-edit.sh worktrees --all (never a parser of its own), upserts keyed by (source, <lane>:<path>), versioned by a digest of the row and its provenance; rows leave when the inventory no longer names them. Provenance only moves forward, per lane: a sync that reads a lane's lifecycle generation below the one its rows were written at writes nothing for that lane and says so; reconcile takes the lane as it now stands. A lane whose trees directory is there and cannot be listed keeps its rows. The source's own row carries a fingerprint of the whole table (the compare-and-swap token) and a sync count. pr is asked of gh best effort (never under LANES_NO_GITHUB=1; a MERGED/CLOSED answer is not asked again while branch and last commit stand). A container with no LANES_WORKSTATION syncs nothing (exit 2, R-A11-14). status names this workstation's inventory; export --worktrees writes every inventory source as the helper's own lines.
The nudge — set-lane-state, set-lane-tree, the lifecycle follow after STARTED/RESUMED/ENDED/RETIRED, and a rename's move of the control root leave LANES_INDEX_NUDGE_INV; cleanup starts the same detached fork a landed push does, as lanes-index sync --source inventory (stdin closed, never waited, skipped where lanes-index is not on PATH or LANES_INDEX=off). The sync lock's rerun mark now names its source, so a register sync holding the lock when an inventory nudge arrives syncs the inventory before it lets go (the register's mark keeps its old path).
lanes --worktrees renders WORKSTATION · LANE · OWNER · LIFECYCLE · WRITER · BRANCH · DIRTY · UNPUSHED · LAST SEEN · PATH, with a footer that every field is the last observation and that the current truth of a lane is lanes-edit.sh lane-reconcile <lane>. stderr says read: index (<store>) at inventory:<ws> synced <UTC> or read: sources (index <why>).
Amendment 14(e) left "#97's lifecycle snapshots and worktree inventories" to "a later word's"; #161 is that word. A resuming account — or one sitting down at the other workstation — wants every open worktree before it decides anything, and today that is one lane-reconcile per lane, on the workstation that holds it.
The columns
column
from
workstation, lane
lanes-edit.sh workstation; the lane's canonical name
the tree's sidecar (the last observation); detached <sha>; an unknown count is NULL; an unreadable or other-schema sidecar is unreadable sidecar / unknown schema <n> with the record id where its path would be
base, last_commit
merge-base of the recorded head with origin/main (else main, else origin/HEAD); <sha> <subject> — in the tree, or its recorded checkout once it is gone
lifecycle, generation, operation
lane-state: RUNNING/SWAPPING/SWAPPED/CLOSED for a legacy lane; INDETERMINATE for a managed or unknown owner, or a snapshot missing, unreadable or of another schema
writer_live
the listing's LIVE; NULL where session records could not be read or the lane is bound on another host (18(b))
pr_number, pr_state
gh at sync, best effort, nullable
provenance, indexed_utc
the generation the tree's own record was filed under; the sync
Decisions worth a reviewer's eye
The fallback is the sidecars, not lane-reconcile.lanes --index --worktrees falls back to lanes-edit.sh worktrees, which reads the same sidecars and snapshot lane-reconcile reads, so the fallback prints the same bytes the index does wherever it is current. Calling lane-reconcile per lane would recompute against the disk (a different answer from the table, by design) at ~3 s a lane; the listing's footer names it as the current truth instead.
dirty/unpushed/last_seen are the last observation, never recomputed, so a replay writes nothing and the table is a copy of the inventory rather than a second reading of the disk.
No test creates a real database or reaches GitHub: every store is SQLite in tmp_path, Postgres is a fake psql, gh is a fake.
Measurements
On Eagle, 2026-10-05, against the live register (61 lanes; this workstation's inventory currently records no trees, so these are the enumeration cost every sync pays), read-only: LANES_NO_FETCH=1, and every store a scratch SQLite under a temporary XDG_STATE_HOME — nothing was written to the workstation's own state. Load average 4–7 (other lanes' suites running).
read
time
lanes-edit.sh worktrees --all (the sources)
2.6 – 4.3 s (7.9 s before the listing's dir column was handed to lane_control_root, re-reading every lane's log a second time)
lanes-index sync --source inventory — first sync, then two no-op replays
Mirror each workstation's lane worktree inventory into the derived index and expose it through a new worktree listing without changing authoritative lane actions.
New Features:
Add lanes --worktrees to list recorded worktree inventory data locally or from the derived index.
Add inventory synchronization and reconciliation for the derived index worktrees table, including cross-workstation sources and provenance tracking.
Bug Fixes:
Ensure inventory changes trigger detached index synchronization without affecting reads or lane actions.
Preserve indexed rows when inventory reads fail or lifecycle provenance moves backward.
Enhancements:
Keep indexed worktree observations separate from authoritative lane actions while providing fallback reads from sidecars.
Add best-effort pull-request metadata, workstation-aware writer liveness, index status reporting, and source-specific rerun handling.
Documentation:
Document the worktrees table, its columns, synchronization behavior, read modes, fallback behavior, and actions that never consume the index.
lanes-edit.sh gains `worktrees [<lane> | --all] [--index] [--fetch]`, the
#97 inventory of every lane on this workstation as US-separated rows: the
lanes are the listing's own (`lanes_rows --all --closed`, so a closed lane's
leftovers are not hidden), the trees are `lane_trees_list`'s sidecars, the
owner is `lane_is_managed_owned`'s answer, the lifecycle is the snapshot's
word for a legacy lane only, and writer liveness is the listing's LIVE.
Nothing is recomputed and no directory is walked for a tree no sidecar
names; the only git reads are of the recorded head (its subject and its
merge-base with main).
This is the one implementation that both `lanes --worktrees` and the
indexer's new inventory source read, so the listing and the derived
index's `worktrees` table cannot disagree. With `--index` the read goes
through `index_open worktrees`, which asks `lanes-index export --worktrees`
and sets its own export directory, never `LANES_IDX_DIR`, so no published
register read is answered out of the index by it.
The listing's directory column is handed to `lane_control_root` as the
payload it takes, so sixty lanes' logs are not read a second time: the
whole read went from 7.9 s to 2.7 s on Eagle's 61 lanes.
Lane: openRepoTools-3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lanes-index gains the source `inventory:<Workstation>` (opensoft/
openRepoTools#161), the "later word" Amendment 14(e) left open for #97's
inventories. `sync`/`reconcile --source inventory` read `lanes-edit.sh
worktrees --all` - the inventory through the helper that writes it - and
upsert the `worktrees` table schema 1 already created with #161's columns,
so no store needs a new version. Rows are keyed by (source, `<lane>:<path>`)
and versioned by a digest of the row and its provenance (the generation the
tree's own record was filed under); a tree the inventory no longer names
leaves in the same sync.
The provenance moves only forward per lane: a sync that reads a lane's
lifecycle generation below the one its rows were written at writes nothing
for that lane, and reconcile takes it as it now stands. A lane whose trees
directory cannot be listed keeps its rows. The source's own row carries a
fingerprint of the table as its compare-and-swap token and a sync count.
`pr` is asked of `gh` best effort, never under LANES_NO_GITHUB=1, and a
MERGED or CLOSED answer is not asked again while the branch stands.
The sync lock's rerun mark now names its source, so a register sync that
holds the lock when an inventory nudge arrives syncs the inventory before
it lets go. `export --worktrees` writes every inventory source the store
holds as the helper's own lines, and `status` names this workstation's
inventory. A container with no LANES_WORKSTATION syncs nothing.
Lane: openRepoTools-3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
set-lane-state, set-lane-tree, the lifecycle follow after a STARTED,
RESUMED, ENDED or RETIRED, and a rename's move of the control root write
no commit and push nothing, so the derived index's `worktrees` table would
only follow them on a person's reconcile. Each now leaves
LANES_INDEX_NUDGE_INV for `cleanup`, which starts the same detached
`lanes-index sync` a landed push does, as `sync --source inventory`:
stdin closed, never waited, its status never read, skipped where
lanes-index is not on PATH or LANES_INDEX=off. Like the register's flag it
is assigned at start-up whatever the environment says.
Lane: openRepoTools-3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`lanes --worktrees [<lane> | --all]` renders `lanes-edit.sh worktrees`:
workstation, lane, owner, lifecycle, writer liveness, branch, dirty and
unpushed counts, last seen and path, with a footer saying every field is
the inventory's last observation and that the current truth of a lane is
`lanes-edit.sh lane-reconcile <lane>`, which reads the disk. With --index
it reads the derived index's `worktrees` table - every workstation syncing
into that store - and stderr says which was read, as for the listing. It
takes none of the listing's narrowing flags, and a helper predating #161
is named as one.
Lane: openRepoTools-3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README-lanes.md's "The derived index" gains the `worktrees` table: what it
is for (the next account's first query, across workstations), each column
and where it comes from, how rows follow the inventory and why the
provenance moves forward per lane, the nudge, the `lanes --worktrees` read
with and without --index, and what never reads it - no act, not
`lane-reconcile` (there is no `lane-reconcile --index`), `lane-start`,
`lane-end`, `lane-handoff` or the worktree sweep (#162). The inventory
section points at the listing, and the reserved-table note becomes the
inventory's.
Lane: openRepoTools-3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tests/test_lanes_index.py gains an inventory fixture written by the real
writers over real worktrees - a RUNNING lane with one clean tree and one
dirty, unpushed one, a SWAPPING lane whose session is live, and the
leftovers of a valid and a malformed managed marker - and holds the
`worktrees` table to the promise #160 made for the register: the rows
mirror the sidecars (owner, lifecycle, liveness, counts, base, last
commit, provenance); a replay writes nothing and one write moves one row;
a tree the inventory no longer names leaves; a wiped table reconciles row
for row and a hand-edited row is put right; the provenance only moves
forward per lane; the owner column is the seam's answer; `lanes
--worktrees` shows the running and the dirty tree; the index read is the
source read byte for byte; each of six failures falls back to the
sidecars and says so; the inventory writers and `lane-reconcile` are
byte-identical against an unreachable store and over a poisoned table;
the writers nudge `sync --source inventory` and the reads never do; `gh`
is asked best effort; a rerun mark names its source; and another
workstation's inventory, or a nameless container's, is refused.
Lane: openRepoTools-3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`worktrees` read `writer_live` as the listing's LIVE and wrote 0 for every
other lane. For a lane whose last STARTED or RESUMED bound it on another
host, this workstation's session records say nothing about its writer, and
Amendment 18(b) pronounces liveness only from inside the binding: from here
it is unknown, never dead. The listing's column 13 says where a lane is
bound, so `elsewhere` now leaves the field empty - NULL in the index,
`unknown` in `lanes --worktrees` - exactly as unreadable session records
already did. A test binds a fixture lane on Raven and reads both.
Lane: openRepoTools-3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The column table said only that unreadable session records leave it NULL;
since the previous commit a lane bound on another host does too, and a
reader of the table has to know that NULL is "not established here", not
"nobody is writing".
Lane: openRepoTools-3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A register sync that takes the inventory's rerun mark runs the inventory
sync in its own loop; a refusal there (a container with no
LANES_WORKSTATION) used to propagate as an exception out of that loop.
It is now said and returned as the 2 it is, exactly as `main` would say
it, so the exit is the same and the loop ends the way every other failed
round ends.
Lane: openRepoTools-3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shell suite's Amendment 14 section runs each act three ways - as it
always ran, against an indexer configured at a store nobody can reach,
and over a poisoned index. It now syncs this suite's whole inventory into
the poisoned store and makes those rows wrong too (a forged tree, forged
lifecycles, counts and owner), proves `worktrees --index` reads the poison
while the plain read does not, and adds the inventory's acts to the
comparison: set-lane-tree and set-lane-state, whose nudge must reach the
unreachable store, and lane-reconcile, lane-trees and lane-state, which
reach for nothing. The snapshot taken between runs now carries the lanes'
control root, or a writer's second run would be a generation ahead of its
first. set-lane-tree writes repoRC-2's record, because repoRC-5's record
of the same tree was left at a schema the writer refuses to replace.
Lane: openRepoTools-3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds a second derived-index source for each workstation’s #97 worktree inventory, exposes it through lanes --worktrees with optional indexed reads, synchronizes it with guarded per-lane provenance and detached write nudges, and verifies that operational acts remain independent of the derived table.
Sequence diagram for worktree inventory synchronization
sequenceDiagram
participant Writer as Inventory writer
participant Helper as lanes-edit.sh
participant Cleanup as cleanup
participant Indexer as lanes-index
participant Store as Derived index
Writer->>Helper: set-lane-state or set-lane-tree
Helper->>Cleanup: set LANES_INDEX_NUDGE_INV
Cleanup->>Indexer: sync --source inventory
Indexer->>Helper: worktrees --all
Helper-->>Indexer: inventory rows and provenance
Indexer->>Store: upsert worktrees rows
Indexer->>Store: remove rows no longer named
Loading
Sequence diagram for indexed worktree listing with fallback
sequenceDiagram
participant User as User
participant Lanes as lanes --worktrees
participant Index as lanes-index
participant Store as Derived index
participant Helper as lanes-edit.sh
participant Sidecars as Worktree sidecars
User->>Lanes: --index --all
Lanes->>Index: export --worktrees
alt index available
Index->>Store: read worktrees sources
Store-->>Index: indexed rows
Index-->>Lanes: export rows
else index unavailable
Lanes->>Helper: worktrees --all
Helper->>Sidecars: read recorded inventory
Sidecars-->>Helper: inventory rows
Helper-->>Lanes: source rows
end
Lanes-->>User: render worktree listing
Loading
Entity relationship diagram for the worktrees index
erDiagram
INVENTORY_SOURCE ||--o{ WORKTREE : contains
INVENTORY_SOURCE {
string workstation
string fingerprint
int sync_count
datetime synced_utc
}
WORKTREE {
string lane
string path
string owner
string lifecycle
int generation
string branch
int dirty_count
int unpushed_count
datetime last_seen_utc
int provenance
}
Loading
File-Level Changes
Change
Details
Files
Add a workstation-scoped worktree inventory listing and derived-index read path.
Introduce worktrees output from sidecars, lifecycle snapshots, ownership projection, and session liveness without walking or recomputing working trees.
Support lane/all selection, --index, --fetch, tabular rendering, footer guidance, and index-read fallback diagnostics.
Keep indexed worktree reads isolated from register index state.
lanes lanes-edit.sh
Implement inventory synchronization into the derived index with guarded provenance and source-specific behavior.
Add inventory sync/reconcile/export/status handling keyed by workstation, lane, and path, including upsert/removal and replay-digest behavior.
Enforce per-lane forward provenance, preserve rows when tree enumeration is unreadable, and maintain source fingerprints and sync counts.
Add best-effort pull-request enrichment and reject non-local or nameless workstation inventory sources.
lanes-index
Nudge inventory indexing after inventory mutations while preserving detached, offline-safe operation.
Mark state, tree, lifecycle-follow, and rename writes for inventory synchronization.
Run detached sync --source inventory nudges when enabled and available, with source-aware rerun marks under the sync lock.
Ensure reads and acts do not consume the derived worktrees table.
lanes-edit.sh lanes-index
Document the worktrees table contract, read semantics, and non-consumers.
Describe schema columns, provenance, last-observation semantics, workstation sources, synchronization, fallback behavior, and index isolation.
Document that operational acts continue reading sidecars and disk rather than the derived table.
docs/README-lanes.md
Add comprehensive coverage for inventory mirroring, reads, synchronization, safety, and edge cases.
Test row-for-row mirroring, replay/removal/reconcile, provenance fences, ownership seams, lifecycle and liveness semantics, and pull-request enrichment.
Test indexed versus source reads, fallback modes, poisoned/offline indexes, source restrictions, and detached nudges.
Extend shell fixtures and snapshots to validate inventory writers and act behavior.
Add a derived-index worktrees table sourced from each workstation's #97 sidecar inventory, containing the specified worktree, lane ownership, lifecycle, liveness, observation, Git, and optional pull-request metadata, with forward-only provenance and removal of rows no longer present in the inventory.
--all]` for cross-workstation worktree visibility, with source-sidecar fallback when the index is unavailable or invalid, while ensuring the existing reconciliation and lane acts do not read or depend on the derived table.
Keep the inventory index synchronized after inventory writes and validate offline no-read behavior, poisoned or wiped indexes, replay, provenance, ownership, fallback, liveness, and cross-workstation behavior through documentation and tests.
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!
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.
Four findings, each a real defect:
- writer_live: the listing's LIVE overrode the locality and readability
checks, so a still-running local session of an older binding read as a
live writer for a lane now bound on another host. Amendment 18(b)
pronounces liveness only from inside the binding: `elsewhere` and an
unreadable session read now win, and a test binds the live fixture lane
on Raven.
- the no-op: "nothing to write" was decided from the rows alone, so a
fingerprint changed by hand with every row right was never repaired. The
source row must now agree too, and a test changes it and syncs.
- the sync loop returned on the first failed source, dropping every mark
already taken off the disk with it. It now drains them all and exits with
the first failure; a test fails the register round and sees the
inventory synced.
- the manual (and the listing's own comment) promised the index read was
the source read byte for byte; on a store other workstations sync into
it carries their rows too. The promise is now this workstation's rows,
and the whole read only where no other workstation writes the store.
Lane: openRepoTools-3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The mode == "sync" guard makes reconcile re-query unchanged MERGED/CLOSED pull requests. That contradicts the documented final-state cache and can replace a final answer with NULL when this best-effort call fails. Preserve final states in both modes while keeping the no-GitHub carryover sync-specific.
Three findings, each a way rows or marks could be lost:
- `worktrees` read any lane-trees failure other than "no record" as an
empty inventory once the trees directory was found, so the next sync
removed that lane's rows. 8 stays "nothing recorded"; any other exit is
now an UNREAD row, whose rows every sync keeps. A test makes a lane's
trees directory unlistable and finds its rows kept and the listing
saying NOT READ.
- the per-lane forward fence skipped a lane that names no tree now, so a
lane whose snapshot was moved aside and begun again without trees was
emptied by a sync. For the few lanes whose rows would all leave, the
indexer now asks `lanes-edit.sh lane-state` for the generation and holds
the lane where it went back; reconcile takes it. A test does exactly
that.
- the eighth-round cap returned with sources still due whose marks it had
already taken off the disk. It now marks them again for the next sync,
as a mark it never took is left; a test keeps both sources busy to the
cap and finds both marks on disk.
Lane: openRepoTools-3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#121 (supervised context restart) landed as 00971b9 while #171 was
open. It changed lanes-edit.sh, lane-handoff, lane-start,
docs/README-lanes.md and tests/test_lane_helpers.sh. This is a merge
and not a rebase because the branch is already pushed, and rewriting
it would need a force push.
One textual conflict: the unknown-subcommand refusal in lanes-edit.sh.
The resolution takes #121's list (lane-holders, legacy-restart-check,
publish-handoff, restart-intent, set-restart-intent) and appends this
branch's `worktrees`. The list still matches the dispatcher's 63 arms.
One semantic fix on top of #121's text: #121's new lane_state_rename
branch, taken when a lane has restart history, moves lane-state.yaml
and trees/ into the new name's control root. That move changes the
inventory the worktrees table mirrors, so it now sets
LANES_INDEX_NUDGE_INV like the other two move branches. #121's
restart-intent.yaml is not part of that inventory, so its writer
nudges nothing.
docs/README-lanes.md and tests/test_lane_helpers.sh merged without
conflict. This branch's changes to those two files and to lanes-edit.sh
are unchanged from fea9a29..f1e96c1, apart from the refusal list and
the nudge line above.
Lane: openRepoTools-3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bring #168 current with main so the lander can squash it on a green
head. The merge was clean: #171 and #168 share only
docs/README-lanes.md, where git placed #171's worktrees-index section
and #168's lane-worktrees section without overlap, and both stay.
#171 adds no placed file, so the install lists and their counts keep
lanes-index (#164) and lane-worktrees (#168) as they were.
Lane: openRepoTools-3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bring #169 current with main after #168 landed as 2080f3d, so the
lander can squash #169 on a green head. Main also carries #171
(f5444c5) and #172 (3660224). The merge base is 00971b9 because the
squash is not an ancestor of this branch, so git saw #168's files as
added on both sides. Four files conflicted:
- lane-worktrees (add/add) and README.md: main's copy is byte-identical
to #168's old head 87da332, and main changed neither file after it,
so this branch's copy (#168 plus #169) is the resolution.
- docs/README-lanes.md: the one block is #169's two report subsections
against nothing on main's side. They stay, and #171's worktrees-table
subsection, which merged cleanly, stays too.
- lanes-edit.sh: #171 and #169 each added a usage line, a dispatcher arm
before `*)`, and a refusal-list entry. Main's text comes first, then
#169's: the `worktrees` line and arm, then `pathspec-check`. The list
ends `|set-restart-intent|worktrees|pathspec-check`, so it still
matches the arms the repo-hygiene parser reads (64).
Changed-line comparison: this merge over main equals #169's own diff,
and over 45fa45d equals main's changes since 87da332 (#171 and #172).
The only difference in each is the refusal-list line that carries both
entries.
Lane: openRepoTools-3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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
readylanding gate: runs tests-macos once before the squash
2 participants
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
The derived index (Amendment 14, #160/#164) gains its second source:
inventory:<Workstation>, this workstation's #97 worktree inventory, copied into theworktreestable schema 1 already created with this issue's columns — so no store needs a new schema version. A new read,lanes --worktrees [<lane> | --all], lists every open worktree; with--indexit reads the table, which holds every workstation syncing into the same store, so Raven sees Eagle's leftovers.lanes-edit.sh worktrees [<lane> | --all] [--index] [--fetch]— one row per tree, the ONE implementation both the listing and the indexer read. Lanes are the listing's own (lanes_rows --all --closed, so a closed lane's leftovers are not hidden); trees arelane_trees_list's sidecars; owner islane_is_managed_owned(the readmanaged-projectionmakes); lifecycle islane_state_read's word for a legacy lane; writer liveness is the listing's LIVE. No directory is walked for a tree no sidecar names, and nothing is recomputed: the only git reads are of the RECORDED head (its subject, and its merge-base withorigin/main, elsemain, elseorigin/HEAD). With--indexit goes throughindex_open worktrees→lanes-index export --worktrees, setting its own export directory and neverLANES_IDX_DIR, so no register read is answered out of the index by it.lanes-index sync|reconcile --source inventory— readslanes-edit.sh worktrees --all(never a parser of its own), upserts keyed by (source,<lane>:<path>), versioned by a digest of the row and its provenance; rows leave when the inventory no longer names them. Provenance only moves forward, per lane: asyncthat reads a lane's lifecycle generation below the one its rows were written at writes nothing for that lane and says so;reconciletakes the lane as it now stands. A lane whosetreesdirectory is there and cannot be listed keeps its rows. The source's own row carries a fingerprint of the whole table (the compare-and-swap token) and a sync count.pris asked ofghbest effort (never underLANES_NO_GITHUB=1; a MERGED/CLOSED answer is not asked again while branch and last commit stand). A container with noLANES_WORKSTATIONsyncs nothing (exit 2, R-A11-14).statusnames this workstation's inventory;export --worktreeswrites every inventory source as the helper's own lines.set-lane-state,set-lane-tree, the lifecycle follow afterSTARTED/RESUMED/ENDED/RETIRED, and a rename's move of the control root leaveLANES_INDEX_NUDGE_INV;cleanupstarts the same detached fork a landed push does, aslanes-index sync --source inventory(stdin closed, never waited, skipped wherelanes-indexis not on PATH orLANES_INDEX=off). The sync lock's rerun mark now names its source, so a register sync holding the lock when an inventory nudge arrives syncs the inventory before it lets go (the register's mark keeps its old path).lanes --worktreesrenders WORKSTATION · LANE · OWNER · LIFECYCLE · WRITER · BRANCH · DIRTY · UNPUSHED · LAST SEEN · PATH, with a footer that every field is the last observation and that the current truth of a lane islanes-edit.sh lane-reconcile <lane>. stderr saysread: index (<store>) at inventory:<ws> synced <UTC>orread: sources (index <why>).worktreestable" under The derived index: what it is for (the next account's first query), every column, how rows follow the inventory, the nudge, the read, and what never reads it (no act: notlane-reconcile— there is nolane-reconcile --index— notlane-start,lane-end,lane-handoffor the sweep lane-worktrees sweep: one act that archives, rescues and removes a lane's abandoned worktrees, branches, scratch and killed-suite sandboxes after an ungraceful swap — dry run first, rescue branch or bundle before any removal, never a live writer #162).Why
Amendment 14(e) left "#97's lifecycle snapshots and worktree inventories" to "a later word's"; #161 is that word. A resuming account — or one sitting down at the other workstation — wants every open worktree before it decides anything, and today that is one
lane-reconcileper lane, on the workstation that holds it.The columns
workstation,lanelanes-edit.sh workstation; the lane's canonical nameownerlegacy/managed <owner>/unknown—managed-projection's readpath,branch,dirty_count,unpushed_count,last_seen_utcdetached <sha>; anunknowncount is NULL; an unreadable or other-schema sidecar isunreadable sidecar/unknown schema <n>with the record id where its path would bebase,last_commitorigin/main(elsemain, elseorigin/HEAD);<sha> <subject>— in the tree, or its recorded checkout once it is gonelifecycle,generation,operationlane-state:RUNNING/SWAPPING/SWAPPED/CLOSEDfor a legacy lane;INDETERMINATEfor a managed or unknown owner, or a snapshot missing, unreadable or of another schemawriter_livepr_number,pr_stateghat sync, best effort, nullableprovenance,indexed_utcDecisions worth a reviewer's eye
lane-reconcile.lanes --index --worktreesfalls back tolanes-edit.sh worktrees, which reads the same sidecars and snapshotlane-reconcilereads, so the fallback prints the same bytes the index does wherever it is current. Callinglane-reconcileper lane would recompute against the disk (a different answer from the table, by design) at ~3 s a lane; the listing's footer names it as the current truth instead.lifecyclealso carriesCLOSED. It is Crash-consistent lane worktree recovery: a lane now says WHERE its session stopped — RUNNING → SWAPPING → SWAPPED with a generation/operation fence, a machine-readable worktree inventory taken at every handoff, and a resume reconciliation that reports and resets nothing #97's fourth word, and a closed lane's leftover trees are exactly what the sweep (lane-worktrees sweep: one act that archives, rescues and removes a lane's abandoned worktrees, branches, scratch and killed-suite sandboxes after an ungraceful swap — dry run first, rescue branch or bundle before any removal, never a live writer #162) will want to see; everything else that is not one of the four words isINDETERMINATE.dirty/unpushed/last_seenare the last observation, never recomputed, so a replay writes nothing and the table is a copy of the inventory rather than a second reading of the disk.commit_shais a fingerprint of the table (the compare-and-swap token).Tests
tests/test_lanes_index.py(new inventory section) and the Amendment 14 section oftests/test_lane_helpers.sh:test_the_inventory_source_mirrors_every_lanes_sidecarstest_an_inventory_replay_changes_nothing_and_one_write_moves_one_rowtest_a_tree_the_inventory_no_longer_names_leaves_the_tabletest_a_wiped_inventory_reconciles_row_for_rowtest_an_inventorys_provenance_only_moves_forward_per_lanetest_the_inventory_owner_column_is_the_seams_answer--worktreesfor a lane with one RUNNING tree and one dirty onetest_lanes_worktrees_shows_one_running_tree_and_one_dirty_onetest_the_index_read_of_the_worktrees_is_the_source_readmissing,unreachable,wiped,unknown-schema,refused,off) saysread: sourcestest_each_worktrees_index_failure_falls_back_to_the_sidecars[*]lane-reconcilebyte-identical against an unreachable store, and the nudges reached ittest_offline_no_read_the_inventory_acts_…+ shella14_three(set-lane-tree, set-lane-state, lane-reconcile ×2, lane-trees, lane-state)worktreesrows change no act's answer — and the poison is realtest_a_poisoned_worktrees_table_…+ shella14_threesync --source inventory, detached; reads never nudge;LANES_INDEX=offstops ittest_an_inventory_write_nudges_and_a_read_never_doesghbest effort; a final answer not asked againtest_a_pull_request_is_asked_of_gh_best_effortstatusnames the inventory; a rerun mark names its sourcetest_status_names_the_inventory_and_a_mark_names_its_sourcetest_the_inventory_source_is_this_workstations_alonetest_a_lane_bound_elsewhere_has_no_liveness_pronounced_hereNo test creates a real database or reaches GitHub: every store is SQLite in
tmp_path, Postgres is a fakepsql,ghis a fake.Measurements
On Eagle, 2026-10-05, against the live register (61 lanes; this workstation's inventory currently records no trees, so these are the enumeration cost every sync pays), read-only:
LANES_NO_FETCH=1, and every store a scratch SQLite under a temporaryXDG_STATE_HOME— nothing was written to the workstation's own state. Load average 4–7 (other lanes' suites running).lanes-edit.sh worktrees --all(the sources)dircolumn was handed tolane_control_root, re-reading every lane's log a second time)lanes-index sync --source inventory— first sync, then two no-op replayslanes-index reconcile --source inventory --dry-runlanes --worktreeslanes --index --worktreesThe sync is detached behind every inventory write and never waited on, so its cost lands on no act.
Closes #161
Refs #97 #160 #164 brettheap/new-workstation#48
Lane: openRepoTools-3
🤖 Generated with Claude Code
Summary by Sourcery
Mirror each workstation's lane worktree inventory into the derived index and expose it through a new worktree listing without changing authoritative lane actions.
New Features:
lanes --worktreesto list recorded worktree inventory data locally or from the derived index.worktreestable, including cross-workstation sources and provenance tracking.Bug Fixes:
Enhancements:
Documentation:
Tests: