Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
0b380ad
Add lane-worktrees: the sweep that retires a lane's abandoned worktrees
brettheap Oct 5, 2026
8b49339
Test the sweep against every row of #162's disposition table
brettheap Oct 5, 2026
7d130ea
Place lane-worktrees as the seventeenth installed file
brettheap Oct 5, 2026
4f2acfb
Document the sweep in the lanes manual
brettheap Oct 5, 2026
7e58b98
Walk the estate for --links with scandir
brettheap Oct 5, 2026
8d2b71b
Keep a test run's bytecode, cache and temp out of the worktree
brettheap Oct 5, 2026
c4d97db
Name the repository's virtual environment on --install
brettheap Oct 5, 2026
c01ec43
Seed the workspace .gitignore with bytecode and cache rules
brettheap Oct 5, 2026
5458571
Refuse a workspace commit whose pathspec carries bytecode
brettheap Oct 5, 2026
23786c2
Run the estate report once a day from lane-start
brettheap Oct 5, 2026
7fdef8a
Report the estate's leftovers with sweep --all --dry-run --report
brettheap Oct 5, 2026
38e090f
Document the estate report and prevention at creation
brettheap Oct 5, 2026
bf066fd
Take Copilot round 1's defects in the sweep
brettheap Oct 5, 2026
ae17742
Document round 1's rules in the lanes manual
brettheap Oct 5, 2026
f4f4915
Merge branch 'feat/lane-worktrees-sweep' into feat/lane-worktrees-report
brettheap Oct 5, 2026
23d84da
Say what expiry_rows keeps now that #168 checks whole archives
brettheap Oct 5, 2026
da7bc59
Stop the suite's whole process group when a run ends
brettheap Oct 5, 2026
b0ea54e
Offer and seed site-packages/ with the other bytecode rules
brettheap Oct 5, 2026
b422d8f
Report duplicate clones with no named checkout, over one estate
brettheap Oct 5, 2026
3c0b188
Take Copilot round 2's defects in the sweep
brettheap Oct 5, 2026
892b02d
Merge branch 'feat/lane-worktrees-sweep' into feat/lane-worktrees-report
brettheap Oct 5, 2026
65f73ed
Refuse bytecode that is already staged, not only what add would stage
brettheap Oct 5, 2026
f5bccbd
Quote the venv paths in the command --install prints
brettheap Oct 5, 2026
dc7971c
Report what the report could not read
brettheap Oct 5, 2026
31db36f
Keep AGENTS.md and README.md within their line caps
brettheap Oct 5, 2026
30cc63a
Assert the flock file only where there is flock
brettheap Oct 5, 2026
5f4bd84
Switch --yes off until #170; keep the gate and exit contract honest
brettheap Oct 5, 2026
296e2f0
Document the --yes switch, lane-root ownership and porcelain escaping
brettheap Oct 5, 2026
9dd63f0
Merge branch 'feat/lane-worktrees-sweep' into feat/lane-worktrees-report
brettheap Oct 5, 2026
0cf1051
Run the suite in the foreground when stdin is a terminal
brettheap Oct 5, 2026
0b7a5c6
Never adopt a standalone clone under the lane's root
brettheap Oct 5, 2026
1484603
Merge branch 'feat/lane-worktrees-sweep' into feat/lane-worktrees-report
brettheap Oct 5, 2026
87da332
Merge remote-tracking branch 'origin/main' into feat/lane-worktrees-s…
brettheap Oct 6, 2026
45fa45d
Merge #168 at main 00971b9 (#121) into the report branch
brettheap Oct 6, 2026
4a79c8c
Fetch the register directly under --yes and refuse when it fails
brettheap Oct 6, 2026
040b12b
Pin the sweep's fetch refspec so --prune cannot delete local branches
brettheap Oct 6, 2026
d71569f
Refuse when a lane root or a registration list cannot be read
brettheap Oct 6, 2026
4b65c30
Walk every estate directory for dependents; a partial scan deletes no…
brettheap Oct 6, 2026
af80118
Read every other lane's control root before granting a tree
brettheap Oct 6, 2026
85365fd
Keep trees whose hidden edits, nested clones, tags or submodule refs …
brettheap Oct 6, 2026
8ec5a9a
Judge an ignored entry by its own name, not an ancestor's
brettheap Oct 6, 2026
d32be5d
Leave other people's open branches alone: rescue, and keep the remote
brettheap Oct 6, 2026
31bbfb1
Rescue before a prune, bundle what only a reflog names, self-check ea…
brettheap Oct 6, 2026
d4724a2
Run the push hooks where git-lfs is configured
brettheap Oct 6, 2026
1e583d1
Keep scratch that holds a tree or a repository
brettheap Oct 6, 2026
741b407
Check --live paths, want the writer count for own trees, stop on NOT …
brettheap Oct 6, 2026
c6ab9f8
Count --branches by origin's own ref, never by upstream or an open PR…
brettheap Oct 6, 2026
2eb5216
Remove a tmp.* only on proof, and read hidden processes as unknown
brettheap Oct 6, 2026
a6fb64b
Document what #170's fixes change in the sweep
brettheap Oct 6, 2026
7e1865a
Hold the existing cases to the register fetch, the untracked bundle a…
brettheap Oct 6, 2026
76011d9
Walk the whole estate only where a removal deletes a directory
brettheap Oct 6, 2026
08ad126
Refuse an inventory row that carries no id
brettheap Oct 6, 2026
5ba0c96
Fail closed when the bytecode check cannot read the pathspec
brettheap Oct 6, 2026
6404f1c
Document G9's id-less row and G13's failed read
brettheap Oct 6, 2026
6ef7c87
Read a .git file that is no pointer as no repository, not as unreadable
brettheap Oct 6, 2026
c550e89
Switch --yes back on: remove LANE_WORKTREES_ENABLE_YES
brettheap Oct 6, 2026
4eb9cbd
Refuse a clone's removal when a reflog directory under it cannot be l…
brettheap Oct 6, 2026
64710fb
Keep a tree whose submodule names an unpublished commit only in its r…
brettheap Oct 6, 2026
968f29d
Archive every ignored entry selected, whatever its ancestors are named
brettheap Oct 6, 2026
573a420
Never remove a cache directory that holds a repository
brettheap Oct 6, 2026
6436450
List an unmarked tmp.* that holds a repository, whatever its age
brettheap Oct 6, 2026
702c2df
Bundle a branch's reflog-only commits and self-check before --branche…
brettheap Oct 6, 2026
8fdb336
Hold back untracked files by the act's own snapshots, and keep untrac…
brettheap Oct 6, 2026
4ea93db
Read a clone's annotated tag through its peeled commit, and bundle th…
brettheap Oct 6, 2026
685cbe3
Document what #174's first Copilot round changes in the sweep
brettheap Oct 6, 2026
10b67c6
lane-end now refuses to end a lane while #162's sweep still finds its…
brettheap Oct 6, 2026
46f1178
lane-worktrees keeps its add verb under its own header ahead of the m…
brettheap Oct 6, 2026
9eb473a
Keep a tree whose git directory keeps a submodule repository with wor…
brettheap Oct 6, 2026
b266ce2
Keep a tree whose submodule has a skip-worktree or assume-unchanged e…
brettheap Oct 6, 2026
72970e2
Pass over nothing but .git when proving a removal takes no nested rep…
brettheap Oct 6, 2026
fed0e5c
Read another lane's claims in every .lane-state the whole-estate walk…
brettheap Oct 6, 2026
4fba346
Never push a lane's commits onto a branch gh still answers OPEN, what…
brettheap Oct 6, 2026
a03f5df
Never prune a gone worktree's registration whose git directory keeps …
brettheap Oct 6, 2026
de45178
Keep a clone with a local git-lfs store when its commits would go to …
brettheap Oct 6, 2026
3cdd387
Count a suite's .lock or run.sh marker as provenance only when it nam…
brettheap Oct 6, 2026
de168db
Document what #174's second Copilot round changes in the sweep
brettheap Oct 6, 2026
c538ec1
Read a submodule repository kept under a git directory as that git di…
brettheap Oct 6, 2026
77db871
Keep a tree or registration whose submodule repository has an annotat…
brettheap Oct 6, 2026
499d2ab
Read the gone worktree's kept submodule repository in its regression …
brettheap Oct 6, 2026
41b5a6a
lane-worktrees add now takes back a tree that a failing git worktree …
brettheap Oct 6, 2026
ed36551
lane-worktrees add now deletes a branch it made only while that branc…
brettheap Oct 6, 2026
c3fd2a2
Merge remote-tracking branch 'origin/main' into feat/lane-worktrees-r…
brettheap Oct 6, 2026
78492a4
Merge remote-tracking branch 'origin/main' into feat/lane-end-closeout
brettheap Oct 6, 2026
2a77d4e
lane-end's close-out gate no longer lets a FOREIGN tree row stand for…
brettheap Oct 6, 2026
2b7d91a
Adopt a tree by its Lane: trailer only when HEAD's own trailer is thi…
brettheap Oct 6, 2026
801d97d
List every tmp.* sandbox and never remove it; a bare repository is a …
brettheap Oct 6, 2026
4ea84c8
Read other lanes' claims in this sweep's estate only; with no tree, a…
brettheap Oct 6, 2026
d89a402
Merge #174's head (4ea84c8) into feat/lane-end-closeout: drop the swi…
brettheap Oct 7, 2026
49bfa40
Read a lane directory that is no repository as having no registration…
brettheap Oct 7, 2026
88b2923
A directory is an absence only with no .git entry of its own; a .git …
brettheap Oct 7, 2026
5c05ab6
Merge origin/main (#179 squash) into feat/lane-end-closeout
brettheap Oct 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 87 additions & 1 deletion docs/README-lanes.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ lane-start 2 # <repo> from the cwd's git root

lane-end openRepoShape-2 # the window is closing
lane-end openRepoShape-2 --retire # …and the lane is not coming back
lane-end openRepoShape-2 --inventory-only # …past the close-out gate (#163), trees left recorded
```

Both print every step to **stderr** and refuse with a message that names the
Expand Down Expand Up @@ -516,6 +517,42 @@ history in it to preserve. `--state-was "<text>"` named the exact text the old
two-step had to replace; it is **accepted and ignored**, and a re-run that
passes it does the same thing a re-run without it does.

**It refuses (exit 2) while anything of the lane's is still on disk** — the
close-out gate of opensoft/openRepoTools#163, asked after the in-flight guard
and before any write, `--dry-run` included. The question is #162's sweep as a
dry run, `lane-worktrees sweep <lane> --branches --include-scratch
--include-caches --dry-run --porcelain` ([its exit
contract](#the-exit-contract-lane-ends-gate-163)): **3** (something to retire)
and **2** (the sweep refused the lane, or a read failed) are a refusal that
names every tree, branch, scratch directory and cache the table gives the lane,
relays the sweep's own reason where it refused, and prints the table's command
and the `--yes` that would retire them — which removes only what is on origin
or in a bundle, never a live writer's tree. **Any other exit, or
no `lane-worktrees` beside the command, is a gate nobody read: exit 1, never a
pass.** Beside the table the gate makes two reads of its own. Every tree of the
lane's is read with `git status --porcelain --ignored`, so a tree holding only
an `.env` or a `node_modules` is named as holding them and never as clean. And
every entry directly under the lane's own root, `.lane-worktrees/<lane>/`, that
no tree, scratch or cache row names is **residue** — the ignored leftovers no
row of the sweep reaches — and it refuses even where the sweep answered 0, as
a cache row does; a root that cannot be listed is the gate unread (exit 1,
whatever the sweep answered), never an empty one. Other lanes' trees in a shared checkout are FOREIGN, are
counted for nobody, and never hold this lane — but a FOREIGN row names nothing
for the residue read either, so a standalone clone directly under the lane's own
root, which the sweep keeps FOREIGN, is residue and holds it.

`--inventory-only` is **the one door past the gate**: the lane ends although the
gate found something, refused, or could not be read, nothing on disk is touched,
and the trees stay in the lane's #97 inventory, where the daily report lists them
as an ended lane's. The reason it was let past is **written down**: the row's
closing phrase gains `ENDED WITH --inventory-only: <what the gate found>; they stay
in its #97 inventory`, and the lane's own `ENDED` (or `RETIRED`) line carries the
same words in full. It does not pass the in-flight guard — that is `--force`'s,
and a lane with both wants both. **`--sweep` is not built**: retiring the trees
in the same run as the ending is not one act, so the word is refused by name
rather than read as an unknown option; `lane-worktrees sweep <lane> --yes`
retires them first, on its own.

`lane-end --retire-dormant <repo>` is a **different act again** and ends no
lane: it is Amendment 19(c)'s sweep of the rows that have no lane left to end.
It is described under [A closed or dormant lane leaves the
Expand Down Expand Up @@ -3590,6 +3627,7 @@ touched.**
lane-worktrees sweep <lane> # the DRY RUN: the table, nothing changed
lane-worktrees sweep <lane> --yes [--live <path>|--live none]
lane-worktrees sweep <lane> --dry-run --porcelain # lane-end's gate (#163): 0 / 3 / 2
lane-worktrees add <lane> <slice> [--branch <b>] [--from <ref>] # make a tree AND record it (#163)
lane-worktrees sweep --expire [--yes] # archives past retention
```

Expand Down Expand Up @@ -3799,6 +3837,15 @@ tree <disposition> <retire|-> <path> <branch> <head> <why>
summary <to retire> <trees> <live> <foreign>
```

**`lane-end` reads it with `--branches --include-scratch --include-caches`** and
refuses on 3 and on 2 — and on 0 too where a `cache` row is printed, because a
cache under the lane's root is still the lane's to clear though never its work to
retire, or where its own read of the lane's root finds residue no row names
(see [`lane-end <lane>`](#lane-end-lane)). It counts the rows whose second field
is `retire`, plus every `cache` row; a FOREIGN tree, another lane's branch and a
`gone` record count for nothing. It passes `LANES_EDIT` through and sets
`LANES_NO_FETCH=1`, having fetched in its own reads.

Every field is escaped — a backslash is written `\\`, a TAB `\t`, a newline `\n`
and a carriage return `\r` — so a path holding any of them is still one field
of one row; bytes that are not UTF-8 pass through as the bytes they are. The
Expand All @@ -3819,7 +3866,8 @@ is 64, a `--live` path that names no tree included.
### Two protocol lines this act assumes (proposed for the amendment that ratifies it)

1. **A lane creates worktrees, never clones.** Every tree a lane works in is a
`git worktree` of the estate's checkout, recorded in its inventory (#97). A
`git worktree` of the estate's checkout, recorded in its inventory (#97) —
which `lane-worktrees add` does in one act (below). A
standalone clone under a lane root is FOREIGN to every sweep, and one that is
a shared store or a local remote is LOAD-BEARING. A lane adds no local remote
and no alternates.
Expand All @@ -3829,6 +3877,44 @@ is 64, a `--live` path that names no tree included.
goes to `${XDG_STATE_HOME:-$HOME/.local/state}/openRepoTools/lanes/<lane>/scratch/`
or the harness scratchpad.

### Making a lane's worktree — `lane-worktrees add` (#163)

**Creation records the tree, in the same act.** Nothing in this tooling made a
worktree before this verb — `lane-start` makes none, and every writer's tree was
a `git worktree add` typed into a brief — so the inventory was written only at a
handoff, and a lane that never handed off had none: lane `openRepoTools-3`'s was
empty while nine trees under its root were its own, and the sweep could not
judge them.

```sh
lane-worktrees add <lane> <slice> [--branch <name>] [--from <ref>] [--checkout <dir>] [--dry-run]
```

It makes `<parent of the lane's dir>/.lane-worktrees/<lane>/<slice>` as a `git
worktree` of the lane's recorded checkout (or `--checkout <dir>`, the root of a
checkout), on `--branch` (default: the slice) — checked out where that branch
exists, otherwise made from `--from` (default `HEAD`) **with `--no-track`**, so
a bare `git push` never aims a writer's commits at the branch it started from —
and then records it with `lanes-edit.sh set-lane-tree <lane> <path> --checkout
<dir>` (with `--writer` where the caller's session is a transcript uuid). The
path is the one line on stdout. It refuses, exit **2** and nothing made, a
managed lane or one whose ownership could not be read, a lane that records no
directory, an inventory that cannot be read, a path that already exists, a
branch name git does not take, and `--from` beside a branch that already exists.
**A record that fails after the tree is made — or a `git worktree add` that
fails after making it, as one does when a `post-checkout` hook fails — takes the
fresh tree and the branch this act made back out** (`git worktree remove`,
without `--force`, and `update-ref -d` at the commit it made) and exits 2; only
where something is left — the undo failed, a directory git does not register,
which is named and never deleted blind, or a new branch that has moved past the
commit this act started it at, which is kept so that commit stays reachable — is
the exit **1**, with the `set-lane-tree` that records it printed. Usage
is 64, and every flag of the sweep's is usage here.

A tree made any other way — a brief's own `git worktree add`, the harness's
`isolation: worktree` — is still legal git and is FOREIGN to every sweep until
`lanes-edit.sh set-lane-tree <lane> <path>` records it.

### The estate report — `sweep --all --dry-run --report`

The net under every actor that never runs `lane-end`: **creation outpacing
Expand Down
Loading
Loading