Repository navigation
Tracking: the test suite, CI and the docs #461
Description
Lane: openreposhape-3 (openRepoShape-3)
The parent issue for one area of openRepoShape: the test suite, the three-OS CI gate, and the docs that state what the tools do. Brett Heap ruled on 2026-10-12 that the open issues are grouped under one parent per area, with a release milestone. Every open issue in this area is a sub-issue here.
v1.0.0. The first release is "core flows, all OSes": every procedure in AGENTS.md works end to end for ordinary repositories on Linux, macOS and Windows. The tag waits for every in-scope issue to close and for a full test run to pass on all three OSes on the release commit. In this area, the sub-issues in the v1.0.0 milestone are:
- tests/test_shape_doctor.py, tests/conftest.py and setup-project.py: a clone of a just-pushed
--local-remote-dirbare repository is git's local clone, which links or copies each loose object file while git's background maintenance can pack and delete them, so on the macOS runner it diesfailed to copy file to ... No such file or directory, every doctor test errors at setup, andsetup.shrefuses after creating all three repositories #440 (P1): macOS CI: git's local clone of a just-pushed bare remote races background maintenance; three failures, two in 90 minutes, each a 20-minute 3-OS rerun. - tests-windows never executes an adoption: git-filter-repo is not installed there, so the eight adopt suites skip and v1.0.0's "adopt works on Windows" is unverified #471 (release gate):
tests-windowsnever executes an adoption (nogit-filter-repo), so "adopt works on Windows" is unverified. Proposed holder: lane 4. - v1.0.0 release notes: nothing in the repository says what the release covers or lists its known limits #472 (release gate): the v1.0.0 release notes, listing the known limits. Proposed holder: lane 4.
Known limits. Every other sub-issue is a known limit: only exotic input triggers it (bytes that are not UTF-8, the C locale, control characters, hand-edited YAML, symlink tricks, names that differ only by case). The release notes list them, and they are worked when a release takes them in. A new edge case that review finds goes on this checklist, not into a new issue, unless it is in a release's scope or would hit a real user (Brett Heap, 2026-10-12).
Edge cases found in review of lane openRepoShape-3's pull requests, kept here rather than filed:
- → Tests: about a dozen fixtures still clone a just-pushed local bare remote by plain path (the #440-class macOS flake), with small test-only fixes alongside #544 About a dozen fixtures (in
tests/test_update_shape.py,tests/test_doctor_validator_rows.pyandtests/test_family.py, and adopt and scaffold fixtures) andtemplates/family-root/scripts/siblings.pystill clone a just-pushed local remote by plain path. None has failed in CI. Move them to the helper setup-project.py: the root clone of--local-remote-diris--no-localand its two leg clones are rewritten tofile://, and the two session fixtures that clone a scaffold do the same, so a maintenance run packing the just-pushed objects mid-walk no longer diesfailed to copy file to ... No such file or directory(#440) #467 adds once it lands. From setup-project.py: the root clone of--local-remote-diris--no-localand its two leg clones are rewritten tofile://, and the two session fixtures that clone a scaffold do the same, so a maintenance run packing the just-pushed objects mid-walk no longer diesfailed to copy file to ... No such file or directory(#440) #467.
Known limits found by lane openRepoShape-3's acceptance passes (standard 36538fd, each re-checked on main 7a8b981) and kept here rather than filed:
- → README.md and the handbook: the by-hand setup steps dry-run the scaffold without the
--visibilityand--elected-bythat setup passes #550 AT-scaffold N4: README's by-hand version of what setup runs (README.md:531-533) dry-runsscaffold-project.py --org <your-org> --project Atlas --dry-runwithout the--visibilityand--elected-byof the real run on the next line, while setup's own dry run passes both (setup-project.py:1587-1604), so the plan a person reads names a different elector than the real run writes. (README.md and AGENTS.md: an accuracy pass for v1.0.0, every command, flag, exit code, refusal code and step they state checked against the tools on main, so the tag's "README and AGENTS.md accurate" criterion holds #483's accuracy pass may correct this first.) - → Comments that say what main no longer does:
submodule_config's reason,run_validator's "costs no child an import", the decoy test's sitecustomize claim under-S, and the verification row's colon in adopt-project.py's header #666 AT-adopt N9: AGENTS.md:182, README.md:810 and adopt-project.py:87 quote the verification row ascode: 0 of N source paths (seeded from template), but the printed row has no colon (code 0 of 7 source paths (seeded from template), adopt-project.py:5553-5554). (README.md and AGENTS.md: an accuracy pass for v1.0.0, every command, flag, exit code, refusal code and step they state checked against the tools on main, so the tag's "README and AGENTS.md accurate" criterion holds #483's accuracy pass may correct this first.) - → adopt-project.py: remove the temp work roots it makes without
--work-dir, keep a recorded--sourceclone, and point the tests' TMPDIR at their basetemp #526, done in adopt-project.py and tests/conftest.py: a run without--work-dirremoves the empty work directory it made, and a recorded clone that is gone is cloned again, so no emptyopenreposhape-adopt-*is left and nosource-unclonablefollows (#526) #594 (c972032) The test suite never pointsTMPDIRat its own scratch for the adopt runs (tests/conftest.py sets none, and no adopt suite does; the five that set it are test_cli_reference, test_family_add_refusals and three scaffold suites), so everyplan,checkandexecutea test runs without--work-dirleaves anopenreposhape-adopt-*directory in the system temp folder, outside pytest'stmp_pathretention: 15,206 sit in one container's/tmp(2,992 of the first 3,000 empty), 226,165 on the family pass's machine. Direction: one autouse fixture pointingTMPDIRinto the session's basetemp; the tool-side half is the first line under Tracking: adopt-project.py, converting an existing repository in place #457. - → .github/workflows/tests.yml: no
merge_grouptrigger, so GitHub's merge queue cannot be turned on for main: a queued pull request would wait for atestscheck that never runs #895 Deferred (2026-10-14), after docs/handbook.html: stop every README PR rewriting the handbook's README blob and line cap, so open README PRs stop conflicting with each other #519: L3-2: the CI workflow has nomerge_grouptrigger (.github/workflows/tests.yml:12-16), so GitHub's merge queue cannot batch landings. Enabling it also needs a ruleset change, and every required check, SonarCloud included, must report onmerge_group. - CI cost: the Windows job runs serially, 75-130 minutes a run; shard it, or run it under xdist. Moot since ci: run the test suite on Linux only #597 (
654c476c): CI has no full Windows job now. - CI cost: the workflow's concurrency group cancels main's previous run on every landing. Fixed by .github/workflows/tests.yml: a push run on main is grouped by its commit, so the next landing no longer cancels it, and every landing gets its own Linux result (#592) #593 (
024ea55e): a push run is grouped by its commit. - → tests/conftest.py: one helper builds the
url.<base>.insteadOfrewrite thatclone_just_pushedandread_through_giteach build, and the plain-pathgit submodule update --initsites of #554's review use it (#440) #662 Tests (tests: every test that clones a just-pushed local remote by its plain path now clones it withclone_just_pushed, so the macOSfailed to copy filerace of #440 cannot reach it;load_without_bytecodekeeps the doctor's and the Windows-path tests' loads of the family root's scripts from writing.pycinto the templates; the not-a-list doctor cases hold INVALID, exit 1; andGOOD_KEYSholds th #554's review, 2026-10-14):git submodule update --initof a plain-path URL is still git's local clone of a just-pushed remote, at tests/test_family_bump_remove_rollback.py:337 and :908 and tests/test_doctor_legs_members_next.py:630 and :981. The same-c url.<base>.insteadOfrewriteclone_just_pusheduses would cover them. - → tests: four guards that do not hold what they say: a
.pycthe CLI reference's child writes under templates/, a launcher guard blind tofamily.py, a quoting-sentence inventory with three holes, and an S5778pytest.raises#665 Tests (tests: every test that clones a just-pushed local remote by its plain path now clones it withclone_just_pushed, so the macOSfailed to copy filerace of #440 cannot reach it;load_without_bytecodekeeps the doctor's and the Windows-path tests' loads of the family root's scripts from writing.pycinto the templates; the not-a-list doctor cases hold INVALID, exit 1; andGOOD_KEYSholds th #554's review): tests/test_cli_reference.py leaves templates/family-root/scripts/pycache/bootstrap.cpython-312.pyc. scripts/render-cli-reference.py runs the template's siblings.py--helpin a child whosechild_env()does not set PYTHONDONTWRITEBYTECODE. Set it there, or in the test. - → tests/conftest.py: one helper builds the
url.<base>.insteadOfrewrite thatclone_just_pushedandread_through_giteach build, and the plain-pathgit submodule update --initsites of #554's review use it (#440) #662 Tests (tests: every test that clones a just-pushed local remote by its plain path now clones it withclone_just_pushed, so the macOSfailed to copy filerace of #440 cannot reach it;load_without_bytecodekeeps the doctor's and the Windows-path tests' loads of the family root's scripts from writing.pycinto the templates; the not-a-list doctor cases hold INVALID, exit 1; andGOOD_KEYSholds th #554's review, optional): a hygiene test that refuses a plain-path recursivegit cloneof a just-pushed remote in tests/ outside conftest.py, so a new one, like adopt-project.py:y()quotes what the plan's loader would read as a keyword, so--spec-path null,--id nulland--tracking-branch trueadopt as typed, andplan,checkandexecuterefuse, before anything exists, a value the project's own loader would read back as something else from the lineexecutewrites #518's at tests/test_adopt_plan_yaml_quoting.py:2036, is caught when it is added. Closed unwritten by tests/conftest.py: one helper builds theurl.<file URI>.insteadOfrewrite thatclone_just_pushed,read_through_gitand the adopt tests'pastedeach built, and the empty-mountsubmodule update --initsites use it (#662) #748, as tests/conftest.py: one helper builds theurl.<base>.insteadOfrewrite thatclone_just_pushedandread_through_giteach build, and the plain-pathgit submodule update --initsites of #554's review use it (#440) #662 allowed (lane 3's triage, 2026-10-18).
Activity
;,', a backtick or a parenthesis" passes only because pytest's temporary roots need no quoting: under a root with a space the same sentence's keep line isgit -C '<root>/spec' branch NAME, as the file's own spelling test expects #415update-shape.py apply --addrefuses a path that is already pinned, one the target's lists do not name and one the root already has a file at, and omitupdate-add-symlink(#269, on main) and, once #346 lands,update-add-not-a-directory, so a reader meets a refusal no document names; the README is at its line cap and the handbook records its blob, so the clause is a README edit plus a handbook regeneration #429Path.is_file()/is_dir()/exists()guard in the shipped tools answers False to any OSError on Python 3.14 (3.12 and 3.13 raise PermissionError), so a directory this user cannot search reads as ABSENT: validate-pins.py skips its workflow@<sha>check, update-shape.py sayscopy-missingandupdate-pin-missing("adopt the project") for files that are there, the doctor says DRIFTED (5 copy-missing), and bump-leg.py's remaining guards, and every other tool's, skip or misreport instead of refusing by name #430--local-remote-dirbare repository is git's local clone, which links or copies each loose object file while git's background maintenance can pack and delete them, so on the macOS runner it diesfailed to copy file to ... No such file or directory, every doctor test errors at setup, andsetup.shrefuses after creating all three repositories #440brettheap commentedon Oct 12, 2026
(lane openRepoShape-2, 2026-10-12T21:19Z)
This is an edge case this lane's review of #343 found and kept off the issue list, per the v1.0.0 rule that a new edge case goes on its area's Tracking parent.
tests/test_shape_pin_files_not_a_list.py:152(update-shape.pyread_pinrefusesupdate-pin-row, and the pinned validate-pins.py and validate-family.py reportshape-pin-row, when a shape pin'sfiles:is not a list, sofiles: 5,trueor1.5no longer endscheck,apply, shape-doctor.py and the project's own validator inTypeError: ... is not iterable(#290) #343) accepts the doctor's exit 3 (returncode in (1, 3)), and the module docstring (:22-25) still says the doctor's verdict is shape-doctor.py: a root's owncontracts/shape-pin.yamlthat does not read as a pin (a merge conflict, a non-UTF-8 byte, acommit:that is not 40 hex, a deleted file) isINVALID, exit 1, with the git command that puts back the last pin a tool wrote as its NEXT, instead ofCANNOT ANSWER, exit 3, sending the reader togit fetch --allin the standard (#237) #267's to move. shape-doctor.py: a root's owncontracts/shape-pin.yamlthat does not read as a pin (a merge conflict, a non-UTF-8 byte, acommit:that is not 40 hex, a deleted file) isINVALID, exit 1, with the git command that puts back the last pin a tool wrote as its NEXT, instead ofCANNOT ANSWER, exit 3, sending the reader togit fetch --allin the standard (#237) #267 is on main (decff87): a root pin thatread_pinrefuses is INVALID, exit 1, and exit 3 (CANNOT ANSWER) is the regression shape-doctor.py: a root's owncontracts/shape-pin.yamlthat does not read as a pin (a merge conflict, a non-UTF-8 byte, acommit:that is not 40 hex, a deleted file) isINVALID, exit 1, with the git command that puts back the last pin a tool wrote as its NEXT, instead ofCANNOT ANSWER, exit 3, sending the reader togit fetch --allin the standard (#237) #267 fixed.ROOT_PIN_FAULTSpins INVALID only forfiles: 5,trueand1.5, so a doctor that answered CANNOT ANSWER forfiles: abc, a quoted string or a mapping would still pass this file. Fix: assertreturncode == 1and the INVALID verdict, and update the docstring. Seen at update-shape.pyread_pinrefusesupdate-pin-row, and the pinned validate-pins.py and validate-family.py reportshape-pin-row, when a shape pin'sfiles:is not a list, sofiles: 5,trueor1.5no longer endscheck,apply, shape-doctor.py and the project's own validator inTypeError: ... is not iterable(#290) #343's head 91a4a4f. Fixed by tests: every test that clones a just-pushed local remote by its plain path now clones it withclone_just_pushed, so the macOSfailed to copy filerace of #440 cannot reach it;load_without_bytecodekeeps the doctor's and the Windows-path tests' loads of the family root's scripts from writing.pycinto the templates; the not-a-list doctor cases hold INVALID, exit 1; andGOOD_KEYSholds th #554 (2247dbff): the test now assertsreturncode == 1.brettheap commentedon Oct 12, 2026
Lane: openreposhape-5 (openRepoShape-5)
(lane openRepoShape-5, 2026-10-12T21:26Z) One item for this area's checklist, from this lane's #456 work on 2026-10-12. This lane filed it as #469 before reading the ruling that new review edge cases go on the area's checklist; #469 is closed as moved here. It is not in
v1.0.0's scope, and no ordinary repository hits it.Please fold this line into the checklist:
tests/test_shape_doctor.py:438-452andtests/test_windows_paths.py:80-92loadtemplates/family-root/scripts/bootstrap.pyandsiblings.pywithspec.loader.exec_module, which writestemplates/family-root/scripts/__pycache__/*.pycinto the template tree (ignored, sogit statusis silent). Today nothing reads every file undertemplates/family-rootafter them, so the suite is green by order. Both roots' scripts/bootstrap.py:mainescapes what stdout cannot encode and spells each value it prints, soname: "a\ud83db"or日本underPYTHONIOENCODING=asciino longer endsmake bootstrapin a traceback (#391) #456's CI failed this way for the assembly root (run 38297896001:tests/test_scaffold_leg_path_collisions.pyread a.pycas UTF-8). Direction:sys.dont_write_bytecodearound eachexec_module, as Both roots' scripts/bootstrap.py:mainescapes what stdout cannot encode and spells each value it prints, soname: "a\ud83db"or日本underPYTHONIOENCODING=asciino longer endsmake bootstrapin a traceback (#391) #456'sload()does, ideally one helper intests/conftest.py. Fixed with Tests: about a dozen fixtures still clone a just-pushed local bare remote by plain path (the #440-class macOS flake), with small test-only fixes alongside #544: on main879d8047both files load it withload_without_bytecode.exec_module, which leaves.pycfiles inside the template tree, so a test that reads every file under templates/family-root would pass or fail by test order (the failure #456's CI hit for the assembly root) #469brettheap commentedon Oct 12, 2026
Lane: openreposhape-5 (openRepoShape-5)
(lane openRepoShape-5, 2026-10-12T22:20Z) For this area's two release criteria that no issue held: on Brett Heap's word of 2026-10-12, lane 5 takes the release notes (#482:
docs/release-notes/v1.0.0.md, by pull request, kept a draft until the last in-scope issue closes) and the README and AGENTS.md accuracy pass (#483). Both are on thev1.0.0milestone.The notes will be built from this parent's checklist and the other four parents' (#457-#460), and from the open issues left off the milestone. When you fold or tick a checklist item, the notes follow at the refresh just before the tag; nothing for you to do.
brettheap commentedon Oct 13, 2026
(lane openRepoShape-2, 2026-10-13T14:07Z)
This lane's review of #435 found this test gap. Per the v1.0.0 rule, a new edge case goes on its area's Tracking parent, so it is listed here and not filed as an issue.
source:orlegs:is not a_plan_key_findingsfinding. Fixed with Tests: about a dozen fixtures still clone a just-pushed local bare remote by plain path (the #440-class macOS flake), with small test-only fixes alongside #544: on main879d8047,tests/test_adopt_execute_refusals.py:1039-1041holdssource-bare,source-absentandlegs-bare.adopt-project.py:4374and:4379(as adopt-project.py:checknames, andexecuterefuses before the work directory is made, a plan whosesource:orlegs:is not a mapping or whoseallow_empty_legs:orpaths:is not a list of whatplanwrites, and asks the same first of apins:that is not a list, so a number or a boolean in any of the four no longer ends both in a traceback andallow_empty_legs: codeis no longer read as four legs (#386) #435 landed them, main f391e58) exemptsource is Noneandlegs is None. That follows the function's docstring: a key that is absent or bare is read as it always was,plan-no-sourceforsource:and the names' own refusal forlegs:.GOOD_KEYS(tests/test_adopt_execute_refusals.py:1020) holds that rule forpins:,allow_empty_legs:andpaths:only.is Nonearm, so a bare key becomesplan-bad-sourceorplan-bad-legs. The behaviour is right today.source-bare,source-absentandlegs-barerows to the control table. Each asserts noplan-bad-finding and the code main gives.--visibilityand--elected-bythat setup passes #550docs/release-notes/v1.0.0.md: brought up to main 3121945, so section …
8 remaining items
brettheap commentedon Oct 15, 2026
(lane openRepoShape-5, 2026-10-15T17:27Z) #578 (#543) landed as
3f768a6and takes #543's half of the fixture line here. That is the "→ #544" line: "About a dozen fixtures … andtemplates/family-root/scripts/siblings.pystill clone a just-pushed local remote by plain path".siblings.pyhalf is done. Its member clone is nowgit clone --no-local. A member url that is a path on disk is read through git instead of copied file by file, so a repack in that remote cannot break the clone halfway (tests/test_shape_doctor.py, tests/conftest.py and setup-project.py: a clone of a just-pushed--local-remote-dirbare repository is git's local clone, which links or copies each loose object file while git's background maintenance can pack and delete them, so on the macOS runner it diesfailed to copy file to ... No such file or directory, every doctor test errors at setup, andsetup.shrefuses after creating all three repositories #440).Lane: openreposhape-5 (openRepoShape-5)
brettheap commentedon Oct 15, 2026
(lane openRepoShape-4, 2026-10-15T19:02:18Z) Two lines for this checklist, from R588's verification of #588 (#532) at
c2e19022. #588 is now on main as709eec1. Both concern tests only; neither is user-facing..pycthe CLI reference's child writes under templates/, a launcher guard blind tofamily.py, a quoting-sentence inventory with three holes, and an S5778pytest.raises#665 The inventory test intests/test_readme_windows_quoting_sentence.py(test_the_tools_that_quote_for_the_host_are_the_ones_the_sentence_names, :106) lists which shipped tools call a host-dialect quoter. It has gaps, and closing them is optional hardening:from repo_shape import shell_line as joined) is missed; R588's mutant V2 survives;.pysuffix, such as the rootbootstrap, is not scanned (V5);family.pyline switched to POSIX quoting would still pass while the README sentence names it (V3);scripts/repo_shape.pyis exempt, where only the bodies ofshell_quotedandshell_lineneed to be..pycthe CLI reference's child writes under templates/, a launcher guard blind tofamily.py, a quoting-sentence inventory with three holes, and an S5778pytest.raises#665 Sonar S5778 attests/test_bump_leg_paste_lines.py:402-404:leg_placement(leg)is evaluated insidepytest.raises, so an exception from it would count as the expected one. The gate passes.brettheap commentedon Oct 15, 2026
(lane openRepoShape-2, 2026-10-15T21:23:02Z)
These items came from this lane's v1.1.0 reviews of PRs #620, #621, #622 and #623, from its re-review of #570, and from its writers, and per the v1.0.0 rule a new edge case goes on its area's Tracking parent rather than into a new issue.
CONTRACTS_DIR, a second setup run refused at the plan) #606 L2-66 (nit, docs; README.md, adopt-project.py and scaffold-project.py: the Free-plan secrets hint follows the legs' visibility (adopt) or names the legs as its reason (scaffold), though the root's visibility decides it #602 review): README's "If the legs are private" paragraph ties the advice to set REPOSITORY secrets to private legs, but what decides it is whether the assembly root is private or internal. Fixed 2026-10-16 by README.md and the handbook: say that a source's ownsingle-repository.yamlrecord arrives asleg: drop, that the adopted Makefile'sCONTRACTS_DIRfollows the spec leg's mount, and that a second setup run is refused at the plan, so the three sentences #566, #572 and #573 left false or incomplete are true of main (#606) #627 →8062810.README.md:510-514at 55faad2: "If the legs are private, CI needs a credential to fetch them: a GitHub App ... as REPOSITORY secrets on the GitHub Free plan, where organisation secrets do not reach private repositories". Thevalidateworkflow runs in the root, and on the Free plan an organisation secret reaches a public repository only. So private legs under a public root can take the credential as an organisation secret, and a private root needs repository secrets. The assembly root's own README says it by repository (templates/assembly-root/README.md:79-83), and so doesREADME.md:628-631.impl602-free-plan-hint-root/pr-body.md, "README follow-up"): "as REPOSITORY secrets on the assembly root when it is private or internal and the organisation is on the GitHub Free plan, where organisation secrets reach only public repositories". README.md and the handbook: the sentences #566, #572 and #573 leave false or incomplete (an ambiguous record that arrives answered, the spec mount'sCONTRACTS_DIR, a second setup run refused at the plan) #606 (README and handbook) does not list this line, and adopt-project.py and scaffold-project.py: the Free-plan secrets hint goes by the assembly root's own visibility on GitHub and names the root as its reason, so a public source adopted with private legs is no longer told its organisation secrets resolve to EMPTY (#602) #621 names README.md and the handbook: the sentences #566, #572 and #573 leave false or incomplete (an ambiguous record that arrives answered, the spec mount'sCONTRACTS_DIR, a second setup run refused at the plan) #606 as its home, so README.md and the handbook: the sentences #566, #572 and #573 leave false or incomplete (an ambiguous record that arrives answered, the spec mount'sCONTRACTS_DIR, a second setup run refused at the plan) #606 needs to take it or it stays here.## Starting a project in a new organisationsection, so the change follows tests/test_repo_hygiene.py and docs/handbook.html: each handbook section records the hash of every README section it reads, and README.md's line cap is 1422 plus one file per raise in tests/readme-cap.d/, so two README pull requests no longer conflict on the footer's one blob or the cap's one line, and a stale page names exactly which sections to re-read (#519) #551's hash step for that section's seven handbook markers (for exampledocs/handbook.html:475). README is at its line cap, so the edit has to keep the line count or take its owntests/readme-cap.d/file. The handbook has no twin of this sentence.family.py init: the dry runs skip the target-exists and folder checks, and anygh repo viewfailure reads as "does not exist" #609 review): two tests in scaffold-project.py and family.py: both runs ask whether each folder they make can be made and take only gh's not-found answer as "absent", so--dry-runrefuses a file where a folder goes, or agh repo viewthat fails otherwise, by name as the real run does, and a refusedinitleaves no empty family folder (#609) #622 assert POSIXstrerrortext and carry no Windows skip. Fixed 2026-10-16 by scaffold-project.py and family.py: both runs ask whether each folder they make can be made and take only gh's not-found answer as "absent", so--dry-runrefuses a file where a folder goes, or agh repo viewthat fails otherwise, by name as the real run does, and a refusedinitleaves no empty family folder (#609) #622 →d4a7fab.--dry-runrefuses a file where a folder goes, or agh repo viewthat fails otherwise, by name as the real run does, and a refusedinitleaves no empty family folder (#609) #622, not on main yet):tests/test_dry_run_parity.py:369expectscannot be made: File existsand:608expectscannot be made: Not a directory. On Windowsmkdirwords both differently, for example "Cannot create a file when that file already exists".strerrorassertion in the file (:595-598) is already insideif os.name != "nt".strerror, or mark both tests POSIX-only. Make the change in scaffold-project.py and family.py: both runs ask whether each folder they make can be made and take only gh's not-found answer as "absent", so--dry-runrefuses a file where a folder goes, or agh repo viewthat fails otherwise, by name as the real run does, and a refusedinitleaves no empty family folder (#609) #622 if it has a fix round, or after it lands.Source reports, in
opensoft/brett-wipunderhandoffs/xFactory/attachments/lane-openRepoShape-2/:review621-8577b15/fix-list.md(N3),review621-8577b15/final-report.mdandimpl602-free-plan-hint-root/pr-body.md("README follow-up") (L2-66);review622-7f7e1b4/fix-list.md(nit 2) andreview622-7f7e1b4/final-report.md("Windows and CRLF") (L2-67).scaffold-project.py and family.py: an existing EMPTY working tree or …
brettheap commentedon Oct 15, 2026
(lane openRepoShape-5, 2026-10-15T23:52:24Z) Checklist items for #461, per ruling 5, from lane 5's #596 (#585), which landed as
bf7b19b. They are outside #585's lines, and none is fixed or filed.→ openRepoShape in Git Bash: the
machine:report probespython3alone, a drive-letter$OPENREPOSHAPE_REPOor$OPENREPOSHAPE_BIN_DIRis misread,tests-git-bashskips #598's installer cases, and a doctor test's docstring is stale #649 A test's docstring says a refused$OPENREPOSHAPE_REFarrives before any clone, and--doctorclones first.test_a_ref_the_doctor_would_check_out_is_validatedsays the refusal "has to arrive before any clone does".shape_checkoutrunsgit clonefirst and callschecked_ref "$REF"after it. The test passes either way: it asserts exit 2, andOPENREPOSHAPE_REFandgit checkoutin stderr (:678-680), and none of those pins the order.tests/test_openreposhape_command.py:668-669at bf7b19b (the sentence; the test is :662-684);openRepoShape:482(shape_checkout), thegit cloneat :490 andchecked_ref "$REF"at :495.→ Publish openRepoShape to TestPyPI and PyPI as
openreposhape, so onepipx installputs the tools and git-filter-repo on any machine (design first) #563 The handbook's step 3 has no Git Bash path.data-platform="linux mac wsl", so a reader on the Windows tab never sees the one-line install that the Windows panel says Git Bash runs. The Windows tab shows the "Fetch the flow" heading and the PowerShell download instead.docs/handbook.html:781(the install block) and :782-791 (the paragraph) at bf7b19b; the Windows tab's step is :780 and :804-812; the panel's sentence about Git Bash is :724-728.→ openRepoShape in Git Bash: the
machine:report probespython3alone, a drive-letter$OPENREPOSHAPE_REPOor$OPENREPOSHAPE_BIN_DIRis misread,tests-git-bashskips #598's installer cases, and a doctor test's docstring is stale #649 install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598's installer cases do not run under Git Bash.tests-git-bashrunstests/test_openreposhape_command.pyandtests/test_install_sh.py.test_install_sh.pyreaches install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598's code in two ways: every run printsadoption:anddone:, andno_pipxexpects uv's printed command. But the uv install, the readinesses' wording and the pasted next steps are tested intests/test_install_sh_filter_repo.pyonly, on Linux. README says Git Bash runs step 3's one line, "tested in CI" (README.md:154-155)..github/workflows/tests.yml:220, the job's pytest line (the job is :156-220), at bf7b19b;tests/test_install_sh_filter_repo.py(409 lines);install.sh:453and :463-475 (theadoption:anddone:lines);tests/test_install_sh.py:479-481(no_pipx).test_install_sh.pytestmark(BASH_SKIPandgit_bash_session;test_install_sh_filter_repo.py:52) and drives every run throughNet. Its ownuvfake writes LF bytes and names the interpreter asshlex.quote(sys.executable), asNet.fakedoes (test_install_sh_filter_repo.py:141-148,test_install_sh.py:211-218). No assert compares a path bash printed with a Python path; each checks a line's start or end. Its programs seePATH=net.pathalone, so the runner's own pipx or uv cannot leak in. Under Git Bash it would likely pass.windows-latestrun. tests: a tests-git-bash job runs the launcher's and install.sh's suites in Git for Windows' own bash, so README and the handbook say Git Bash runs the one-line install, tested in CI but for a typed yes, which it cannot take there (#585) #596 did not add it. Not run on Windows: 18 passed on Linux, in 7.32s, on tests: a tests-git-bash job runs the launcher's and install.sh's suites in Git for Windows' own bash, so README and the handbook say Git Bash runs the one-line install, tested in CI but for a typed yes, which it cannot take there (#585) #596's head52524fe3.Not filed: README's
pip install git-filter-repofor adopting (README.md:768at bf7b19b). Lane 3's open PR #630 (l3-563-docs) removes that line.Lane: openreposhape-5 (openRepoShape-5)
brettheap commentedon Oct 16, 2026
(lane openRepoShape-4, 2026-10-16T11:28:29Z) One line for this checklist, from R434's review of #633 (#434), which is now on main as
f1c73e2. R434 ran it as a mutant against the new test file atac508957; it is a test gap and not user-facing, and #633 does not fix it.S_ISREGchild check of validate-pins.py's workflow scan (a directory namedsub.yml, a dangling and a looping symlink, #434) and siblings.py's holder-root spelling in both headers (#454) #664 Test gap: theS_ISREGhalf of the rewritten child check is unpinned (mutant K survives). Replacing the child's kind check atvalidate-pins.py:345with_is_a(path, lambda mode: True)leavestests/test_validate_pins_unreadable_workflows_dir.pyat 28 passed, and also all oftest_validate_pins_unreadable_workflow.py,test_validate_pins_without_git.py,test_validate_pins_lockstep_lines.pyandtest_pin_validator.pytogether (102 passed). Over a root holding a directory named.github/workflows/sub.ymlthe head sayspins ok, but mutant K saysREFUSED workflow-unreadable: .github/workflows/sub.yml: cannot be read (Is a directory). validate-pins.py (assembly-root template): a.github/workflows/directory that this user cannot list or search is aworkflow-unreadablerefusal naming it, exit 2, so the project's pins gate no longer ends in a PermissionError traceback, exit 1, where a workflow file it cannot open already was one (#434) #633 rewrote exactly that expression. A pinning test, besidetest_a_workflows_path_that_is_a_file_is_still_no_workflows(:289-298atac508957): a control that a directory named.github/workflows/sub.yml, a danglingdangling.ymlsymlink and aloop.ymlself-symlink are still skipped. Assert thatvalidator._workflow_refs(project)returns the leg's reference and nothing else, orpins okfromvalidate(project). It is mode-free, so it runs everywhere, Windows included; Windows may need the symlink half skipped where symlinks are not allowed (R434 N1).Lane: openreposhape-4 (openRepoShape-4)
brettheap commentedon Oct 16, 2026
(lane openRepoShape-4, 2026-10-16T11:29:52Z) One line for this checklist, from R454's review of #642 (#454, #629), which is now on main as
0ed1b84. R454 ran it as a mutant (R1) against the new test file at1a42df41; it is a test gap and not user-facing, and #642 does not fix it.S_ISREGchild check of validate-pins.py's workflow scan (a directory namedsub.yml, a dangling and a looping symlink, #434) and siblings.py's holder-root spelling in both headers (#454) #664 Test gap: the holder ROOT's spelling in both headers is unpinned (mutant R1 survives). Making the placement header print the holder's root raw (siblings.py:1172) leaves all 150 tests oftests/test_siblings_family_name.pypassing, because no test puts an exotic character in the holder's own path. The code is right: a probe with byte 0xff in the holder's path printsbad\udcffdirand exits 0, where main exits 1 underutf-8:strict(family-root siblings.py:make siblings,parkandresumeprint the family'snamewith a plainprint, soname: "a\ud83db"ends each in aUnicodeEncodeError, and the literal dash ends every name underPYTHONIOENCODING=ascii#454 asks for "the name and the root first"). A pinning test, besidetest_the_header_is_printed_whatever_the_name_and_the_stdout(:150-167): a row with the holder's family folder under a path holding U+2028 or an ESC, for examplein_family_folder(family_template, tmp_path / "a b", PLAIN), so the run still passes its folder check. Assert, for the placement and dispatch dry runs, that the first line issiblings: InkRouter (<spelled root>) — 0 member(s), that thefamily folder <spelled folder>line holds the spelled folder, and thatnot NEVER_RAW & set(out). It pinssiblings.py:1172-1176and:807-809(R454 N1).Lane: openreposhape-4 (openRepoShape-4)
brettheap commentedon Oct 16, 2026
(lane openRepoShape-4, 2026-10-16T11:31:56Z) Two lines for this checklist, from R423's review of #641 (#423, #426), which is now on main as
42f6411. R423 ran the second as a mutant (R1) againsttests/test_non_utf8_git_reads.pyata67a05b6and reasoned the first without running it; both are test gaps and not user-facing, and #641 fixes neither.→ tests: the non-UTF-8 name and branch tests: one skip helper for a filesystem that refuses
probe\udcff, Windows guards onmake_fileand the--branch a\udcffbcase, and the UTF-8 promise and the leg finding's role pinned #663 Test gap, Windows (reasoned, not run):make_filerelies on the filesystem refusingcaf\udce9.yml.tests/test_non_utf8_git_reads.py:86-93. On Windows, CPython passes a str path toCreateFileWas UTF-16, so the lone surrogate is one code unit U+DCE9, and NTFS stores code units unchecked: the file is most likely CREATED, not skipped. Git for Windows would then see the name through its own UTF-16 conversion, so the\xe9rows would fail or error on a Windows run of the suite, against the module docstring's "where the disk refuses (macOS, Windows) the test skips". APFS's EILSEQ skip on macOS is right. Fix: skip onos.name == "nt"first, asPOSIX_NAMESintests/test_adopt_pins.py:818-821does and as this file's ownPOSIX_ENVIRONMENT_ONLY(:390) does for the environment rows, and keep thetry/exceptfor APFS. Nothing pins it on Linux CI (R423 N1).→ tests: the non-UTF-8 name and branch tests: one skip helper for a filesystem that refuses
probe\udcff, Windows guards onmake_fileand the--branch a\udcffbcase, and the UTF-8 promise and the leg finding's role pinned #663 Test gap: the UTF-8 promise is unpinned (mutant R1 survives). With bump-leg's dirt read asdirt.decode("ascii", "backslashreplace")(scripts/bump-leg.py:1126), all 47 tests of the file still pass, although every non-ASCII UTF-8 name then prints as escapes (café.ymlascaf\xc3\xa9.yml): a display regression from main for any non-English name undercore.quotePath=false. The docstring promises the opposite ("Every name that IS UTF-8 prints exactly as it did"). Intest_a_name_that_is_utf8_under_quote_path_false_is_still_refused(:202-217) the:215check matches only the name's ASCII ends, for a locale reason that is sound (a child's stderr is decoded by the locale, which is not UTF-8 on every platform), so it acceptscaf\xc3\xa9.yml. Fix: a locale-independent assertion that the named line holds no escape:A locale decode of UTF-8
énever produces a literal\x, so the ASCII-ends caution stays intact. R1 must then be killed (R423 N2).Lane: openreposhape-4 (openRepoShape-4)
brettheap commentedon Oct 16, 2026
(lane openRepoShape-4, 2026-10-16T11:35:24Z) Two lines for this checklist, from R455's review of #644 (#455), which is now on main as
cd5ffa2. R455 ran the second as a mutant (R2) againsttests/test_bootstrap_branch_bytes.pyat733f8530and reasoned the first for Windows without running it (CI is Linux only); both are test gaps and not user-facing, and #644 fixes neither.→ tests: the non-UTF-8 name and branch tests: one skip helper for a filesystem that refuses
probe\udcff, Windows guards onmake_fileand the--branch a\udcffbcase, and the UTF-8 promise and the leg finding's role pinned #663 Test gap, Windows (reasoned, not run):test_a_name_that_is_not_utf8_is_still_carried_and_the_branch_madeskips on the disk alone, not onos.fsencode.tests/test_bootstrap_branch_bytes.py:346-357at733f8530(:346oncd5ffa2). R455 called it the one should-fix, test only. The skip asks only whether the disk takes the nameprobe-\udcff. On Windows NTFS takes it: the str goes toCreateFileWas UTF-16 with the unpaired unit 0xDCFF. The child then receives--branch a\udcffbthroughCreateProcessW, where_argument_faultRIGHTLY refuses U+DCFF, becauseos.fsencodewrites 3 bytes. Sobranch a\udcffb created …is absent and the test fails. Fix: skip first, on the same test the next unit test uses:The file's own emulated
test_where_a_surrogate_is_three_bytes_and_no_byte_it_is_refusedkeeps the Windows half, and the existing test keeps the POSIX half (R455 F1).→ tests: the non-UTF-8 name and branch tests: one skip helper for a filesystem that refuses
probe\udcff, Windows guards onmake_fileand the--branch a\udcffbcase, and the UTF-8 promise and the leg finding's role pinned #663 Test gap: the leg finding's NAME is unpinned (mutant R2 survives, 54 passed). If the leg finding names the leg'spathinstead of itsrole, nothing fails. It is minor: the templates' roles equal their paths, so--spec-path contractswould printleg 'contracts'unseen. A unit test that the leg finding names the ROLE:validator._leg_branch_findings([{"role": "spec", "path": "contracts", "branch": "a\x00b"}])starts withFINDING manifest-leg-branch: leg 'spec': branch is 'a\x00b'. Use the module the last test loads (load_without_bytecode) (R455 F3).Lane: openreposhape-4 (openRepoShape-4)
brettheap commentedon Oct 16, 2026
(lane openRepoShape-4, 2026-10-16T11:37:28Z) One line for this checklist, from R432's review of #632 (#432), which is now on main as
ddb8708. R432 read it againstascii_textand--json'sjson.dumpsinshape-doctor.py; it is a docstring slip, not user-facing, and #632 does not fix it.submodule_config's reason,run_validator's "costs no child an import", the decoy test's sitecustomize claim under-S, and the verification row's colon in adopt-project.py's header #666submodule_config's docstring gives a reason that is true of the string but not decisive.shape-doctor.py:6718-6719at86c6b9c(:6984onddb8708): "It is also what the row PRINTS, and a lone surrogate is a string no UTF-8 stream can write." The table goes throughascii_text(:576-589,encode("ascii", "replace")) and--jsonthroughjson.dumpswith the defaultensure_ascii=True(:8604-8617), so neither path would have raised on a surrogate. The comparison withorigin_url's reading of a clone's remote is the reason, and the docstring gives it first. Fix: drop the sentence, or say that--jsonwould carry a lone\udce9escape that strict JSON readers refuse; the printed table never could carry one. No test pins it (R432 N1).Lane: openreposhape-4 (openRepoShape-4)
brettheap commentedon Oct 16, 2026
(lane openRepoShape-4, 2026-10-16T11:39:33Z) Three lines for this checklist, from R342's review of #636 (#342), which is now on main as
c22da12. R342 measured the first two on main4cf5fb07and on #636's head4aade9f4(mutants M4 and R1 against both, and a probe root whose own validator gainedimport yaml) and reasoned the third without running it; none is user-facing, and #636 fixes none of them.submodule_config's reason,run_validator's "costs no child an import", the decoy test's sitecustomize claim under-S, and the verification row's colon in adopt-project.py's header #666 Two test comments are now false, and shape-doctor.py: every validator child starts with-S, so no.pthin the user's or a virtualenv's site-packages stands in for therepo_shape.pya root lost, and themanifestrow is the gate's ownModuleNotFoundError(#342) #636 is what made them false.tests/test_shape_doctor.py:10241-10257("THEsitecustomize.pyIS WHAT DOES THE WORK, and it catches every way the directory can reach a validator") and the inline comment at:10302-10310(that the decoy catches arun_validatorthat kept the variable, shape-doctor.py: after #238 the doctor runs a root's OWN validator with the operator'sPYTHONPATHstill in the child's environment, so for an operator who exports one that reaches arepo_shape.pythemanifestrow of a root that lost its ownscripts/repo_shape.pysaysok ... passesagain, the false pass #238 removed, where the project's own gate in CI fails onModuleNotFoundError: No module named 'repo_shape'#306 review S1) describe a guard that-Sremoves: under shape-doctor.py: every validator child starts with-S, so no.pthin the user's or a virtualenv's site-packages stands in for therepo_shape.pya root lost, and themanifestrow is the gate's ownModuleNotFoundError(#342) #636 no child imports asitecustomize. On main,test_an_exported_pythonpath_does_not_reach_a_template_validatorcaught BOTH shape-doctor.py: after #238 the doctor runs a root's OWN validator with the operator'sPYTHONPATHstill in the child's environment, so for an operator who exports one that reaches arepo_shape.pythemanifestrow of a root that lost its ownscripts/repo_shape.pysaysok ... passesagain, the false pass #238 removed, where the project's own gate in CI fails onModuleNotFoundError: No module named 'repo_shape'#306 regressions it was written for (M4-on-main, shape-doctor.py: after #238 the doctor runs a root's OWN validator with the operator'sPYTHONPATHstill in the child's environment, so for an operator who exports one that reaches arepo_shape.pythemanifestrow of a root that lost its ownscripts/repo_shape.pysaysok ... passesagain, the false pass #238 removed, where the project's own gate in CI fails onModuleNotFoundError: No module named 'repo_shape'#306's prepend:[family],[manifest]and[pins]fail; R1-on-main, the variable popped only for a script inside the root:[manifest]and[pins]fail). On the head it catches NEITHER: all three cases pass. Nothing is unguarded: both regressions stay killed bytest_run_validator_gives_the_child_exactly_the_pythonpath_it_is_givenalone (:10336). Fix: say that replace-not-prepend and the S1 case are now held by that test, while the decoy test holds the template row's real verdict under an exportedPYTHONPATH. Comment only; fold it in with the next push to that file (R342 NIT-1).submodule_config's reason,run_validator's "costs no child an import", the decoy test's sitecustomize claim under-S, and the verification row's colon in adopt-project.py's header #666run_validator's docstring overstates "IT COSTS NO CHILD AN IMPORT".shape-doctor.py:1565-1568onc22da12(:1517-1525at4aade9f4): "a root's pinned copy of one ... imports the standard library and this standard's own modules and nothing else" is true of an UNEDITED copy only. Measured: a root whose ownvalidate-manifest.pygainedimport yaml(PyYAML 6.0.3 sits in this host'sdist-packages) readsok manifest ... passeson main andFINDING manifest: ... crashed before reaching a verdict (exit 1): ModuleNotFoundError: No module named 'yaml'on the head; thepinsrow isFINDING shape-copy-drifton both, and the NEXT pasted withsiteon exits 0. A root that edited its own copy to import more now gets the crash its own CI gets, becausevalidate.ymlinstalls nothing ("Standard library only"). Only a project that also edited its workflow, and kept both with--accept-local, sees a crash its CI does not. Fix: add that sentence to the docstring. The row namingNo module named 'yaml'is a fair answer, so R342 would not change the code (R342 NIT-2).tests-git-bashruns on Windows with a stub doctor (tests/test_openreposhape_command.py:1385-1400,STUB DOCTOR AT). So R342's reasoning that-Scosts no child an import there is unmeasured: CPython finds the standard library through its path calculation, never throughsite, on every platform (Windows python.org and setup-python builds, Windows venvs throughpyvenv.cfg'shome, macOS python.org framework and Homebrew builds), andsiteadds only site-packages and the.pthand customize hooks. Reasoned, not run (R342 Tracking: the test suite, CI and the docs #461).Lane: openreposhape-4 (openRepoShape-4)
docs/handbook.html and docs/release-notes/v1.1.0.md: step 3's Windows…
brettheap commentedon Oct 16, 2026
(lane openRepoShape-5, 2026-10-16T12:55:21Z) Two lines for this checklist, lane 5's own leftovers from #649's writer (PR #656, head
d91b3144), filed at once on Brett Heap's word of 2026-10-16T12:17:02Z ("Lane 5's own leftovers only").$OPENREPOSHAPE_BIN_DIRis respelled in install.sh's own lines and refused before its fetch, and the PATH note quotes the directory it prints (#649's two leftovers) #667 install.sh echoes a Windows-spelled$OPENREPOSHAPE_BIN_DIRas typed (install.sh:372-373, and the lines at :435, :449 and :476-488), and learns that--installrefuses it (after openRepoShape in Git Bash: themachine:row findspython, a drive-letter$OPENREPOSHAPE_REPOis a path, a Windows-spelled$OPENREPOSHAPE_BIN_DIRis respelled or refused, andtests-git-bashruns #598's installer cases (#649) #656) only once it has fetched the launcher.$OPENREPOSHAPE_BIN_DIRis respelled in install.sh's own lines and refused before its fetch, and the PATH note quotes the directory it prints (#649's two leftovers) #667 The PATH note is not quoted for every directory (openRepoShape:456-461): a directory holding",$, a backquote or\prints anexport PATH=line that does not do what it says, and one holding:or a newline cannot be on$PATHat all.Lane: openreposhape-5 (openRepoShape-5)
install.sh and openRepoShape: a Windows-spelled `$OPENREPOSHAPE_BIN_D…
brettheap commentedon Oct 18, 2026
(lane openRepoShape-3, 2026-10-18T14:28:40Z) Triage of #461's unchecked items at the v1.1.1 release commit
5017830. This is lane 3's TRIAGE-461, read only.25 unchecked lines: 21 FIXED, 1 STALE and 2 OPEN, now filed as #894 and #895. Of the v1.0.0 limits citing #461, #415 and #430 are COVERED by their open issues, and #429 is FIXED. Body edits made now: B1, B2, B3, B5 and B6 ticked; B7 ticked as closed unwritten; B4 points to #895.
50178302247dbf(#554):tests/test_update_shape.py:112,tests/test_doctor_validator_rows.py:423,tests/test_family.py:1428usecloned_just_pushed.3f768a6(#578):siblings.py:797--no-localf6eaab7(#579):README.md:568-569,docs/handbook.html:1046code: 0 of Nquotingaf01068(#505):AGENTS.md:184,README.md:860-861.974d482(#730):adopt-project.py:176-177merge_grouptriggersubmodule update --init431c8cf(#748):tests/conftest.py:193-214submodule_update_through_git, at all five sites.pycin templates2000488(#724):scripts/render-cli-reference.py:315PYTHONDONTWRITEBYTECODE=1741679e(#713),tests/conftest.py:372-375insteadOfrewrite built twice431c8cf(#748):tests/conftest.py:83-123family.py2000488(#724):tests/test_validator_child_encoding.py:635,:678-681bad05d2(#723):tests/conftest.py:11552000488(#724):tests/test_readme_windows_quoting_sentence.py:79,:137-147,:267-2712000488(#724):tests/test_bump_leg_paste_lines.py:418-420190133d(#630):docs/handbook.html:834-836bb439a2(#706):tests/test_pins_child_kind_and_siblings_root_spelling.py:94-121,:133-166bad05d2(#723):tests/test_non_utf8_git_reads.py:107,:178;tests/test_bootstrap_branch_bytes.py:438,:524-53855c6ba7(#700):shape-doctor.py:9474-9477.974d482(#730):tests/test_shape_doctor.py:10771-10784,shape-doctor.py:1903-1904-Schildren on macOS or Windowsrelease.yml's install check passes with a preflight child that dies on import (probe in #894)dcf62ed(#726):install.sh:520-543;openRepoShape:406-446C1 to C18 are the unchecked lines in this issue's comments, in order. Those comments are left as their authors wrote them.
#894 is v1.2.0 and unclaimed. Any lane may claim it as usual.
Lane: openreposhape-3 (openRepoShape-3)