Repository navigation
openRepoTools v1.2.0
v1.2.0 makes every register write fetch before it edits and confirm on origin after it pushes, writes a lane's handoff by that same act, and stops the lane tools from losing a lane they could not read whole. It is openRepoTools as it stands at ecd1bce7b1079ef81215d6166b6db9ee7d481adf in opensoft/openRepoTools-code, tagged on 2026-11-10. 24 pull requests landed on the code leg's main between v1.1.0's commit, 2a057a6d, and this release's: #2 to #25, the last of which made that main ecd1bce. 11 more landed on the spec leg's main, in opensoft/openRepoTools-spec, after v1.1.0's notes: opensoft/openRepoTools-spec#6 to opensoft/openRepoTools-spec#18, all but opensoft/openRepoTools-spec#14 and opensoft/openRepoTools-spec#17, which are open.
v1.2.0 is the second version on PyPI, after 1.1.0. It is published from opensoft/openRepoTools-code, as 1.1.0 was, and its tag is made there. Feature 007, the SQL register (opensoft/openRepoTools#165), is not part of it: on Brett Heap's word of 2026-11-10 it moves to the v1.3.0 milestone, and no part of it ships here.
What 1.2.0 adds
- Every register write fetches first, and says
pushedonly once origin has it (opensoft/openRepoTools#198; #17 with opensoft/openRepoTools-spec#15). This is act 2 of lane-collision-protocol Amendment 23. Eachlanes-edit.shwriter, routed or not, now fetches and fast-forwards its checkout under the checkout's lock before it reads the file it edits. Where the checkout cannot be brought to origin's tip, it refuses with exit 2, nothing written, and names the checkout and its recovery: a fetch that failed,LANES_NO_FETCH=1among them; an operation in progress or unmerged entries; HEAD off the branch; staged entries that are not the write's own; unpushed commits; a diverged branch; a pending change to a path the write commits; or a fast-forward git refused. The write then commits by pathspec, pushes with no pull, and reads origin back.pushed origin/<branch> (attempt N)and exit 0 now mean that origin holds the commit, and a push that went, or timed out, and cannot be confirmed is exit 3,pushed, not confirmed: <sha>, never success. A push refused because origin moved replays the one edit on origin's new tip, with the verb's own checks run again, aclaim's rescan among them, up to six attempts in all. At that bound, or where a push is refused with origin unmoved, the commit is kept on HEAD and named, with exit 6, and every later write from that checkout refuses until the lane publishes or archives it.lanes-edit.sh commitwith nothing staged is the publish act: it sends the commits origin lacks by a fast-forward push only, and refuses (2) a stack that origin has moved past, naming the archive and thereset --keepthat settle it. - A lane's handoff is written by one verb, under the lock (opensoft/openRepoTools#198; #18 with opensoft/openRepoTools-spec#16, #20 and #19).
lanes-edit.sh write-handoff <lane>writes a lane's handoff in the checkout its writes go through, as a Rule 3 stamp (--stamp), a new top block (--prepend) or a whole file (--whole), under that checkout's lock and with the fetch, the publish, the read-back and the replay that every other write has. With--prependit proves that the block and the old file both survive before it replaces the file.lanes-edit.sh handoff-path <lane> [--create]says where that file is.lane-startwrites its Rule 3 stamp, andlane-handoffits PAUSED block, each with onewrite-handoffcall, and the handoff skill writes its new top block with onewrite-handoff --prependcall, so none of the three commits, pulls or pushes on its own any more. After its first write,lane-startrefuses a launch only at a restart's own fences; where the stamp could not be written it says so and prints thewrite-handoff --stampcall that writes it. A launch reads only the filehandoff-pathnames, and where that file is not there it goes on with no first prompt and names the file. Wherelane-handoffor the skill could not write the top block, the block is kept and printed with the onewrite-handoff --prependcall that puts it in. UnderLANES_NO_GIT=1,lane-startandlane-handoffsay that the handoff is written and nothing is committed or pushed. - Each lane can write through its own clone of the register, by opt-in (opensoft/openRepoTools#198; #16 and #20). W is the checkout of the register that
workspace.yaml'spath:names. WithLANES_ROUTE_WRITES=1and noLANES_REPO, every write goes through the lane's own clone,<W>-lanes/<lane>/, made on first use, while reads still read W. W then takes no commit: aLANES_REPOthat names W refuses every write (2), and W moves only bylanes-edit.sh refresh-workspace, a fast-forward, whichlane-startruns before and after its writes.lanes-edit.sh lane-checkout <lane> [--create]names a lane's clone, or makes it.lane-worktrees' report lists<W>-lanes/<lane>under "Register checkouts (Amendment 23 S1)", with what each holds that origin lacks, and never under FOREIGN. WithoutLANES_ROUTE_WRITES=1, which is how this release ships, every write goes throughLANES_REPO, W by default, and every lock is that checkout's, as in 1.1.0. lane-end --sweep-checkoutsremoves the clones nothing holds (opensoft/openRepoTools#198; #21). It lists the clones under<W>-lanes/that CLOSED lanes left, and the clones of the register beside W, and it is a dry run by default: exit 3 where something is removable, and 0 where nothing is. With--yes, run as the laneLANES_LANEnames, it removes only those nothing holds, each decided again under its own lock after a fetch. W, a live lane's clone, the runner's own, the one a caller'sLANES_REPOnames and a symbolic link are never candidates. Work origin lacks, uncommitted or untracked files, a stash, an operation in progress, a repository that borrows the clone's objects, a linked worktree, a process working inside it or naming it inLANES_REPO, and a file system mounted in it each hold a clone. What it removed is recorded in oneNOTEDline in the runner's own log, and exit 1 is a read that failed, or a removal that did not finish or was not recorded. It refuses (2), the dry run too, from a window whoseLANES_WORKSPACE_ROOTnames a lane's own checkout, and--yesrefuses (2) while that variable names any directory butworkspace.yaml'spath:: run it with the variable unset.- The lane tools no longer lose a lane they could not read whole (the five issues of the
v1.2.0milestone, carried to the legs as part of opensoft/openRepoTools#186):- A lane whose recorded directory holds a space is no longer left out of
worktrees --all, ofworktrees <lane>and of the inventory index:worktree_rowshands the directory on quoted where it holds a space, aslane-startwrites it (opensoft/openRepoTools#295, #3). - A lane whose recorded directory holds a TAB or a backslash is listed, indexed and reached as recorded. The listing's directory column is escaped as
lane-worktreesescapes a porcelain field, andlanes,laneandworktreesdecode it. Every read of a stored sub-field gives back what was written, becausepayload_subfieldno longer passes it throughawk -v. Such a lane's lifecycle and trees are now looked for beside its checkout, and records the old read filed under$PROJECTS_ROOT/.lane-stateor a sibling directory stay there, unread, with no migration. The binding and pause checks judge the text that is written and not itsawk -vexpansion, sohost a\101bis no longer passed asaAb, andrequest-handoffrefuses a requesting host or container outside letters, digits,.,_,-and:, on the request and--forcepaths and on either one's--dry-run, with exit 2 and "Nothing was written" (opensoft/openRepoTools#301, #4 with opensoft/openRepoTools-spec#7). - A lane's control root, or a directory above it, that this account cannot search is read as not read, not as no trees:
lane-stateandlane-treesexit 10, not 8, and say why,worktreesnames the laneUNREADso thatlanes-index synckeeps its rows,lane-reconcileisindeterminate, and a write under such a root fails instead of landing under$PROJECTS_ROOT(opensoft/openRepoTools#291, #5 with opensoft/openRepoTools-spec#8). - A tree record that names no absolute path no longer swaps its tree's row in the index for one keyed by the record's id:
worktreesnames the laneUNREAD, solanes-index syncandreconcile --source inventorykeep the lane's rows and say why, and one such record holds every row of its lane until it is put right (opensoft/openRepoTools#302, #6 with opensoft/openRepoTools-spec#9). - A named
worktrees <lane>read saysUNREAD, with exit 0, where the lane's log or the register could not be read, where it used to say none (8). Thelaneslisting says what it could not read and exits 1 where it names no lane for that reason, a tree's writer is unknown and not0where the lane's binding was not read, andlane-stateexits 10, not 8, over a log it could not read, solanes-indexholds that lane's rows (opensoft/openRepoTools#290, #7 with opensoft/openRepoTools-spec#10). A known gap remains (opensoft/openRepoTools#303): most other reads of a lane's log still cannot tell a failed read from a lane with no log.
- A lane whose recorded directory holds a space is no longer left out of
What else changed since v1.1.0
- The openRepoShape pin moved. This release pins openRepoShape at v1.1.1,
5017830on itsmain, where 1.1.0 pinned v1.1.0,190133d. Four of the eight files the suite mounts from it changed, and no test expectation moved (#15). - The lane tools' other fixes:
lanes-edit.sh claim --forceposts a takeover's GitHub comment that opens as Rule 1 spells it,TAKEOVER, a dash and then the taker's lane, where it ranTAKEOVERinto the name of the lane taken over from, sogh_reads, which looks for that opening, now finds a takeover comment (opensoft/openRepoTools#346, #2).duplicate-holderno longer reports the lane's own process tree as a duplicate pair where the lane's live pid is a wrapper's child, solane-end --retireon that wrapper exits 8 and ends nothing, where it said to retire "the OTHER pid". Where a second holder runs under the same wrapper, the read exits 1 naming the wrapper, the lane's own pid and that holder, andlane-endrelays its words.lane-end --retirepasses over a helper too old forduplicate-holderand goes on to the session records, and where it skipped aduplicate-holderortranscript-holdersread it no longer says that nothing of any kind is live: it dies 1, saying that a read that did not run is not "nothing". Both of these paths are defensive, since nolanes-edit.shthat has shipped lacks either verb and passeslane-end's seam check. The suite's fakepgrepandpscan now fail, so the branches that read a failingpgreporpsas a read that failed are tested (opensoft/openRepoTools#109 to opensoft/openRepoTools#112, #25).
- The release workflow and its checks, follow-ups to opensoft/openRepoTools#228:
- Both wheel checks hold a payload file to the commit's whole mode, 100755 or 100644.
release.yml's wheel and index checks refuse with a named::error::line instead of a traceback, a member stored as LZMA with corrupt data included, and the macOS rerun places the sixteen commands under bash 3.2 in a bin directory of its own, then runs--installthere a second time (opensoft/openRepoTools#278, #12 with opensoft/openRepoTools-spec#11). - Six checks of
tests.yml'spackagejob can no longer be removed with the suite green:tests/test_tests_workflow.pyruns its wheel check on four more wheels and its step 10 whole, and holds the pipx lock'stomlientry equal to the build lock's (opensoft/openRepoTools#275, #13 with opensoft/openRepoTools-spec#12). release.yml's RELEASING header says that no job reads more of Brett Heap's release acts than the required reviewer and the deployment limit, and that the build checks only that a required reviewer is named; it names the tag ruleset by its rule and by this repository's own name, andtests/test_release_workflow.pypins those sentences. The spec and the OpenSpec proposal say that only the admin role creates, moves or deletes av*tag (opensoft/openRepoTools#279, #14 with opensoft/openRepoTools-spec#13).release.yml's wheel check refuses, with a named::error::line, a directory entry the wheel may not hold, a wheel that declares more than 256 MiB uncompressed and a member stored in any method but STORED or DEFLATED; it hashes each member in 1 MiB chunks as it reads it; and its four index checks end a too-deep JSON answer with their named refusal instead of a traceback. A wheel built by the pinned hatchling passes (items 1 to 3 of opensoft/openRepoTools#352, #23).tests/test_release_workflow.pyruns the wheel check's whole step on a rehearsal's branch and on the version's tag, so aTAGtaken from the ref's name, or aREPOSITORYhard-coded to the code leg, fails the suite (opensoft/openRepoTools#285, #8).- It makes a served-digest read fail with a
URLErrorthat has no HTTP status, and another time out, sorelease.yml's retry cannot lose either with the suite green (opensoft/openRepoTools#286, #9). - It runs the ref step on a tag in a checkout that holds four commits and requires it to leave them whole, so a
--depthon its fetch cannot pass the suite (opensoft/openRepoTools#287, #10). - It runs the stamp writer and the steps after it through the wheel check, so a step that rewrites the stamp is refused by the wheel check and not only counted by the step-order pin (opensoft/openRepoTools#288, #11).
- Both wheel checks hold a payload file to the commit's whole mode, 100755 or 100644.
- CI runs the suite as shards (opensoft/openRepoTools#77, #22 with opensoft/openRepoTools-spec#18).
testsandtests-no-submoduleare now gates over matrices of shard jobs, four pytest shards and a shell shard fortestsand three and a shell shard fortests-no-submodule, each shard timing out at 60 minutes. The four required check names stay; a gate is never skipped, and it fails unless every shard passed and the shards' test ids equal the full collection, id for id. Runs onmainsince it landed take 28 to 38 minutes, the release commit's own 38, against 76 to 90 for the runs before it on 2026-11-09. The 006 workflows contract describestests.ymlas it now is. - The test suite: the inventory tests'
normtakes the sandbox root out before the tree-id and pid rules, so a run root with ac<digits>-segment no longer fails two tests (opensoft/openRepoTools#334, #24). - The triad migration's records: rehearsal D's record, the Gate B walk at rehearsal D and its plan, Plan-D, are carried into the spec leg as their final bytes, with Plan-D beside run C's plan, as part of opensoft/openRepoTools#186 (opensoft/openRepoTools-spec#6).
Install and upgrade
-
From PyPI: https://pypi.org/project/openrepotools/1.2.0/ is this release's page.
pipx install openrepotools, orpipx install openrepotools==1.2.0for this release, thenopenRepoTools --install, as for 1.1.0, whose notes say wherepipxcomes from and how to move to it from the one-line install.openRepoTools --versionfrom the package prints five lines:openRepoTools 1.2.0 (opensoft/openRepoTools-code @ ecd1bce7b1079ef81215d6166b6db9ee7d481adf) version 1.2.0 tag v1.2.0 commit ecd1bce7b1079ef81215d6166b6db9ee7d481adf digest f44ef8716263a7d0f8ea3e2f200eca398b178ee6c362e00ef41ed156c1d4280f (sorted-ls-tree-r-v1) -
Upgrade from 1.1.0:
pipx upgrade openrepotools, thenopenRepoTools --installagain. Until that second command, the placed files, skills, command files and hook entries stay 1.1.0's. Run both: this release's handoff skill callslanes-edit.sh handoff-pathandwrite-handoff, which 1.1.0'slanes-edit.shdoes not have.openRepoToolsitself is unchanged since 1.1.0, so--install, its count line, its receipt and the hook pair behave as they did. -
A writer no longer pulls. What a script or a person that calls
lanes-edit.shmay meet (#17):LANES_NO_FETCH=1makes every writer refuse (2), with nothing written, because a write must fetch first. Unset it for writes.- Exit 3 has one meaning now,
pushed, not confirmed: <sha>. Its old causes, a rebase conflict and six attempts blocked by a peer's uncommitted file, went with the pull, and a push that timed out is 3 only where the read-back cannot confirm it. A write that runs out of attempts is exit 6, its commit kept on HEAD and named, so 6 no longer means only thatgit add,commitorpushfailed. Exit 0 for a write means that origin holds the commit. - Unpublished commits in a checkout make every later write there refuse (2) until
lanes-edit.sh commit, with nothing staged, publishes them or they are archived. A hand edit of the register is published by its own act,LANES_LANE=<lane> lanes-edit.sh commit "<what the edit is>". --sweepis withdrawn: every verb but the two hooks refuses it (2), and--no-sweepis accepted and changes nothing.- No refusal tells you to
git pull --rebasefirst any more.
-
Exit codes that changed in the lane tools:
lane-stateandlane-trees: 10, not 8, where the control root or a directory above it cannot be searched, andlane-statealso where the lane's log could not be read (opensoft/openRepoTools#291, opensoft/openRepoTools#290).lanes-edit.sh worktrees <lane>: 0 with anUNREADline, not 8, where the lane's log or the register could not be read (opensoft/openRepoTools#290).lanes-edit.sh lanes: 1, not 8, where it names no lane because the register, the archive or the list of the logs could not be read, and thelanescommand refuses on it (opensoft/openRepoTools#290).lanes-edit.sh request-handoff: 2, with nothing written, for a requesting host or container outside its alphabet (opensoft/openRepoTools#301).lane-end --retire: 8, not 2, where the only pairduplicate-holderfound was the lane's own process under its wrapper, and 1, with the helper's words, where a second holder runs under that wrapper (opensoft/openRepoTools#111).
-
Write routing stays off in 1.2.0. Leave
LANES_ROUTE_WRITESunset: turning it on by default is opensoft/openRepoTools#356. Do not setLANES_ROUTE_WRITES=1while lane windows exportLANES_WORKSPACE_ROOTnaming the lane's own checkout, or every write refuses with 2. Keep the windows'LANES_REPOandLANES_WORKSPACE_ROOTexports as they are: with routing off, a write goes through the checkoutLANES_REPOnames. Install the helper,lanes-edit.sh, and the handoff skill together;openRepoTools --installplaces both. A workstation whose shared checkout, W, holds commits origin lacks repairs it first, by hand, as Amendment 23's S6.5 sets out, because a writer withLANES_REPOunset writes through W, and over those commits it refuses with 2 and writes nothing. -
The per-lane clones are opt-in.
LANES_ROUTE_WRITES=1turns them on. Each writing lane then has its own clone of the register, about 0.4 GB each as #17 gives it, andlane-end --sweep-checkoutsremoves the ones CLOSED lanes leave. Under it, a 1.1.0 helper still running beside this release's takes W's mutex for the lifecycle, restart and tree acts where this one takes the workstation's lock, so the two do not serialise those acts with each other; the fence in each act stays the check (#16). -
One install route or the other, never both, as for 1.1.0: until opensoft/openBenches#165 lets workBenches'
scripts/setup-estate-commands.shaccept pipx's link, a workstation uses pipx's route or workBenches'./setup.sh, as 1.1.0's notes describe. State of opensoft/openBenches#165 at the tag: open, and not landed, as read on 2026-11-10. -
The one-line install and a checkout work as before, and their
--versionstill prints a repository and ref and no release. They take the code leg at the commit the assembly root pins. Since opensoft/openRepoTools#357 that isecd1bce, the commit this release was built from, or a later one, so they carry this release. -
Windows is reached through WSL2, as for every bash command here. The package has no native Windows command, and it is a wheel only, with no source distribution.
Tagged on Brett Heap's word (2026-11-10T16:51:42Z, "yes, tag v1.2.0 at ecd1bce and dispatch it").
Full list
- The
v1.2.0milestone: https://github.com/opensoft/openRepoTools/milestone/2 - Act 2 of Amendment 23, which the items credited to opensoft/openRepoTools#198 above describe, in landing order: #16 (slice 1a), #17 and opensoft/openRepoTools-spec#15 (1b), #18 and opensoft/openRepoTools-spec#16 (2a), #20 (2b), #19 (3) and #21 (4).
- The release follow-ups to opensoft/openRepoTools#228, 27 cherry-picks in all: #8 to #14 and opensoft/openRepoTools-spec#11 to opensoft/openRepoTools-spec#13.
These notes are docs/release-notes/v1.2.0.md in opensoft/openRepoTools-spec.