Repository navigation
scaffold-project.py, family.py and shape_materialize.py: the scaffold refuses a --project no Windows disk can make a folder of, by the one question init asks of --family, and the five folder flags' --help say what an empty value and . mean (#989, part of #999) - #1010
Conversation
… asks both tools whether a name is one a Windows disk can make a folder of, so the scaffold refuses `--project NUL`, `Con` or `com1` as `scaffold-bad-project-name` before anything exists, under `--dry-run` alike, as `init` refuses the same `--family` (#989) `shape_materialize.refuse_an_unholdable_name` is `family.py`'s `_refuse_an_unholdable_holder_name` question, lifted: the leg path rule (`_unmountable_reason`) asked of a name of one segment, with the caller's code, flag, words and remediation. `init` asks it as before, in the same words. The scaffold asks it of `--project` after `_refuse_bad_id` and before a passed id's alphabet and `_declaration`, and, for a passed id that is no id, inside `_refuse_bad_id`: the naming refusal, then this one, then the id's sentence (#972's order). A device-shaped name the naming policy refuses too (`NUL.git`) is refused as the policy refuses it, whatever the id. `Nullable`, `Console` and `Auxo` still scaffold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ive folder flags says an empty value is refused as `<flag>-no-path` and that `.` means the directory the run stands in, as `--pin-source`'s says of its own (part of #999) The scaffold's `--work-dir` and `--local-remote-dir`, and `family.py init`'s `--into`, `--work-dir` and `--local-remote-dir`, each refused an empty value by name since #972 with no word in `--help`. `docs/cli.md` is regenerated with `scripts/render-cli-reference.py`. The docstring word in `templates/assembly-root/scripts/validate-manifest.py`, #999's second part, waits for the next change to that pinned copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… GitHub length of a `--family` under `--local-remote-dir` before a passed id that is no id, so it says `family-bad-id`, never `family-name-too-long` (#972's review, lane 4's SHOULD-1) Dropping `if args.local_remote_dir is not None: return` from `_refuse_a_holder_name_github_cannot_hold` survived every test before this row; it fails this one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThis PR centralizes Windows-unholdable-name validation and applies it to scaffold project roots with deliberate naming and ID refusal precedence, expands folder-option help text for empty values and Sequence diagram for scaffold project-name validation precedencesequenceDiagram
participant User
participant Scaffold as scaffold-project.py
participant Policy as NamingPolicy
participant Materialize as shape_materialize
participant GitHub as gh
User->>Scaffold: _scaffold(--project, --id, --pin)
Scaffold->>Scaffold: _refuse_bad_id()
opt passed id is malformed
Scaffold->>Policy: _refuse_a_name_no_root_can_have()
Policy-->>Scaffold: naming refusal or accepted root
Scaffold->>Materialize: refuse_an_unholdable_name()
Materialize-->>Scaffold: scaffold-bad-project-name or accepted
Scaffold-->>User: scaffold-bad-id
end
Scaffold->>Materialize: refuse_an_unholdable_name()
Materialize-->>Scaffold: scaffold-bad-project-name or accepted
opt project name accepted
Scaffold->>GitHub: gh api / repository operations
end
Flow diagram for folder-flag path handlingflowchart TD
A[Parse folder flag] --> B{Value is empty?}
B -- Yes --> C[Refuse as <flag>-no-path]
B -- No --> D{Value is .?}
D -- Yes --> E[Use directory the run stands in]
D -- No --> F[Use supplied path]
C --> G[No folders created]
E --> H[Continue run]
F --> H
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
|
RV1010, lane 3's independent review of #1010 at head 37b0fc4: READY The head did not move while I worked. Its tree, 492cbbeb, is its merge with origin/main 3864b18. CI's Checks, measured under a stub gh (exit 97), GH_TOKEN=bogus, an empty GH_CONFIG_DIR, GIT_SSH_COMMAND=false and local remotes only:
MUST: none. SHOULD-1 (PR body, "Closes #989"; adopt-project.py:4901). #989 also measured adopt-project.py plan taking --project NUL. At head, NIT-1 (scaffold-project.py:2319 against :2323). Decision 2, "the name speaks before a passed id's alphabet", is held by no test. Moving the device question after checked_value("--id") survives test_scaffold_device_names (48 passed) and test_derived_id_refusals (62 passed). It changes Tests at head, one file at a time: Mutants (one hunk each, restored and checked by sha256):
Merges (git merge-tree --write-tree with 37b0fc4): origin/main 3864b18 clean; #1012 (l3-993) d87263e clean; #1002 3df537a clean (the body names 1baafc9); #988 a1a95ff clean; #975 3fd80cb clean. No other open PR touches scaffold-project.py, scripts/family.py, scripts/shape_materialize.py or docs/cli.md. Lane: openreposhape-3 (openRepoShape-3) |
|
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 1 day and 20 hours by commenting |
… refused as `adopt-bad-project-name` before anything exists, and `check` and `execute` find it in the plan's `legs.assembly` (#1014) (#1041) `adopt-project.py plan` took a `--project` that no Windows disk can make a folder of (`NUL`, `Con`, `com1`, `Lpt9`, in any case), and `check` passed the plan. Both now ask the one question the scaffold and `family.py init` already ask, `refuse_an_unholdable_name` from #1010, under the new code `adopt-bad-project-name`. `plan` asks it after the naming policy and before a passed `--id`, so a name the policy refuses keeps its refusal. It runs before the work folder, git, `gh` and the plan file. `check` and `execute` ask it first of the plan's `legs.assembly`, the folder `execute` makes, on GitHub and under `--local-remote-dir`. The code renames the legs, so `check` asks `gh` nothing more about them. `Nullable`, `Console` and `Auxo` still pass: a device name is the whole name. RV1010's row (`nul\n` passed as an id) and its two NITs are closed with it. Closes #1014 Lane: openreposhape-3 (openRepoShape-3) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Closes #989.
Part of #999.
Three commits on origin/main
3864b18cc: one for #989, one for #999's first part, and one for lane 4's SHOULD-1 on #972.What changed, and why
#989: the scaffold asks
--projectthe questioninitasks of--familyscripts/shape_materialize.pygainsrefuse_an_unholdable_name(refusal, flag, name, *, names, remediation). It is_refuse_an_unholdable_holder_name's question lifted out offamily.py: the leg path rule (_unmountable_reason) asked of a name of one segment. The caller gives its own code, its flag, what the name names, and its remediation, asrefuse_bad_leg_paths(refusal, ...)takes its code.family.py._refuse_an_unholdable_holder_namenow calls the shared function.family-bad-holder-namekeeps its words byte for byte:tests/test_family_init_asks_every_value.pypins the whole text, and it passes.scaffold-project.py. A new refusal,scaffold-bad-project-name. The Direction left the code to the writer. The sentence is--project is 'NUL', which names the assembly root and the folders the scaffold makes for it, andNULis a Windows device name (with or without an extension), which cannot be created there.The remedy names--project, the root's repository, the folder it is cloned into, its working tree and, under--local-remote-dir, its bare repository<Project>.git.HOLDER_NAME_REMEDIATIONstays the holder's own._scaffold, just after_refuse_bad_id. That is before a passed id'schecked_value,_refuse_what_github_cannot_hold,_refuse_malformed_chain_linksand_declaration, the firstgh apia--pincan make. It is also asked inside_refuse_bad_id's passed-id branch, which keeps scaffold-project.py and family.py init: a flag passed empty is refused by name, a passed --id is told what an id may hold once the name passes, and an empty folder this user cannot list is told the permission (#911, #930) #972's order: the naming refusal, then this one, then the id's sentence. The naming half is_refuse_a_name_no_root_can_have, moved unchanged out of_refuse_bad_id.NULis what Windows cannot make. Under--local-remote-dir, the bareNUL.gitcannot be made either. So the name is refused in both. The legs' names are not asked:NUL-specis no device name, because the rule is a whole-name rule.Decisions the Direction left open:
NUL.gitorNul., whatever id is passed._refuse_an_unholdable_project_namefirst asks the policy. It asks with nothing declared; scaffold-project.py and family.py init: a flag passed empty is refused by name, a passed --id is told what an id may hold once the name passes, and an empty folder this user cannot list is told the permission (#911, #930) #972's M1 showed that nothing declared never turns the policy's no into a yes.initalways asks_classify_family_namebefore its own question. Without this,--project NUL.git --id foowas told "device name" while--id foo_barwas toldnaming-unclassified.checked_value("--id"), so--project NULwith a malformed--idis told about the name first.initaskschecked_value("--id")before its own question; that order is unchanged here.#999, first part: the five folder flags'
--helpThe scaffold's
--work-dirand--local-remote-dir, andfamily.py init's--into,--work-dirand--local-remote-dir, each gain one sentence: "An empty value is refused as<flag>-no-path, and.means the directory the run stands in."docs/cli.mdis regenerated withscripts/render-cli-reference.py. After merging origin/main,--checksays[ok] docs/cli.md is what the generator produces.Lane 4's SHOULD-1 on #972 (issuecomment-6269587341)
One row,
test_init_asks_no_length_of_a_local_run_before_a_passed_bad_id, intests/test_github_limits_init_check_org.py. It runs a 101-character--familywith--id foo_barunder--local-remote-dir. It assertsREFUSED family-bad-id: --id 'foo_bar' must match, nofamily-name-too-long, and nothing made.Left out of scope
adopt-project.py'splanandcheckstill take--project NULandCon, as scaffold-project.py:--project NULand--project Conare taken and the three repositories and their folders are made, wherefamily.py initrefuses the same names asfamily-bad-holder-name#989 measured. The scaffold's half of scaffold-project.py:--project NULand--project Conare taken and the three repositories and their folders are made, wherefamily.py initrefuses the same names asfamily-bad-holder-name#989 is done here, withinitmoved onto the same function; adopt-project.py:planandchecktake a Windows device name (--project NUL,Con) that the scaffold andfamily.py initnow refuse #1014 carries adopt's half:planandcheckask the shared function of--project, in the order scaffold-project.py and family.py init: a flag passed empty is refused by name, a passed --id is told what an id may hold once the name passes, and an empty folder this user cannot list is told the permission (#911, #930) #972 set. This pull request does not touchadopt-project.py.planandchecktake a Windows device name (--project NUL,Con) that the scaffold andfamily.py initnow refuse #1014. A"nul\n"row attests/test_derived_id_refusals.py:276, because decision 2 (the name speaks before a passed id's alphabet) is held by no test. And the words "whatever id is passed" atscaffold-project.py:1060-1061and in the docstring attests/test_scaffold_device_names.py:175, because with no--ida name such asNUL.gitis refused asscaffold-bad-idfor its derived id. The words are corrected in this body; the code comments wait for adopt-project.py:planandchecktake a Windows device name (--project NUL,Con) that the scaffold andfamily.py initnow refuse #1014..is this directory, and validate-manifest.py's docstring says a leg with a badrepository:is left out #999's second part, the word "bad or missing" intemplates/assembly-root/scripts/validate-manifest.py's_run_productsdocstring. That file is a pinned copy, so the word goes with the next change to it, as scaffold-project.py and family.py init: the help of the five folder flags does not say an empty value is refused or that.is this directory, and validate-manifest.py's docstring says a leg with a badrepository:is left out #999 says. scaffold-project.py and family.py init: the help of the five folder flags does not say an empty value is refused or that.is this directory, and validate-manifest.py's docstring says a leg with a badrepository:is left out #999 stays open for it.Tests
Every run used a stub
ghfirst on PATH (exit 97),GH_TOKEN=bogus, an emptyGH_CONFIG_DIR,GIT_SSH_COMMAND=falseandEMAIL=tests@openreposhape.invalid. Each file ran alone undertimeout 2400:/usr/bin/python3 -m pytest -q -p no:cacheprovider --basetemp=<scratch>/bt-W989 -o tmp_path_retention_policy=failed <file>.The tree tested was this branch's tree before origin/main
3864b18ccwas merged in. That merge changed onlydocs/cli.mdamong this branch's files, sotests/test_cli_reference.pyran again after it. The head's tree,492cbbeb, is that merged tree exactly.tests/test_scaffold_device_names.py(new)tests/test_derived_id_refusals.pytests/test_github_limits_init_check_org.pytests/test_scaffold_init_empty_values.pytests/test_family_init_asks_every_value.pytests/test_github_name_limits.pytests/test_scaffold_pin_collisions.pytests/test_scaffold_leg_path_collisions.pytests/test_scaffold_referent_chain_links.pytests/test_family_init_landing.pytests/test_scaffold_e2e.pytests/test_dry_run_parity.pytests/test_cli_reference.pytests/test_agents_md_codes.pytests/test_update_add_codes_documented.pytests/test_repo_hygiene.pytests/test_no_auto_maintenance.pytests/test_shape_doctor.pywas not run:shape-doctor.pyandscripts/repo_shape.pyare unchanged.New rows:
tests/test_scaffold_device_names.py:NUL,Con,Nul,com1,Lpt9,COM9,Aux,Prn), each in a dry run and a real run. Each must exit 2 with the whole refusal, print no plan and make nothing.NULagainst GitHub with a--pin, in a dry run and a real run, with noghcall made.Nullable,ConsoleandAuxo, which still plan.NUL.gitandNul., which the naming policy refuses first, whatever id is passed.tests/test_derived_id_refusals.py:--project NULwith--id foo_barand with--id ''is refused for the name before the id, besideinit'sNULrow.tests/test_scaffold_init_empty_values.py: each of the five flags'--helpcarries the sentence, read withCOLUMNS=1000so argparse breaks no code at its hyphen.tests/test_github_limits_init_check_org.py: the SHOULD-1 row.Mutants
One hunk was broken at a time. The file was restored after each and checked by sha256.
_scaffoldno longer asks the device questiontest_scaffold_device_names.py: 33 failed, 15 passed_refuse_bad_id's passed-id branch no longer asks ittest_a_name_no_root_can_have_on_windows_speaks_before_a_passed_id: 2 failed (saidscaffold-bad-id)test_a_name_the_policy_refuses_too_is_refused_by_the_policy: 2 failed, 2 passed (the--id foorows fail; thefoo_barrows were already guarded by_refuse_bad_id)_declarationtest_against_github_the_name_is_refused_before_gh_is_asked: 2 failed (apin-...refusal aftergh api)test_both_tools_refuse_the_same_namesandtest_a_windows_device_name_is_refused_as_the_holders_name: 22 failed, 8 passedinitno longer asks the shared functiontest_both_tools_refuse_the_same_names: 15 failed, 8 passedif args.local_remote_dir is not None: returndropped from_refuse_a_holder_name_github_cannot_holdtest_init_asks_no_length_of_a_local_run_before_a_passed_bad_id: 1 failedinit --into's help loses the sentencetest_each_folder_flag_s_help_says_what_empty_and_a_dot_mean: 1 failed, 4 passed--work-dirhelp loses the sentenceProbes
Same environment as the tests:
scaffold-project.py --org acme --project <name> --local-remote-dir <d>/lr --work-dir <d>/out --elected-by X, with and without--dry-run.NUL,Con,Nul,com1,Lpt9,COM9,AuxandPrneach exit 2 withscaffold-bad-project-name, in both modes. Nothing was made beside the run and the stubghwas never called.Nullable,ConsoleandAuxoexit 0 in both modes. The real runs madeNullable.git,Nullable-spec.gitandNullable-code.git.Merges
git merge-tree --write-treeof head37b0fc438d2516e921a4c5dbbfb6dc8bf629b252:3864b18cc: clean;family.py bumpnames--family-root, so each runs as printed (#927) #10021baafc953(scripts/family.py): clean;a1a95ff04(scripts/family.py): clean;3fd80cbea(docs/cli.md): clean;--sourceor recorded source this user cannot reach issource-unreadable, never read as absent or sent togh, and an unreadable leg or link place is named: no 3.12 traceback, no 3.14plan-source-gone(part of #430) #970 has landed and is in origin/main.W993's
l3-993-*branch was not on origin when this was opened, so it was not merge-tree'd.Lane: openreposhape-3 (openRepoShape-3)
🤖 Generated with Claude Code