Skip to content

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

Merged
brettheap merged 8 commits into
mainfrom
fix/538-setup-paste-and-checks
Oct 15, 2026

Conversation

@brettheap

@brettheap brettheap commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

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.

  • Paste lines are one word per path. The hand-over's 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 through shell_word, as the family lines do (family.py and siblings.py print commands with unquoted paths, so under a folder with a space the member-git-dir-cached remedy's rm -rf would delete the wrong directory, and add's push line fails #486), and the three words (cd <clone>, and the backstop's cd <parent> and <assembly>) are spelled for sh on 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 reads C:\work\Sample as C:workSample in 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 a cd line of its own (finish_by_hand), so the whole block can be pasted.
  • The two local recursive clones can read their legs. Under --local-remote-dir, the backstop's clone and the scaffold's own NEXT STEPS clone lacked -c protocol.file.allow=always, so they failed with transport '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.
  • The scaffold's NEXT STEPS stay out of a setup run. scaffold-project.py takes a new --no-next-steps, and setup passes it in step (6), because step (7) runs those steps next. Working trees are at still prints, since README's rehearsal paragraph (README.md:307) sends people to it.
  • A file above the clone path is refused in step (3). A file at <into>/<Family> or at --into used to pass step (3). All three repositories were then created, and os.makedirs ended the run in a FileExistsError traceback. Step (3) now refuses anything other than a folder on the way up from the clone's parent. In step (7), a failed os.makedirs (a race, a permission) is now a refusal that prints the lines to finish by hand.
  • -- --dry-run ends 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-run there, 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.
  • Step (4) sees the pins passed through. The naming validator is now asked with --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_FLAGS records the shortest spelling the scaffold accepts for each flag it reads, and a test checks those against the scaffold's own --help.
  • The upstream guard ignores case. is_upstream_org casefolds, so --org OpenSoft and an origin under OpenSoft/ are refused like opensoft. The refusal names the casing that was typed.

docs/cli.md is re-rendered for the usage line and the new scaffold flag, and render-cli-reference.py --check passes. Nothing under templates/ or contracts/ changed, so there is no re-pin.

Tests

Main moved while this PR was open, and the branch now carries three rounds of it.

Final targeted run on the head 0b6228f, in two runs: 2005 passed, 22 skipped, 0 failed. Run A, every tests/test_setup*.py (this includes test_setup_by_hand_commands.py, now green), tests/test_family_init*.py, test_windows_paths, test_openreposhape_command, test_family_printed_quoting, test_cli_reference and test_repo_hygiene: 592 passed, 19 skipped. Run B, every tests/test_scaffold*.py, test_family, test_reuse_visibility, the five test_adopt_*.py files #581 touched and test_shape_doctor: 1413 passed, 3 skipped. The same two runs on the predicted merge with #565, #573 and #581 (bd0c6b5c, so tests/test_dry_run_asks_remotes.py and #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 --check passes 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 both tests/test_setup_project_py.py and tests/test_setup_sh.py, 14 per file:

  • the closing cd (a run with a space in --into; the line is split with shlex and run in sh);
  • the backstop, reached because the scaffold is given -- --work-dir <into>; its lines are moved aside, pasted into sh and finish the clone and the bootstrap;
  • NEXT STEPS absent;
  • a file above the clone path, ×2;
  • -- --dry-run / --dry, with and without --yes, ×3;
  • --pin, --pin= and --referent-chain reach step (4), ×3;
  • OpenSoft and OPENSOFT, ×2;
  • an 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 on PASSED_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 in tests/test_setup_project_py.py only. It switches os.name and SHELL_BARE for the calls, as shell_word reads them, and reads each printed line back with shlex.split, as Git Bash would. The existing backstop test is updated for the new lines, and test_what_stands_above_a_clone_path[a-link-to-nothing] compares with os.readlink(blocker), because on Windows os.readlink returns 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 on bfed143 (finish_by_hand is not there) and on c71eadb (all three cases: PowerShell's spelling, cd C:\work\Sample and Jane O''Neill). Each failure is the defect itself: exit 1 tracebacks, exit 128 "WERE created", exit 0 scaffolding into OpenSoft, "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 --d prefix, the inner -- handling, the backstop's -c, the --no-next-steps that setup passes and the one the scaffold honours, the step (3) check above the clone path, isdir in that check, the last-chain rule, the quoted cd, the scaffold's -c, the makedirs refusal, the declarations reaching step (4), and the dry-run stop.

On 7baf16b, dropping posix=True from 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.sh actually runs" omit setup's --no-next-steps on purpose: by hand there is no step (7), so the scaffold's closing block (the git clone, the cd and make bootstrap) is the person's next work. tests/test_setup_by_hand_commands.py states 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:

  • Add scaffold support for suppressing its closing NEXT STEPS block when invoked by setup-project.py.
  • Make setup-project.py stop cleanly at the plan when a passed-through dry-run flag is detected.

Bug Fixes:

  • Quote generated pasteable paths for POSIX shells, including Windows paths, and allow local recursive clones to use file-based remotes.
  • Reject files blocking the clone path before creation and turn step-seven directory-creation failures into actionable refusals.
  • Pass through pin and referent declarations to naming validation and make upstream-organization checks case-insensitive.

Documentation:

  • Update the CLI reference for passed-through dry runs and the scaffold's new option.

Tests:

  • Add shared end-to-end and unit coverage for command quoting, local clones, dry runs, path validation, naming declarations, and case-insensitive upstream guards.

…, 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>
@sourcery-ai

sourcery-ai Bot commented Oct 14, 2026

Copy link
Copy Markdown

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 @sourcery-ai review. Upgrade to get a review now.

@sourcery-ai

sourcery-ai Bot commented Oct 14, 2026

Copy link
Copy Markdown

Reviewer's Guide

Updates 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 coordination

sequenceDiagram
    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
Loading

Sequence diagram for local recursive clone commands

sequenceDiagram
    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)
Loading

Flow diagram for clone-path validation before side effects

flowchart 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]
Loading

File-Level Changes

Change Details Files
Make setup-project's generated commands safe to paste and complete for paths with spaces and local file remotes.
  • Quote clone, cd, and parent paths with shell-aware formatting.
  • Add Git's file-protocol override to recursive local clones.
  • Centralize the step (7) manual recovery commands and improve the race/permission refusal path.
setup-project.py
scaffold-project.py
tests/conftest.py
tests/test_scaffold_e2e.py
tests/test_setup_project_py.py
tests/test_setup_sh.py
Align setup orchestration with scaffold behavior so dry runs and NEXT STEPS do not perform or duplicate work.
  • Add scaffold-project's --no-next-steps option and pass it from setup while retaining the working-tree location output.
  • Parse passthrough flags through an inner --, recognize accepted prefixes, and stop setup after the plan for dry runs.
  • Pass pin products and the final referent chain into the step (4) naming validator.
setup-project.py
scaffold-project.py
docs/cli.md
tests/conftest.py
tests/test_setup_project_py.py
tests/test_setup_sh.py
Strengthen clone-path validation and upstream-organization protection.
  • Reject files or non-directory symlinks above a prospective clone before any setup work.
  • Casefold upstream organization comparisons across explicit and origin-derived values and preserve typed casing in refusals.
setup-project.py
tests/conftest.py
tests/test_setup_project_py.py
tests/test_setup_sh.py
Add documentation and broad regression coverage for both setup entry points and scaffold execution.
  • Document passthrough dry-run semantics and the new scaffold option.
  • Share helper-based tests across setup-project.py and setup.sh, including command pasteability, path blockers, dry-run stopping, passthrough declarations, and case-insensitive upstream guards.
  • Add an end-to-end test for scaffold NEXT STEPS under local remotes and a drift check for accepted flag spellings.
docs/cli.md
tests/conftest.py
tests/test_scaffold_e2e.py
tests/test_setup_project_py.py
tests/test_setup_sh.py

Assessment against linked issues

Issue Objective Addressed Explanation
#538 Make setup's printed commands safe and accurate to paste: quote paths in the hand-over and step-(7) backstop, keep the scaffold's redundant NEXT STEPS out of setup runs, and allow the printed recursive clones to work with local remotes. ✅
#538 Correct setup's step checks: refuse a non-directory blocking the clone path before creating repositories, stop cleanly at the plan when scaffold passthrough requests a dry run, and include passed pins and referent-chain declarations in the step-(4) naming check. ✅
#538 Refuse the upstream organization regardless of letter casing, both when explicitly supplied and when identified from the origin. ✅

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@brettheap

Copy link
Copy Markdown
Contributor Author

(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.

  1. P3, fix here: the cd and backstop paths (setup-project.py:2377, :2176) go through shell_word, which quotes for PowerShell on Windows. Git Bash reads cd C:\work\Sample as C:workSample. 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 POSIX-only, as this PR's scaffold line already is (scaffold-project.py:1553).
  2. Nit, fix here: tests/test_setup_project_py.py:2016 compares the whole os.readlink target. On Windows that target carries a \\?\ prefix (see :1907).
  3. Follow-ups for Tracking: setup, scaffold, family and the shared helpers, creating projects and families #459:
    • Step (4) is not given --pin-source as --link-source. A chain's status can differ from the plan's, but the classification agreed in all 12 probes.
    • Step (3) passes an unwritable parent.
    • --no is now ambiguous.

Probes:

  • 45 of 45 paste lines ran in sh, bash and zsh -f.
  • 39 --dry-run spellings agreed with argparse.
  • The upstream guard refused OpenSoft in --org and in https, git@ and ssh:// origins.

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. docs/cli.md re-renders clean on every merge.

Report: opensoft/brett-wip handoffs/xFactory/attachments/lane-openRepoShape-2/review569-c71eadb/final-report.md.

…`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>
@brettheap

Copy link
Copy Markdown
Contributor Author

(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: opensoft/brett-wip handoffs/xFactory/attachments/lane-openRepoShape-2/review569-c71eadb/reverify-7baf16b.md.

Brett Heap and others added 2 commits October 15, 2026 10:59
…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>
Brett Heap and others added 3 commits October 15, 2026 15:31
…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>
@brettheap

Copy link
Copy Markdown
Contributor Author

(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 (test_setup*, test_scaffold_e2e, test_cli_reference, test_repo_hygiene). On the predicted final merge 103b64d6, a spot check passed 84.

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 handoffs/xFactory/attachments/lane-openRepoShape-2/review569-0b6228f/final-report.md in opensoft/brett-wip.

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.

@sonarqubecloud

Copy link
Copy Markdown

@brettheap
brettheap merged commit bcfa364 into main Oct 15, 2026
3 checks passed
@brettheap
brettheap deleted the fix/538-setup-paste-and-checks branch October 15, 2026 19:02
brettheap pushed a commit that referenced this pull request Oct 15, 2026
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>
brettheap pushed a commit that referenced this pull request Oct 15, 2026
…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>
brettheap pushed a commit that referenced this pull request Oct 15, 2026
… 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>
brettheap pushed a commit that referenced this pull request Oct 15, 2026
… 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>
brettheap added a commit that referenced this pull request Oct 16, 2026
… 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

setup-project.py: its paste lines, the step (3), (4) and (7) checks, and the upstream-org guard

1 participant