Repository navigation
setup-project.py: paste lines quote each path, the scaffold's NEXT STEPS wait for step (7), step (3) refuses a file above the clone, -- --dry-run ends at the plan, step (4) hears passed pins, and the upstream guard ignores case (#538) - #569
Conversation
…, the scaffold's NEXT STEPS are left to step (7), step (3) refuses a file above the clone path, `-- --dry-run` ends at the plan, step (4) hears the pins passed through, and the upstream guard ignores case, so no run says what it did not do or creates three repositories and then dies (#538) Six defects in setup-project.py's printed text and step checks, and the two rides-along, from the v1.1.0 triage of the v1.0.0 known limits on #459. On main bfed143 each one reproduces. - The hand-over's `cd <clone>` and step (7)'s backstop printed their paths bare, so `--into "/home/a b"` gave lines the shell split. Both now go through `shell_word`, as the family lines do (#486), and the backstop names the clone's parent as a `cd` line of its own. - Under `--local-remote-dir` the backstop's recursive clone, and the scaffold's own NEXT STEPS clone, lacked `-c protocol.file.allow=always` and failed `transport 'file' not allowed`. Both carry it now; the scaffold's line is POSIX-quoted (`shell_line`, as #532 rules). - Inside setup the scaffold printed its NEXT STEPS (clone, cd, make bootstrap) just before step (7) ran them. The scaffold takes a new `--no-next-steps`, which setup passes in step (6); `Working trees are at` still prints, because README's rehearsal paragraph cites it. - A FILE at `<into>/<Family>`, or at `--into`, passed step (3): all three repositories were created and `os.makedirs` ended the run in a `FileExistsError` traceback. Step (3) now refuses a non-folder above the clone path, and step (7)'s `os.makedirs` is a refusal with the lines that finish by hand, never a traceback. - `-- --dry-run` made the scaffold create nothing, then step (7)'s clone failed and said "The three repositories WERE created" (and left the family folder behind). Setup now reads its passthrough as the scaffold's argparse does (whole, a prefix it takes, up to an inner `--`) and ends the run at the plan, exit 0, asking nothing. - Step (4) asked the naming check about the three names alone, so a `--pin` (or `--referent-chain`) passed through was "no referent pin declared" there and a domain descendant in the plan. The validator is now asked with `--pins=` and `--referent-chain=` from the passthrough. - The upstream-org guard compared `opensoft` exactly, so `--org OpenSoft` and an `origin` under `OpenSoft/` scaffolded into opensoft's namespace. Every upstream check casefolds now (`is_upstream_org`). docs/cli.md is re-rendered for the usage line and the new scaffold flag. The tests are shared helpers in tests/conftest.py, called by one-line twins in tests/test_setup_project_py.py and tests/test_setup_sh.py, plus in-process tests and one scaffold test; all 29 new run tests are red on main. Closes #538 Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 2 days by commenting |
Reviewer's GuideUpdates setup-project.py and scaffold-project.py to safely handle quoted paths and local recursive clones, correctly coordinate passthrough flags and dry runs, validate clone paths before side effects, and reject upstream organizations case-insensitively, with matching CLI documentation and extensive shared regression coverage. Sequence diagram for setup dry-run and scaffold coordinationsequenceDiagram
participant User
participant Setup as setup-project.py
participant Scaffold as scaffold-project.py
participant FS as Filesystem
User->>Setup: invoke with passthrough flags
Setup->>Setup: naming_declarations(passthrough)
Setup->>Scaffold: run plan with forwarded pins
Scaffold-->>Setup: print plan
alt --dry-run or accepted prefix passed through
Setup->>Setup: passes_a_dry_run(passthrough)
Setup-->>User: report plan only
else confirmed run
Setup->>Scaffold: run with --no-next-steps
Scaffold->>FS: create repositories
Setup->>FS: os.makedirs(parent)
Setup-->>User: clone, bootstrap, and hand-over steps
end
Sequence diagram for local recursive clone commandssequenceDiagram
participant Setup as setup-project.py
participant Git
participant Local as Local remote directory
Setup->>Git: git -c protocol.file.allow=always clone --recurse-submodules assembly
Git->>Local: read local recursive submodules
Local-->>Git: provide leg repositories
Git-->>Setup: create assembly working tree
Setup-->>Setup: finish_by_hand(parent, assembly, project, local)
Flow diagram for clone-path validation before side effectsflowchart TD
A[Resolve clone path] --> B{Clone path exists?}
B -->|yes| C[refuse_a_taken_clone_path]
B -->|no| D[nearest_entry of clone parent]
D --> E{Nearest entry is a folder?}
E -->|no| F[Refuse file or non-folder above clone]
E -->|yes| G[Continue to plan]
C --> H[Stop without creating repositories]
F --> H
G --> I[Confirm plan]
I --> J[Create scaffold and clone repositories]
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-2, 2026-10-14T23:39Z) This lane reviewed its own PR #569 at c71eadb read-only, in its own detached worktrees, and authored nothing. CHANGES NEEDED, for two small fixes. A fix round follows on this branch. The PR lands only on Brett Heap's word.
Probes:
Tests:
Merges are clean with main, #565, #570, #561 and #554. #573 conflicts only by adjacent additions to three test files, which a union resolves. Report: |
…`sh` on every host, so a Windows path reaches Git Bash whole, and the link-to-nothing test reads the target as Windows returns it (#538) Review of PR #569 at c71eadb found two things to fix in the PR. The hand-over's `cd <clone>` and step (7)'s `cd <parent>` and `<assembly>` went through `shell_word`, which quotes for PowerShell on Windows: it left `C:\work\Sample` bare, which Git Bash reads as `C:workSample`, and doubled the apostrophe of `Jane O'Neill`, which sh reads as `ONeill`. #532 rules the printed commands POSIX-only, and this PR already prints the scaffold's NEXT STEPS clone with `shell_line(..., "posix")`. `shell_word` takes a `posix` switch now, over a copy of the standard's `SHELL_BARE_POSIX` (this file runs before any checkout of the standard exists), and these three words pass it. The family lines (`family_next_command`) are #532's and still take the host's dialect. The new test pins the Windows spelling in-process, with `os.name` and `SHELL_BARE` switched for the calls, and reads each printed line back with `shlex.split`. It is red on c71eadb in all three cases. The existing backstop test now expects the POSIX spelling on every host. `test_what_stands_above_a_clone_path[a-link-to-nothing]` compared the refusal with the link's target as it was given. Since Python 3.8 `os.readlink` on Windows returns the substitute name with a `\\?\` prefix, and the refusal names that, so it now compares with `os.readlink(blocker)`. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-2, 2026-10-15T10:56Z) This lane re-verified its own PR #569 at 7baf16b read-only and authored nothing. MERGEABLE WITH NITS on bfed143. CHANGES NEEDED when main 2247dbf is merged in.
Report: |
…ust_pushed`) so the PR's CI merge ref and this fix round run on current main CI runs on the merge of this PR with main, and it failed on all three OSes: tests/test_scaffold_e2e.py::test_the_next_steps_clone_under_local_remotes_reads_the_legs raised `NameError: name 'FILE_PROTOCOL' is not defined`. #554 rewrote the file's first conftest import without `FILE_PROTOCOL`, and this PR's test still uses it, so the textual merge was clean and the merged file was broken. Resolution: #554's import line stays as it is, and `FILE_PROTOCOL` is imported again on the file's second conftest import line (`from conftest import FILE_PROTOCOL, indented_commands, pasted, read_through_git`). No other change; the merge had no textual conflicts. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…-run`) so the PR's CI merge ref and this fix round run on current main The coordinator fetched at 11:02Z: main moved from 2247dbf to 7d9bec9 with b4c1f50 (#553, shape-doctor.py) and 7d9bec9 (#561, scripts/bump-leg.py), and both changed docs/cli.md. This is the second main merge of the round, on top of the merge of 2247dbf that restored the `FILE_PROTOCOL` import in tests/test_scaffold_e2e.py. No textual conflict: docs/cli.md merged by itself and `scripts/render-cli-reference.py --check` passes on the result, so nothing was re-rendered. No other change. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…y-hand test and #581 among them) into #569, so the PR's CI runs on current main Merges origin/main fb786c1, read from the shared store without a fetch. Main moved from 7d9bec9 (#569's last merge) by #556, #557, #558, #559, #560, #562, #567, #568, #579, #580 and #581. #579 (#550) landed tests/test_setup_by_hand_commands.py, which this PR's `--no-next-steps` makes red; the next commit answers it. The merge itself is clean, and its tree is the pm569 prediction's #581 step (53b6d0f7)'s, so the resolution prepared against the predicted main carries over. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…eal run is the plan's arguments and a final `--no-next-steps`, and returns it without that flag, so #538's flag no longer reds the by-hand guard while the README's and the handbook's lines stay the ones a person runs (#538) #579 (#550) landed this guard after #569's last CI: it runs `plan_and_confirm` and `scaffold` with `run` recorded and holds README.md's and the handbook's by-hand `scaffold-project.py` lines, and `plan == real + " --dry-run"`, to what setup runs. #538 makes setup's real run carry `--no-next-steps`, because step (7) runs the closing clone, `cd` and `make bootstrap` itself, so three tests went red on main's merge (the README's, the handbook's and the plan-and-real one). By hand there is no step (7): the scaffold's closing block is the person's next work, so the documents' lines correctly omit the flag, and no README or handbook word changes (four open PRs touch both, and each landing would force another conflicted merge here). The test says the difference instead: `recorded_scaffold_runs` keeps the argument vectors as built; `what_setup_runs` asserts the plan ends `--dry-run` and the real run is the plan's arguments plus exactly one final `--no-next-steps`, then returns the real run without it, the reason in its docstring and the module's. A flag added to the plan or the real run alone, or the flag moved, still fails. Mutants run: the flag dropped, an extra flag on the real run, and the flag moved first each fail three of the five tests. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…h.py: #569's three #538 blocks sit where #573 inserts nothing, so the merge after #573 lands is clean and needs no new three-OS run (#538) A PURE MOVE; nothing inside a block changes. #569 and #573 both insert at the point after `assert_a_taken_clone_path_is_refused` (conftest.py) and after `test_a_taken_clone_path_is_refused_before_anything` (test_setup_project_py.py and test_setup_sh.py), so once #573 lands, #569 conflicts with main in all three files, and a conflicted merge is a new H0 that needs a full three-OS run (Windows alone takes over an hour and a half). With #569's blocks elsewhere, the merge after #573 is clean and, by ruling 27, needs only Linux and SonarCloud. Moved: tests/conftest.py the `# --- setup's paste lines, its step checks and the upstream guard (#538)` block, to the end of the file tests/test_setup_project_py.py the `# --- #538, on an interpreter alone` wrappers, to just before `# --- setup.sh is a shim, not a second flag list` tests/test_setup_sh.py the `# --- #538, through the shim` wrappers, to just before `# --- self- bootstrap` Checked: each file's lines are the same multiset before and after and so are its top-level definitions; pyflakes is clean; no name the conftest block defines is used at import time outside it (it moved down, so what it needs is still above it); `git merge-tree --write-tree` of this head with the predicted main bd0c6b5c reports no conflict. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-2, 2026-10-15T16:14:10Z) This lane re-verified its own PR #569 at 0b6228f, read-only in its own detached worktrees, and authored nothing. MERGEABLE WITH NITS. Land it after #573. No nit needs a commit.
Tests on 0b6228f: 362 passed, 0 failed ( Merges from 0b6228f are all clean:
The head before the move conflicted in the three moved files. pyflakes finds no new message on any merge. The report is at The coordinator has since edited the PR body for the first two nits: "on this PR's merge with today's main", and the heading is now "README follow-up". Nothing was pushed. |
|
Brings in main bcfa364: #588 (709eec1, POSIX-quoted paste lines and README's Windows sentence) and #569 (bcfa364, setup-project.py's paste lines). One conflict: docs/handbook.html: the seven markers that read README.md § Starting a project in a new organisation (masthead, start, partway, ci, legs, doublepin, park) conflicted, this branch's a0e0f23c against #588's 95f06438. Both moved to the merged section's blob, d77a1e59fe4951aa2a6f96808725d0506d4ce357, as the hygiene check printed. Both sides' prose merged on its own: #588's POSIX-quoting sentence in the Windows panel beside #587's install step, NOTE and macOS sentence. Re-read: nothing in the seven sections is made false by the other side. README.md and docs/cli.md merged cleanly with both sides' hunks, and `render-cli-reference.py --check` passes. README.md is 1449 lines (cap 1449) and AGENTS.md 492 (cap 492, raised by #566 on main). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n-source-unread Merges origin/main 66267f0 so the PR's CI merge ref and the fix that follows run on current main. The fourteen landings since 2bf8fd4: #597, #578, #593, #589 (#449), #586, #573 (#537), #575 (#539), #566 (#528), #571, #588, #569 (#538), #582 (#523), #616 and #576 (#441, #442). Those that touch this PR's files: - adopt-project.py: #589 (`check` recomputes each pin's digest in the clone `plan` read it from: LOCAL_CLONE_RE, _recorded_clone, _why_not_rechecked, _recomputed_pin_finding, _pin_digest_findings), #582 (what `plan`, `execute` and AGENTS.md say around a run) and #566 (a source's own `single-repository.yaml`). - scaffold-project.py and docs/cli.md: #573, #569 and #576 (the `_declaration` docstring: `--pin-source` verifies the first chain link alone, which this PR's N3 wording already says). - tests/test_adopt_pins.py: #589's thirteen cases. - tests/test_scaffold_leg_path_collisions.py: #573. This commit holds the textual resolution only. One hunk conflicted, the `from shape_materialize import (...)` list at adopt-project.py:261. It is resolved as the union of both sides' changes to the list: main's added `pin_digest_from_source` (#589), and this PR's added `DIGEST_FROM_CLONE`, `DIGEST_FROM_GH`, `pin_source_path` and `refuse_an_unread_pin_source` and its removal of `one_printable_line`, whose last caller this PR deleted and to which main added none (keeping it is pyflakes' "imported but unused"). pyflakes adopt-project.py is clean on the merge. Every other file merged without a marker, and `scripts/render-cli-reference.py --check` is ok. The merge alone does not keep #589 working: this PR's `plan` records a bare `local-clone`, which names no clone for #589's `check` to read, and `check` refuses `local-clone (<path>)` as `plan-pin-malformed`. The next commit reconciles the two. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… run on current main The branch was cut at 2989b4c (#573). origin/main is now 396c0ff: seventeen commits later, among them #574 (family.py `add`'s refusals, 841 lines of scripts/family.py), #570 and #576 (scaffold-project.py's `--pin-source`), #569 and #538 (scaffold-project.py's `--no-next-steps`), and several that moved docs/cli.md. `git merge-tree` said clean, and the merge made no conflict: scaffold-project.py, scripts/family.py, scripts/shape_materialize.py and docs/cli.md each merged by themselves. `scripts/render-cli-reference.py --check` passes on the result, so docs/cli.md was not re-rendered, and pyflakes gives on the three .py files exactly the warnings main's own copies give. No other change; the next commit on this branch is the fix round (L2-60, L2-67). Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… the Free-plan hint now named by the root is tested on main's tools (#602) This branch was made from 2989b4c. Main has moved through #582, #584, #591, #569, #572, #570 and the rest since, and changed adopt-project.py and scaffold-project.py. The merge is clean, with no hunk of main's inside this PR's: main's adopt hunks sit away from `_create_leg_remotes` and the new `_free_plan_hint_by_the_root` and `_root_visibility`, and its scaffold hunks (shell_line, pin_source_path, the `--pin-source` text, the NEXT STEPS block) sit away from `_private_leg_note`. `import json`, `run`, `CommandFailed` and `VISIBILITY_CHOICES` are still what adopt-project.py imports, and scripts/repo_shape.py's `free_plan_secret_hint` and `FREE_PLAN_HINT` are byte for byte as they were, so nothing needs re-pinning. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… drive that is not there, step (4) verifies a chain from `--pin-source`, `--pin` no longer takes a flag as its value, and step (5) says a printed plan was refused (#608) (#623) Four defects in setup-project.py's step checks, from the #569 and #573 reviews. Each reproduces on main bcfa364. - Step (3) asked only that what stands above the clone is a folder. A parent at mode 555 passed, all three repositories were created, and step (7) then refused `could not be made a folder (Permission denied)`; a path whose drive is not there passed too. Step (3) now makes a folder in the nearest existing parent and takes it away again, as step (7) does, and refuses by name with the system's reason when the folder cannot be made; a missing drive is refused naming it. Both come before the plan. Taking the probe away is best-effort, and names the folder when it cannot. - Step (4) was given a chain but not the `--pin-source` the scaffold reads its links from, so a chain the plan verified showed `[declared-unverified]` and a chain its tree broke showed no break. The last `--pin-source` now goes to the naming validator as a bare `--link-source`, with a chain of two links at least (a chain of one has no holder to read a source for). An exactly empty one goes as no source at all: since #570 the scaffold refuses `--pin-source ''` as `pin-source-no-path`, so step (4) leaves it to the plan, which refuses it by name, and the validator is never asked about a flag the person never typed (`--link-source=` would end step (4) as `link-source-no-path`, and `.` names a folder the plan never reads). A blank value is a path and goes as typed. - `passed_values` took a following word that starts with `-` as the flag's value, so `--pin --referent-chain x` gave step (4) `--pins=--referent-chain`. It leaves such a word out. - Step (5) said "the plan could not be produced" under a plan that had printed, which #573's dry run does before it refuses an existing remote. After a refusal setup asks the plan once more, captured, and says "the plan was refused" when it printed; a plan that never printed keeps the old words, and a second run that does not refuse says the refusal did not repeat and to run setup again. Closes #608 Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Lane: openreposhape-2 (openRepoShape-2)
Claim: #538 (comment)
What changed
All six lines of #538 and both rides-along, on main
bfed143. Every one reproduces there.cd <clone>and step (7)'s backstop printed their paths bare, so a run with--into "/home/a b"printed lines the shell splits. Both now go throughshell_word, as the family lines do (family.py and siblings.py print commands with unquoted paths, so under a folder with a space themember-git-dir-cachedremedy'srm -rfwould delete the wrong directory, andadd's push line fails #486), and the three words (cd <clone>, and the backstop'scd <parent>and<assembly>) are spelled forshon every host, Windows included (shell_word(..., posix=True)), as the scaffold's own NEXT STEPS clone already is (shell_line(..., "posix")): Printed commands are POSIX-only: quote the bare paste lines in bump-leg.py and update-shape.py, and say in the docs that pasted commands are for a POSIX shell #532 rules the printed commands POSIX-only, and PowerShell's spelling readsC:\work\SampleasC:workSamplein Git Bash. The family lines (family_next_command) are Printed commands are POSIX-only: quote the bare paste lines in bump-leg.py and update-shape.py, and say in the docs that pasted commands are for a POSIX shell #532's and are unchanged. The backstop also names the clone's parent as acdline of its own (finish_by_hand), so the whole block can be pasted.--local-remote-dir, the backstop's clone and the scaffold's own NEXT STEPS clone lacked-c protocol.file.allow=always, so they failed withtransport 'file' not allowed. Both carry it now. The scaffold's line is POSIX-quoted (shell_line(..., "posix"), per the Printed commands are POSIX-only: quote the bare paste lines in bump-leg.py and update-shape.py, and say in the docs that pasted commands are for a POSIX shell #532 ruling). This is rides-along R1 and R2.scaffold-project.pytakes a new--no-next-steps, and setup passes it in step (6), because step (7) runs those steps next.Working trees are atstill prints, since README's rehearsal paragraph (README.md:307) sends people to it.<into>/<Family>or at--intoused to pass step (3). All three repositories were then created, andos.makedirsended the run in aFileExistsErrortraceback. Step (3) now refuses anything other than a folder on the way up from the clone's parent. In step (7), a failedos.makedirs(a race, a permission) is now a refusal that prints the lines to finish by hand.-- --dry-runends at the plan. Setup reads its passthrough the way the scaffold's argparse does: the flag whole, any prefix argparse accepts for it, and nothing after an inner--. With--dry-runthere, the run stops after the plan with exit 0 and asks nothing. Before, step (7) said "The three repositories WERE created" when none were, and left the family folder behind. The usage text says so.--pins=<products>and--referent-chain=taken from the passthrough. A<Domainx><Product>project is then a domain descendant in step (4), as it already was in the plan, where step (4) used to say "no referent pin declared".PASSED_FLAGSrecords the shortest spelling the scaffold accepts for each flag it reads, and a test checks those against the scaffold's own--help.is_upstream_orgcasefolds, so--org OpenSoftand anoriginunderOpenSoft/are refused likeopensoft. The refusal names the casing that was typed.docs/cli.mdis re-rendered for the usage line and the new scaffold flag, andrender-cli-reference.py --checkpasses. Nothing undertemplates/orcontracts/changed, so there is no re-pin.Tests
Main moved while this PR was open, and the branch now carries three rounds of it.
NameError: name 'FILE_PROTOCOL' is not definedintests/test_scaffold_e2e.py::test_the_next_steps_clone_under_local_remotes_reads_the_legs: 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 rewrote the file's firstconftestimport without that name, and this PR's test still uses it. The branch merged main 2247dbf and then 7d9bec9 (dc87f8a); the first merge keeps 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 import line and importsFILE_PROTOCOLagain on the file's secondconftestimport line.5e9e3a4, clean; adopt-project.py:checkfinds, andexecuterefuses before any leg exists, a path the root keeps at the shape's own pin and a--work-dirthis user cannot write in, socontracts/shape-pin.yamlkept in the root and achmod 500work directory no longer passcheckand fail only after both legs are pushed (#524) #581 and ten other landings). It brought README.md and the handbook: the by-hand setup commands dry-run the scaffold with the--visibilityand--elected-bysetup passes, andapply --addnames all five refusals it gives by their codes, each held by a test (#550, #429) #579'stests/test_setup_by_hand_commands.py, which this PR turned red: the test runsplan_and_confirmandscaffoldwithrunrecorded and holds README.md's and the handbook's by-handscaffold-project.pylines, andplan == real + " --dry-run", to what setup runs, and setup's real run now carries--no-next-steps. Three tests failed on this PR's merge with today's main (the README's, the handbook's and the plan-and-real one).df67b46,tests/test_setup_by_hand_commands.pyonly). By hand there is no step (7), so the scaffold's closing block is the person's next work and the documents' lines correctly omit the flag; changing README.md and the handbook instead would put this PR in another conflicted merge with each of the four open PRs that touch both.what_setup_runsnow asserts the plan ends--dry-runand the real run is the plan's arguments plus exactly one final--no-next-steps, then returns the real run without it, the reason in its docstring and the module's.test_setup_runs_the_plan_and_the_real_run_with_the_same_argumentskeepsplan == real + " --dry-run"and asserts the flag on the recorded argv. One-at-a-time mutants ofsetup-project.py(the flag dropped, an extra flag on the real run, the flag moved first) each fail three of the five tests.0b6228f, a pure move: each file's lines are the same multiset, and so are its top-level definitions). setup-project.py: paste lines quote each path, the scaffold's NEXT STEPS wait for step (7), step (3) refuses a file above the clone,-- --dry-runends at the plan, step (4) hears passed pins, and the upstream guard ignores case (#538) #569 and family.py and scaffold-project.py:--dry-runasks the real run's own question about existing remotes, sofamily-remote-existsandscaffold-remote-existscome at the plan, and a missingghisgh-not-found, not a traceback (#537) #573 both insert afterassert_a_taken_clone_path_is_refusedintests/conftest.pyand aftertest_a_taken_clone_path_is_refused_before_anythingintests/test_setup_project_py.pyandtests/test_setup_sh.py, so whichever lands second conflicts in all three files, and a conflicted merge is a new H0 that needs a full three-OS run. The# --- setup's paste lines...helper block now endstests/conftest.py, and the# --- #538wrapper blocks sit before# --- setup.sh is a shim, not a second flag listand# --- self-bootstrap.git merge-tree --write-treeof the head with the predicted mainbd0c6b5c(origin/main 8890f57 plus adopt-project.py:checkfinds, andexecuterefuses before any leg exists, a path the root keeps at the shape's own pin and a--work-dirthis user cannot write in, socontracts/shape-pin.yamlkept in the root and achmod 500work directory no longer passcheckand fail only after both legs are pushed (#524) #581, scaffold-project.py: a legghsees and git cannot read is refusedscaffold-remote-existswith git's terminal prompt off, and the reuse re-run's visibility lines go by each leg as it stands (#542) #565 and family.py and scaffold-project.py:--dry-runasks the real run's own question about existing remotes, sofamily-remote-existsandscaffold-remote-existscome at the plan, and a missingghisgh-not-found, not a traceback (#537) #573) is conflict-free, and the merge made from it was never pushed.Final targeted run on the head
0b6228f, in two runs: 2005 passed, 22 skipped, 0 failed. Run A, everytests/test_setup*.py(this includestest_setup_by_hand_commands.py, now green),tests/test_family_init*.py,test_windows_paths,test_openreposhape_command,test_family_printed_quoting,test_cli_referenceandtest_repo_hygiene: 592 passed, 19 skipped. Run B, everytests/test_scaffold*.py,test_family,test_reuse_visibility, the fivetest_adopt_*.pyfiles #581 touched andtest_shape_doctor: 1413 passed, 3 skipped. The same two runs on the predicted merge with #565, #573 and #581 (bd0c6b5c, sotests/test_dry_run_asks_remotes.pyand #573's two plan tests are in): 2053 passed, 22 skipped, 0 failed (A: 594 passed, 19 skipped; B, 20 files: 1459 passed, 3 skipped).render-cli-reference.py --checkpasses on both.The new tests are shared helpers in
tests/conftest.py, so the twin tests are not duplicated code (Sonar). Each helper has a one-line test in bothtests/test_setup_project_py.pyandtests/test_setup_sh.py, 14 per file:cd(a run with a space in--into; the line is split withshlexand run insh);-- --work-dir <into>; its lines are moved aside, pasted intoshand finish the clone and the bootstrap;-- --dry-run/--dry, with and without--yes, ×3;--pin,--pin=and--referent-chainreach step (4), ×3;OpenSoftandOPENSOFT, ×2;OpenSoft/origin.There is also one scaffold test (the NEXT STEPS clone run in
sh), plus 24 in-process tests: the passthrough readers, the drift guard onPASSED_FLAGS, what stands above a clone path, step (7)'s makedirs refusal, and one test with three cases that pins the Windows spelling of the changed paste lines. That one lives intests/test_setup_project_py.pyonly. It switchesos.nameandSHELL_BAREfor the calls, asshell_wordreads them, and reads each printed line back withshlex.split, as Git Bash would. The existing backstop test is updated for the new lines, andtest_what_stands_above_a_clone_path[a-link-to-nothing]compares withos.readlink(blocker), because on Windowsos.readlinkreturns the\\?\form.Red on main: 51 of the 53 new tests and both updated ones fail on
bfed143. The two that pass are controls: a link to a folder, and nothing on the path. The Windows-spelling test is red onbfed143(finish_by_handis not there) and onc71eadb(all three cases: PowerShell's spelling,cd C:\work\SampleandJane O''Neill). Each failure is the defect itself: exit 1 tracebacks, exit 128 "WERE created", exit 0 scaffolding intoOpenSoft, "project-leg / assembly" in step (4), and lines split at the space.Mutants: 14 of 14 killed. One at a time, the change undid: the casefold, the
--dprefix, the inner--handling, the backstop's-c, the--no-next-stepsthat setup passes and the one the scaffold honours, the step (3) check above the clone path,isdirin that check, the last-chain rule, the quotedcd, the scaffold's-c, the makedirs refusal, the declarations reaching step (4), and the dry-run stop.On
7baf16b, droppingposix=Truefrom each of the three words in turn (cd <clone>,cd <parent>,<assembly>) fails the Windows-spelling test: 3 of 3 killed.README follow-up
None required: README.md:338 and AGENTS.md:29-33 still say what the guard refuses. Optionally, README.md:338 could add "in any case" after
--org opensoft.The by-hand lines in README.md's "What setup.sh does" and the handbook's "What
setup.shactually runs" omit setup's--no-next-stepson purpose: by hand there is no step (7), so the scaffold's closing block (thegit clone, thecdandmake bootstrap) is the person's next work.tests/test_setup_by_hand_commands.pystates the difference, so no word of either document needs to change. If a later README change wants to say it, one sentence under that list is enough.Closes #538
🤖 Generated with Claude Code
Summary by Sourcery
Harden setup-project.py and scaffold-project.py for safe dry runs, reliable pasted commands, robust path handling, and consistent validation.
New Features:
Bug Fixes:
Documentation:
Tests: