Repository navigation
openRepoTools v1.1.0
openRepoTools v1.1.0
v1.1.0 puts openRepoTools on PyPI. It is openRepoTools as it stands at 2a057a6d84e58c8acb4bda20d2b7b667b552c3fe in opensoft/openRepoTools-code, tagged on 2026-10-28. pipx install openrepotools is a second way in, beside the one-line install and a checkout's --install, and openRepoTools --version from the package names the release it runs. 60 pull requests landed between v1.0.0's commit, aaa7bc8, and this release's: 59 on main in opensoft/openRepoTools, up to 33ce26d, the commit the triad's legs were carved from, and then #1 on the code leg's main. The split itself, opensoft/openRepoTools#344, which made opensoft/openRepoTools the triad's assembly root, landed there after them and is not one of the 60.
v1.0.0 is not on PyPI, and 1.1.0 is the first version there. This release is published from opensoft/openRepoTools-code, after the triad migration (opensoft/openRepoTools#186), and its tag is made there. v1.0.0 stays in opensoft/openRepoTools, where it was made, and no tag is carried from one repository to the other.
What 1.1.0 adds
-
openRepoTools on PyPI, as
openrepotools(opensoft/openRepoTools#228). https://pypi.org/project/openrepotools/1.1.0/ is its page. Two commands install it:pipx install openrepotools openRepoTools --install
The package places one command,
openRepoTools. It runs the packagedopenRepoToolswith the firstbashon yourPATHand your arguments unchanged, and with nobashit refuses and names it. It needs Python 3.9 or newer and declares no Python dependencies. The toolset's own requirements are unchanged:bash,git, andjqfor--install.The wheel carries everything
--installplaces, the sixteen commands andrepos.tsv, the three skills and the three command files, and the openRepoShape pin, each byte for byte as the release commit holds it. SoopenRepoTools --installrun from the package reaches no network. pipx ownsopenRepoToolsin its bin directory,~/.local/binby default, as a link into its own environment, and--installleaves that one link alone: it places the other sixteen, says1 left to pipx, and writes 28 receipt rows where a full install writes 29.The version is the tag's, and no file in the repository carries a version number: the release build stamps it into the wheel.
-
--versionnames the release (opensoft/openRepoTools#228). From the package it prints five lines:openRepoTools 1.1.0 (opensoft/openRepoTools-code @ 2a057a6d84e58c8acb4bda20d2b7b667b552c3fe) version 1.1.0 tag v1.1.0 commit 2a057a6d84e58c8acb4bda20d2b7b667b552c3fe digest 31123e2d08efe36583e72956ed8b8dbbe4b968c75402b59af5b8952be83ef6d5 (sorted-ls-tree-r-v1)A package built outside a release prints
openRepoTools 0.0.0 (an unstamped package)and says so. A checkout and the one-line install print a repository and ref, as in v1.0.0, and no version number: a checkout's ref ismainby default, and the one-line install, which since the triad migration (opensoft/openRepoTools#186) runs through the assembly root's front door, names theopensoft/openRepoToolscommit it resolvedmainto. A package whose stamp is broken refuses every invocation,--helpincluded, and names the fix,pipx reinstall openrepotools. -
--installmeets each other installer by a rule (opensoft/openRepoTools#228):- pipx's link is skipped, by whichever
--installmeets it: the package's, a checkout's or the one-line install's. A checkout's or the one-line install's count line then says where the other sixteen now come from. - pip's console script at the target
openRepoTools, whichpip install --user openrepotoolsleaves, is refused in every mode before anything is placed, and the refusal names pipx as the one package route. - Any other link at a target still refuses the whole install, and no refusal advises removing pipx's link.
- After a package-mode install, a note names the first
openRepoToolson yourPATHwhen it is not the package's, so a stale bash file ahead of pipx's link is not mistaken for the release.
- pipx's link is skipped, by whichever
-
The hook pair waits for its target (opensoft/openRepoTools#237, opensoft/openRepoTools#247).
openRepoTools --installandopenRepoTools wip initadd the pair of hook entries,SessionStartand the name guard (guard), to~/.claude/settings.jsononly once~/projects/xFactory/lanes-edit.shexists, is executable and answersguard. Until then they say why, and how to complete it, and our exact guard entry is taken back out of a file that already carries it; ourSessionStartentry, which ends in|| true, and every hook of yours stay. So a workstation upgrading from v1.0.0 whose~/projects/xFactory/lanes-edit.shdoes not answerguardloses that one entry on its next--install, untilopenRepoTools wip init, orlink-estatesand--installagain, completes the pair. Before this, a fresh workstation got both entries before workspace initialization had provided that file, so the name guard could fail with a command-not-found error at every prompt. -
CI proves the package, and a release workflow publishes it (opensoft/openRepoTools#228).
tests.ymlruns on every push tomainand to everyrelease/v*branch, as well as on pull requests, nightly and by dispatch, and every run has four jobs:guard-launch-mode,tests,tests-no-submoduleandpackage. Thepackagejob builds the wheel twice at the commit's time and requires the two to be one file, checks every packaged payload file against the commit's blob in bytes and in mode, checks the wheel's.dist-info, installs the wheel with pipx, runs the unstamped--versionand an offline--installbeside pipx's link, and runs the package's tests, failing if one is skipped.release.ymlis run by hand, on an annotated tag, with the version. It is adapted from openRepoShape v1.1.0's. Its build refuses unless the ref is the version's annotated tag and the tag names the run's commit; that commit is onmainor onrelease/vX.Y; thetestpypiandpypienvironments each name a required reviewer and limit deployments tov*tags; the commit's four CI jobs ran in a push or dispatch run oftests.ymland the newest succeeded; and the tree digest computed here equals the one computed from GitHub's tree listing. It then builds twice fromgit archive, stamps the build, and requires one file.- It uploads by trusted publishing, with no token: to TestPyPI, then an install check on Ubuntu, on macOS (run again under macOS's own bash 3.2) and on Ubuntu with Python 3.9, then to PyPI. Each upload waits for Brett Heap's approval in its environment. A pre-release
X.Y.ZrcNreaches TestPyPI only. Onlyopensoft/openRepoTools-codemay publish a final version, and nothing uploads to PyPI fromopensoft/openRepoTools.
-
Releases are annotated tags (opensoft/openRepoTools#228). A release is an annotated tag
vX.Y.Zon a commit onmainor onrelease/vX.Y, never a branch and never a version number committed to a file, and a tag is never moved or reused.release/vX.Yis cut atvX.Y.0and carries the patch tags. A fix lands onmainfirst, then reaches the release branch by a fast-forward, a merge, or a pull request ofgit cherry-pick -x. A version is uploaded once, and a bad upload is fixed by the next patch. -
The lane tools name what they could not read in a triad's legs (opensoft/openRepoTools#255, opensoft/openRepoTools#271). It closes two gaps in opensoft/openRepoTools#246, which taught the lane tools to look inside a triad's legs; each broke that change's rule that a read that failed is never an empty one.
lane-handoffnames every tree-inventory record that is not a whole schema-1 one in its NOT POLLED note, and the lane stays SWAPPING. A leg directory this account cannot search is unreadable and neverabsent:lane-worktrees sweepexits 3,lane-endrefuses over it,lane-reconcilegives it anunreadablerow, andlane-handoffwithholds its tree. A dependency nested in a leg is unreadable to the sweep too where this account cannot search its directory, or a directory above it inside the leg: the sweep exits 3, andlane-endrefuses over it. A leg directory that is not there at all is stillabsent. A known gap remains (opensoft/openRepoTools#272): the sweep also reads a declared leg beneath a directory this account cannot search asabsent, solane-enddoes not hold on it. These readers run only in an assembly, a checkout whoseproject.yamldeclares a leg, so a checkout with noproject.yamlsees no change. -
lane-handoffreads the lane's lifecycle whole (opensoft/openRepoTools#280, opensoft/openRepoTools#283). It used to pipelanes-edit.sh lane-stateinto anawkthat exits as soon as it has printed the one field it wants. Underpipefail, alane-statethat wrote again after that died of SIGPIPE, so now and then the read exited 141, the handoff said the lifecycle could not be read, and it recorded no RUNNING to SWAPPING to SWAPPED transition. That failed safe, but whether it happened depended on timing alone, and it could turn a pull request'stestsjob red at random. The handoff now takeslane-state's whole output first and parses it afterwards, so its status islane-state's own. The read of the operation of a lane that is still SWAPPING, which only names that operation in a note, is made the same way.
What else changed since v1.0.0
- The openRepoShape pin moved. This release pins openRepoShape at v1.1.0,
190133don itsmain, the commit its tests were verified against (opensoft/openRepoTools#233). v1.0.0 pinned7f84ca4. - The estate commands (
park,resumeandstatus):resumeandstatushold each leg to the branch bootstrap puts it on: the leg row's ownbranch:, else the root'stracking_branch(opensoft/openRepoTools#238).park,resumeandstatusfind a family folder named for its repository or for its display name, andresumenames the holder's repository fromfamily.yamlwhere it is on disk (opensoft/openRepoTools#240).park's not-carried report counts a leg's unpushed commits on the branch its own row declares, else the root'stracking_branch(opensoft/openRepoTools#249).family.yamlvalues are read the way openRepoShape v1.1.0 reads them,statusnames one family laid out twice, andresumefinds a holder by its id when its record names another folder (opensoft/openRepoTools#251).
- The register and
lanes-edit.sh:swappednames a lane by the register row's spelling, not the log file's (opensoft/openRepoTools#201).superseded_byfollows a chain of TAKEOVERs to its last link, so one lane holds the object (opensoft/openRepoTools#204).- The lane listing reads a row's state phrase when its log is missing, so a migrated PAUSED row stops showing NO LOG (opensoft/openRepoTools#206).
managed-projectionreads this checkout's row as the writers do,lane-treestells an unlistable trees directory from no inventory, and the manual's seven-column row is qualified (opensoft/openRepoTools#213).lanes-edit.sh commitwith nothing staged still publishes the local commits that origin lacks, when each of them only adds or changes a register or handoff file, so the recovery's re-run ofcommitpushes the edit it stranded, and the conflict recovery warns thatreset --harddrops a peer's commits (opensoft/openRepoTools#214).- A one-line record cannot take a newline:
append-lineandlane-handoff's why refuse it before writing, and the deferredlane-startcommand keeps--agent(opensoft/openRepoTools#218). lanes-indexfollow-ups:exportreads one snapshot,history --indexsays its closing stderr count, a foreign source is refused before the lock, a damaged table reads as unusable, an unread lane stays NOT READ in the index, and the worktrees footer names the workstation (opensoft/openRepoTools#219).- A register that could not be read no longer reads as a lane with no session ids, so the dead-lane
--forcecheck andduplicate-holderfail closed (opensoft/openRepoTools#221). - Register sweeps and reads refuse what they could not read:
retire-rows,archive-rows,migrate-state-cells, the DORMANT class,next-freeandadd-row(opensoft/openRepoTools#230). - Lifecycle reads that failed are no longer answers,
set-lane-tree's fence closes with its operation, and a lane sidecar is valid only when whole (opensoft/openRepoTools#252). - The register's rewrites refuse a copy or write they cannot prove and put a cut-short register back,
retire-rowsrefuses an unpushed log rename before it writes and calls a lane closed only from its published log, andadd-rowrefuses an archive row it cannot read and takes back a row refused after its rebase (opensoft/openRepoTools#263). - A lane listing that is not whole no longer removes indexed worktree rows: the inventory read says PARTIAL,
lanes --worktreesprints it NOT WHOLE, andlanes-indexwrites nothing over it; an older helper's tree with no path stays a tree (opensoft/openRepoTools#289).
lane,lane-start,lane-end, the name guard and their helpers:lane-end's synopsis says what a DUPLICATE retire does, a state cell that breaks the phrase's grammar is read whole instead of narrowed, and the TAKEOVER grammar table and twotests.ymlheader comments are corrected (opensoft/openRepoTools#202).lane-start's transcript follow counts a holder only whentranscript-holdersverifies it, not onkill -0(opensoft/openRepoTools#203).duplicate-holderandlane-end --retireread a--resumepath whole and act on every verified child, not the first (opensoft/openRepoTools#205).- The name guard and the SessionStart hook name an unreadable register or binding log instead of guessing a branch (opensoft/openRepoTools#208).
lanesfilterslanes-edit.sh's no-fetch notice out of its stderr with agrepthat now takes its pattern as-e's argument, so under macOS's BSDgrepthe--after the pattern ends the options and is no longer read as a file name, and every other line is printed aslanes-edit.shwrote it, with nogrep: --: No such file or directoryerror and no temporary file's name in front; the suite'srepoG#8assertion can now fail, and the bash files are no longer counted in prose (opensoft/openRepoTools#209).- Three wrapper reads that failed no longer read as no answer:
lane's window-name check asks the unpushed local row too,lanes' hidden-row probe reads its whole stream, andlane-end --retire-dormantrefuses without its stderr capture (opensoft/openRepoTools#210). claude-currentfalls back from a native Claude Code that does not run to the next one that does, andclaude-restart-checkrecognises a running native in a customCLAUDE_CURRENT_NATIVE_DIR, symlinked included (opensoft/openRepoTools#211).lane-startfails closed on three fences: a new lane's rename is read back and undone when another window took the name, a row or log that says RETIRED is not started again, and a new lane never takes a window named for another registered lane (opensoft/openRepoTools#216).laneandlane-handoffrefuse empty and flag-shaped flag values, andlane-handoffrenames the window only once the handoff can no longer be refused (opensoft/openRepoTools#220).- A silent tmux is no longer read as a dead window, the companion verdict is read off the lane's live window from any window, and the guard's cure for a lane named before Rule 4 says what it cannot fill in (opensoft/openRepoTools#222).
lane-startgains a guard-only binding mode that stamps the transcript being bound, so the offer's yes no longer records a RESUMED nobody is in (opensoft/openRepoTools#223).- Under
set -e,lanes --worktrees,openRepoTools wip initandlane-endno longer end on macOS's bash 3.2 where a utility run throughcommandfails, and a scan of the shipped scripts keeps that shape out (opensoft/openRepoTools#294).
- The handoff and its skill:
- The handoff skill commits the handoff in the checkout it was published in, by its own path, and refuses foreign staged entries and in-progress operations (opensoft/openRepoTools#199).
- Every shell block in the handoff skill parses, and the skill stops calling
/renamethe only act there is (opensoft/openRepoTools#200). - The handoff skill resolves
lanes-edit.shonPATHfirst, its comments no longer promise write (c) without a workstation, and it refuses the object-log and register-line writes for a lane that has never had a session recorded (opensoft/openRepoTools#207). rename-lane's Rule 3 handoff stamp is published through a same-directory temporary file and one atomic rename, so a writer killed mid-stamp leaves the handoff whole (opensoft/openRepoTools#212).- The handoff skill's step-1 fence refuses Amendment 15(d)'s exit 2 instead of reading it as no answer, and its window ref is checked as a whole string (opensoft/openRepoTools#260).
- The handoff skill's two PAUSED writes carry the why it computed, sanitized as
lane-handoffsanitizes it, and a why carrying a line break is refused before step 1 (opensoft/openRepoTools#264).
- The lane tools look inside a triad's legs and paired feature trees, so the sweep,
lane-end's gate,lane-handoff,lane-reconcileand the daily report can see a lane's work there, and a checkout with noproject.yamlis treated as before (opensoft/openRepoTools#246). - The test suite and its checks:
- Follow-ups to the test roots of opensoft/openRepoTools#190: a refused root's value is named, the pin's commit is checked, paired feature worktrees are found, redirected roots show under
-q, the lane manual's sections run, and the installer-source test is restated (opensoft/openRepoTools#195). - Clause (c)'s TAIL case is read on a checkout at a short fixed path, and
lane-start's row stamp is driven at three checkout lengths, so the case no longer answers by the length of the run root (opensoft/openRepoTools#215). - The shell suite's wrapper stops the suite's process group on a timeout, an interrupt, TERM or hangup, the launcher precedence of
lane <name>is pinned with launchers the suite names, and the pty driver's comment says what it counts (opensoft/openRepoTools#217). - The hygiene check refuses host paths under the bench root, with an allowance for quoted
greppatterns, and the consumer reads and the durable-intent sample no longer carry them (opensoft/openRepoTools#229). tests/run.shrefuses to start when it cannot record its lock's owner, and its cleanup removes only a lock that is still its own (opensoft/openRepoTools#232).- The hygiene check's end-of-options rule splits a line only outside quotes, and the suite stops counting its bash files (opensoft/openRepoTools#239).
- The hygiene check reads every exit-3 die of
lanes-edit.shagainst its cause and both documented rows, so a die that claims nothing was pushed, or a fourth die no row documents, fails it (opensoft/openRepoTools#259). - The shell suite times each of its sections and its exit-trap teardown, and prints both in its transcript and in CI's step summary (opensoft/openRepoTools#261).
- The shell suite's wrapper prints the suite's timing table and teardown line on a pass, where CI's log can read them (opensoft/openRepoTools#265).
- CI and the release workflow run on
ubuntu-24.04, becauseubuntu-latestis now Ubuntu 26.04, where setup-python has no Python 3.9 and the TERM-ignoring kill test fails: every Ubuntu runner intests.ymlandrelease.ymlis pinned to it, the tests and the 006 contract lines that name the label follow, andmacos-latestis unchanged (opensoft/openRepoTools#296).
- Follow-ups to the test roots of opensoft/openRepoTools#190: a refused root's value is named, the pin's commit is checked, paired feature worktrees are found, redirected roots show under
- The triad migration's records:
- The migration's evidence of 2026-10-08 and 2026-10-09 is recorded: test roots and CI patches, the installer entry point, restore and rollback, opensoft/openRepoTools#192's corrections, T018 and the Gate B status (opensoft/openRepoTools#196).
- T006's
assembly-0001and T010's code-legtests.ymlpatch are re-cut against the files that opensoft/openRepoTools#228's series leaves, T009's assembly-root length-caps patch is added, and lines 17 and 33 of the Gate B walk are marked PARTLY until they are reviewed, as part of opensoft/openRepoTools#186 (opensoft/openRepoTools#266). - T010's patch for the assembly's
exact-pinsjob is regenerated so that the job runs onubuntu-24.04, like the code leg's jobs under opensoft/openRepoTools#296, and the Gate B documents name that runner and the patchedtests.yml's job lines as opensoft/openRepoTools#296 leaves them, as part of opensoft/openRepoTools#186 (opensoft/openRepoTools#299). - After the split, the code leg gets its own front door, the shape's leg
AGENTS.md,CLAUDE.mdandREADME.md, with the LF rule in.gitattributes, an ignore list and aLICENSE, which the split had left in the root alone; itstests.ymlnames the standalone checkout it runs in and changes no job, trigger orrun:line; the suite's line caps on an assembly root'sAGENTS.mdandREADME.mdbecome 354 and 529, the re-cutassembly-0001's own counts; and the shell suite'slane-startrow-stamp cases no longer answer by the length oftests/run.sh'sTMPDIR, for opensoft/openRepoTools#304, as part of opensoft/openRepoTools#186 (#1).
- These release notes say that the fixes for opensoft/openRepoTools#255 and opensoft/openRepoTools#280 are part of 1.1.0, and list by area what else changed on
mainsince v1.0.0 (opensoft/openRepoTools#293).
Install and upgrade
-
From PyPI:
pipx install openrepotools, orpipx install openrepotools==1.1.0for this release, thenopenRepoTools --install.pipxitself comes fromsudo apt-get install -y pipxon Debian and Ubuntu andbrew install pipxon macOS; then runpipx ensurepathand open a new terminal. The package is tested against the layouts of pipx 1.11.1, 1.17.8 and 1.18.3.openRepoTools --versionprints the five lines above. -
From the one-line install to pipx: run
pipx install --force openrepotools openRepoTools --install
--forceis needed. The one-line install left a regular file at~/.local/bin/openRepoTools, and a plainpipx install openrepotoolsmeets it: pipx only warns that the file exists and that it is not modifying it, places no link, and exits 0. The bash file stays at that path and keeps running, so--versionstill names a repository and ref and not a release, and its next--installfinds no payload beside it and fetchesmain.--forceputs pipx's link in the file's place. ThenopenRepoTools --installskips that link and replaces the other sixteen files with the package's. On a workstation with noopenRepoToolsyet,pipx install openrepotoolsis enough. -
Upgrade:
pipx upgrade openrepotools, thenopenRepoTools --installagain. Until that second command, the placed files, skills, command files and hook entries stay the earlier release's, and the link is skipped again. -
Remove:
pipx uninstall openrepotoolsremoves only the command and its environment. The placed files, the skills, the command files, the hook entries and the receipt stay, and a checkout's--installthen places a regularopenRepoToolsat the freed path. -
One install route or the other, never both. Until opensoft/openBenches#165 lets workBenches'
scripts/setup-estate-commands.shaccept pipx's link, a workstation uses pipx's route or workBenches'./setup.sh../setup.shrefuses a symlinkedopenRepoToolsbefore it installs anything, and after installing it requires a regular file identical to its vendored copy, so on a workstation with pipx's link every./setup.shrefuses its estate step and names the link. This is Brett Heap's option (c), of 2026-10-18. State of opensoft/openBenches#165 at the tag: open, and not landed, as read on 2026-10-28. -
The one-line install and a checkout work as before, and their
--versionstill prints a repository and ref and no release: a checkout's ref ismainby default, and the one-line install's is now theopensoft/openRepoToolscommit that the assembly root's front door resolvedmainto. -
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-10-28T21:14:02Z, "yes, tag v1.1.0 at 2a057a6 and dispatch it").
Full list
- The
v1.1.0milestone: https://github.com/opensoft/openRepoTools/milestone/1 - The package series of opensoft/openRepoTools#228, which the items credited to it above describe: opensoft/openRepoTools#262, opensoft/openRepoTools#267, opensoft/openRepoTools#268, opensoft/openRepoTools#269, opensoft/openRepoTools#270 and opensoft/openRepoTools#284.
These notes are docs/release-notes/v1.1.0.md in opensoft/openRepoTools-spec.