Repository navigation
shape-doctor.py: a validator row prints the PYTHONPATH it ran the standard's TEMPLATE copy under, in sh's or PowerShell's dialect, and runs the root's OWN copy under none, so the manifest, pins and family rows' NEXT, pasted anywhere, prints the validator's own finding and exits with detail.exit instead of dying on ModuleNotFoundError: No module named 'repo_shape', and a root that lost its own repo_shape.py is no longer told its gate passes (#238) - #287
Conversation
…y prints the `PYTHONPATH` it ran it under in its NEXT, in sh's or PowerShell's dialect, so pasting the `manifest`, `pins` or `family` row's NEXT prints the validator's own finding and exits with `detail.exit` instead of dying on `ModuleNotFoundError: No module named 'repo_shape'` with exit 1, a finding's code (#238) A root with no `scripts/validate-<x>.py` of its own is checked by the standard's template copy, and the row's NEXT named that template as a bare command. The template imports `repo_shape` from beside itself, and `repo_shape.py` is copied INTO a project out of the standard's `scripts/`, so the doctor's own child ran it only because `run_validator` puts `<standard>/scripts` on the child's `PYTHONPATH`. The printed line was a different process from the one whose output the row quoted: pasted from any directory with no `PYTHONPATH` in the environment it printed a traceback and exited 1, which is also what a validator exits with for "findings", so anything keyed on the exit code read a crash as the finding the row had already reported. A reader whose shell exported a `PYTHONPATH` reaching the standard never saw it. The fix is in the one definition #205 split out, not in a row. `validator_for` now also returns whether the validator is the root's own copy, and a new `validator_command` spells the line from that, for the reader's platform, the way `copy_command` and `scaffold_command` already do (`platform` is passed on, `None` is the host): sh PYTHONPATH=<standard>/scripts python3 <template> --root <root> PowerShell $env:PYTHONPATH = '<standard>\scripts'; python <template> --root <root> `validator_row` (the `manifest`, `pins` and `family` rows) and `unloaded_manifest_row` both build their NEXT through it, so there is no per-row patch. The root's own copy is unchanged byte for byte: it sits beside its own `repo_shape.py`, and an environment in front of it would send a project's pinned gate at the standard's `repo_shape.py` instead. `validator_scripts` names the directory once, for `run_validator`'s `env` and for the printed line, so the two cannot name different places. Decisions the issue left open: - The PowerShell value is quoted whether or not `quote_arg` would leave it bare. What follows `=` is a pipeline, so `$env:PYTHONPATH = C:\shape\scripts` would RUN the path as a command. `quote_arg` takes a keyword-only `always` for that position and nothing else changes, so the doubling of an apostrophe stays in the one function that knows it. - ONE directory, assigned, not spliced: the doctor's child prepends to the reader's `PYTHONPATH`; a printed line cannot see it and sets only the path the import needs. `PYTHONIOENCODING` and `PYTHONDONTWRITEBYTECODE` are the doctor's own, for quoting prose and for writing nothing, and are not printed. - PowerShell's `$env:` is the session's, so the assignment outlives the line. That is the dialect's property; undoing it would delete a `PYTHONPATH` the reader may have meant to keep. - The row's reason is untouched: it already says "the standard's <template>". Tests (tests/test_shape_doctor.py): for a root with no validator of its own and a gate that fails on a file that still loads (a wrong `topic:`, a zeroed pin digest, a `Bad Id` family id), the row's NEXT is pasted into `sh -c` from the filesystem root with `PYTHONPATH` removed and must print the finding, exit with `detail.exit` and show no traceback; the same line without its environment is shown to die on the import, so the test can see the defect. The same for `unloaded_manifest_row` over each manifest breakage and a holder; from a standard checked out at a path with a space and an apostrophe; `--json` and the text report carry one line; the root's own copy is asserted byte for byte in both rows; and the Windows spelling is asserted as text through the builder with `platform="nt"`, the doctor's own spelling of the platform, including the apostrophe doubling and the always-quoted value. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… prints and under no other, so only the standard's TEMPLATE copy gets `<shape>/scripts`, and a root's OWN validator that cannot import `repo_shape` from beside itself is a FINDING quoting the validator's `ModuleNotFoundError` instead of a "passes" its own gate would never give (#238) Review S1 (fix round 1). `run_validator` put `<shape>/scripts` on the child's `PYTHONPATH` for EVERY validator run, while `validator_command` printed it only for the standard's template copy. While a root keeps `scripts/repo_shape.py` beside its own validator the difference is inert, because the validator inserts its own directory first. A root that lost that file got a row that quoted what the doctor's child saw -- a pass, or the validator's own finding -- over a gate that dies on `ModuleNotFoundError` when the project runs it, and a pasted line that did exactly that. `validator_scripts(shape, own_copy)` is now the one place the CONDITION is decided as well as the directory: it answers the standard's `scripts/` for the template copy and None for the root's own. `validator_row` hands that to `run_validator` (a new `pythonpath` argument, off unless a caller asks) and `validator_command` prints from the same answer, each asked with the one `own_copy` that `validator_for` returned, so there is no second test of it to drift. The own-copy NEXT line stays byte-identical. The two docstring sentences the review quotes ("the line and the run cannot name two different places", "it sits beside its own `repo_shape.py`") are rewritten so each is true. Decision the brief left open: the other `run_validator` callers (`check_naming`, `check_not_a_root_naming` and `check_machine`) now get no `PYTHONPATH` either. `check_manifest` is not one of them: it goes through `validator_row` and gets the decision above. The two naming callers run the standard's own `validate-repository-naming.py`, which inserts its own directory ahead of its `repo_shape` import. The machine preflight needs nothing from `repo_shape` at all: `setup-project.py --preflight` returns before the only `import repo_shape` (deferred) and its module imports are the standard library's, so it never imports it. So it was inert for all three and printed nowhere. Review N1. The PowerShell spelling is kept. Its docstring now says what it does: the `$env:PYTHONPATH` assignment lasts for the PowerShell session and REPLACES any `PYTHONPATH` the reader had, which is accepted because the issue and the plan named this spelling and the effect ends with the session. The backwards reason ("would delete a `PYTHONPATH` the reader may have meant to keep") is deleted. Review N3. A Windows-only test runs each template NEXT line for real in a fresh `powershell -NoProfile -NonInteractive -Command "<line>; exit $LASTEXITCODE"` (cwd the filesystem root, `PYTHONPATH` removed, the row's trailing `#` comment cut first so it cannot swallow the `exit`, output read as bytes) and asserts the validator's finding and the row's `detail.exit`. Skipped elsewhere with the reason beside it; it has not been run anywhere until tests-windows runs it. Tests: a root with its own validator and no `scripts/repo_shape.py`, for the manifest, pins and family rows -- (a) with the file edited red, the row is a FINDING quoting the `ModuleNotFoundError` and its own-copy NEXT pasted into `sh -c` from `/` with no `PYTHONPATH` exits with `detail.exit`; (b) with a valid file, the row no longer says the validator passes and agrees with the project's own gate run directly. Mutation: `PYTHONPATH` put back on the own-copy run only -- all 6 new tests fail (log mut2.log). The template-copy tests are unchanged. Replayed onto main d94d155 (after #232 landed), the targeted run (tests/test_shape_doctor.py, tests/test_shape_advisory.py, tests/test_cli_reference.py) gives 295 passed, 3 skipped; the 3 are the Windows-only PowerShell test. Not here: N2, O1-O4. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ight needs nothing from `repo_shape` and never imports it, instead of that it "finds `repo_shape` itself", so the note on which of the standard's own scripts get no `PYTHONPATH` is true of the preflight as well as of the naming script (#238) Re-check N3 on the fix round. The sentence said "The standard's own scripts (naming, the machine's preflight) find `repo_shape` themselves". That is true of `validate-repository-naming.py`, which inserts its own directory ahead of its `repo_shape` import. It is not true of `setup-project.py --preflight`: that returns before the only `import repo_shape` (deferred) and its module imports are the standard library's, so it finds nothing because it looks for nothing. A reader taking the old wording would think the preflight needed `repo_shape` on the path and would restore a `PYTHONPATH` it has no use for. Docstring only; no code, no test and no `--help` text changes. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Sonnet 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 and 18 hours by commenting |
Reviewer's GuideThe PR synchronizes validator execution with the NEXT command it prints: template validators receive the standard scripts directory through an explicitly generated shell or PowerShell command, while root-owned validators run without injected PYTHONPATH and now correctly surface missing-local-module failures. Extensive regression tests cover pasted commands, unloaded manifests, platform-specific quoting, and root/template ownership cases. Sequence diagram for synchronized validator execution and NEXT commandssequenceDiagram
participant Doctor as shape_doctor
participant Selection as validator_for
participant Environment as validator_scripts
participant Validator as validator
participant Reader as Reader
Doctor->>Selection: validator_for(ctx, own, template)
Selection-->>Doctor: used, which, own_copy
Doctor->>Environment: validator_scripts(shape, own_copy)
Environment-->>Doctor: scripts or None
Doctor->>Validator: run_validator(ctx, used, args, pythonpath)
Validator-->>Doctor: code, quoted, output
Doctor->>Environment: validator_scripts(shape, own_copy)
Environment-->>Doctor: same scripts or None
Doctor->>Doctor: validator_command(shape, used, own_copy, args)
Doctor-->>Reader: NEXT command
Reader->>Validator: paste NEXT command
Validator-->>Reader: validator finding or successful output
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
…s `_entry_findings` composed with #255's, and this PR's pair renamed `unwritable_findings`/`unwritable_remediation` #231 (6f45bf9) puts each entry's path first in `_entry_findings` and returns `unsafe_remediation` ahead of the rest; this branch appends each value `execute` writes into a file that no file can hold. The two hunks are its docstring's order sentence and its return. The sentence keeps main's order and appends this PR's clause; the return is `(findings + submodule_findings + unwritable_findings, unsafe_remediation + remediation + unwritable_remediation)`, so the paths still come first, as #196 needs. This PR's local pair `value_findings, value_remediation` is renamed `unwritable_findings, unwritable_remediation`. #261's branch (ca21b57) binds `value_findings, value_remediation` in the same function, from `_manifest_findings`: a resolution keeping both lines under one name would silently drop one PR's findings. With the rename it cannot. The rest of main (#254/#281, #238/#287, #203/#236, #204/#227, #249/#250, #241, #215 and the earlier merges) merges with no conflict. The resolution is the lane's re-verify one (`resolve-main.py`, 385 passed on 6f45bf9), rename aside. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#241, #249/#250, #204, #203 and #238 merge with no conflict, and a `.gitmodules` value git refuses is answered before #190's relative-url check, which reads no url from a file git cannot read Main since d94d155, the main GitHub's update-branch merged at 8ce5614: c2ff2b0 (#215 via #240), c3a9645 (#241 via #256), b30595c (#249/#250 via #284), be2790e (#204 via #227), 6d8d88b (#203 via #236) and ede15ce (#238 via #287). No conflict hunk. Three files were changed on both sides; `git merge` auto-merged each, and every hunk of each side lies apart from the other's. Each side's +/- lines are identical before and after the merge in all three (`git diff -U0` d94d155..8ce5614 against ede15ce..merge, and d94d155..ede15ce against 8ce5614..merge). Line numbers are the merged file's. adopt-project.py - main, c2ff2b0 (#215): 384-506, `SUBMODULE_TYPED_KEY_RE` to `_refused_value` and `_who_refuses_a_boolean`; 510-515 and 522-524, `_registrations`' docstring and its return of `_refused_value`'s words; 791-797, `Source.registered_submodules`' docstring; 2181-2185, `_where_git_fails`' docstring. - #190: 36-38 and 51-53, the module docstring; 102-103, the `repo_shape` import list; 334-339, `Registrations.url`; 2252-2501, `RELATIVE_URL` to `_relative_url_findings`; 2503-2548, `_submodule_findings` and `_entry_findings` with `local_remote_dir` and the NOTEs; 2608 and 2630, `cmd_check`; 3070-3092, `_repository_urls` and `_remote_urls`; 3111-3216, `_refuse_what_check_finds` and `_as_resolved_here`; 3443, `cmd_execute`. scripts/repo_shape.py - main, b30595c (#249/#250): 173-535, the YAML reader's quote boundaries, from the comment after `YamlError` to `_split_flow`. - #190: 1975-1985, `REMOTE_PORT_RE` and `REMOTE_HOST_ALIASES`; 2099-2116, `remote_key`'s docstring and body. tests/test_shape_doctor.py - #190: 2662-2693, `test_a_port_and_githubs_ssh_host_are_not_part_of_a_remotes_key`. - main, ede15ce (#238): 5209-5808, the validator-PYTHONPATH tests after `test_a_drifted_roots_next_command_runs_when_it_is_pasted`. Every other file is one side's blob: main's for the 17 main changed, and 8ce5614's for tests/test_adopt_relative_submodule_url.py and tests/test_adopt_submodules.py. The merge's diff against main is #190's five files, with the stat it had against d94d155 (1097+, 41-). No function or class is bound twice at the top level of adopt-project.py, scaffold-project.py, scripts/repo_shape.py, scripts/shape_materialize.py, shape-doctor.py, scripts/family.py or the three test files. The helpers c2ff2b0 moved into tests/conftest.py (`planned`, `findings`, ...) are plain functions, and tests/test_adopt_relative_submodule_url.py imports seven named helpers from conftest and defines its own `planned`. HOW #215 AND #190 MEET. `_submodule_findings` lists `submodule_plan_problems` first, which holds #215's `plan-gitmodules-unreadable`, then `_relative_url_findings`. Where git's submodule reader refuses a value of the source's `.gitmodules`, `registered_submodules` answers with git's words, a string, and `_kept_relative_urls` reads no url from a string. So a source with both problems gets #215's finding alone, and no NOTE either. #190's finding is not lost: once the value is fixed, `check` reports it, and `execute` refuses it through the same list. #190 adds lines only after #215's, so it can hide nothing of #215's. A relative url kept in a leg is always beside a gitlink kept in a leg, where `_where_git_fails` names a failure, so the unreadable finding is always printed whenever #190's would be skipped. Measured on this tree with a source holding `upstream/dependency`, `url = ../dependency` and `update = bogus`, with `.gitmodules` and `upstream/` kept in code for testorg: - `origin` in otherorg: `check` 1 with `plan-gitmodules-unreadable` alone; `execute --local-remote-dir` 2, `REFUSED plan-gitmodules-unreadable`, with no remotes directory made. With `update = checkout`: `check` 1, `plan-submodule-relative-url`. - `origin` in testorg: the same `check` 1 and `execute` 2, with no NOTE. With `update = checkout`: `check` 0 and the NOTE. - At 8ce5614, before #215, the first source was `plan-submodule-relative-url`. The second was `check` 0 with the NOTE, over a file `git fast-export` dies on. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, #242 via #308, #262 and #263 via #313) into fix/adopt-record-submodule-placement Brings in ede15ce (#238), 6f45bf9 (#196), b421e6b (#254), c0ed615 (#242) and 71b58a1 (#262, #263) on top of 6d8d88b. No rebase. ede15ce changes shape-doctor.py. 6f45bf9 and 71b58a1 change adopt-project.py and scripts/shape_materialize.py. b421e6b changes scripts/shape_materialize.py too, giving a project's validators an explicit PYTHONIOENCODING. All of these are files this branch changes. c0ed615 changes scripts/family.py alone. No conflicts. Checked: - adopt-project.py: #196 gives `check` and `execute` one test for an entry's path, `entry_path_problem`, as the finding `plan-unsafe-path`. #262 and #263 add `_root_shape_findings` to `_leg_findings`, for a kept root path where the shape writes. Both reach `execute` through `_refuse_what_check_finds`, before any leg exists and before `_commit_the_split` appends the record. This branch's `submodule_record`, `_resolution_text`, the record's preamble, `_answered_legs`, `_kept_in_a_leg`, `_commit_the_split`, `cmd_execute`, `submodule_plan_problems`, `_submodule_findings`, `Plan`, `y`, `emit` and `walk` are unchanged by the merge. `_root_shape_findings` is asked only beside a `legs:` mapping, so the doctor's placement plan, whose `legs:` is a list, still goes through `_leg_findings(plan, None)`. - shape-doctor.py: #238 changes `validator_row` and `run_validator`. This branch's `check_placement`, `recorded_submodules`, `audit_leg`, `placement_row`, `placement_verdict` and `Context` are unchanged by the merge. - scripts/repo_shape.py is untouched by these commits, so the record's writer and reader, and `double_quoted` in `submodule_record`, are 3e86200's. - Both sides added lines to the module docstrings of adopt-project.py and shape-doctor.py. Every line either side added is in the merge. - No top-level name is bound twice in adopt-project.py, scaffold-project.py, scripts/repo_shape.py, scripts/shape_materialize.py, shape-doctor.py, validate-manifest.py, update-shape.py, scripts/family.py or scripts/bump-leg.py. Each merged module's names are the base's plus what either side added. Every definition only one side changed is that side's text. - AGENTS.md is untouched: 472 lines, under the cap of 472. - The branch's diff against main is still #189 alone: 12 files, +1670 -157, file by file the same as against 6d8d88b. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main brings five commits since 6d8d88b: - ede15ce (#238 via #287): shape-doctor.py's validator rows; - 6f45bf9 (#196 via #231): `entry_path_problem`, one test of an entry's path for `check` and `execute`; - b421e6b (#254 via #281): bump-leg.py and update-shape.py give the validators an explicit PYTHONIOENCODING (scripts/shape_materialize.py gains `captured_child_environment` and `inherited_child_environment`); - c0ed615 (#242 via #308): family.py's `add` and `bump` under excludes; - 71b58a1 (#262, #263 via #313): `check` finds, and `execute` refuses, a kept root path where the shape writes a directory or its `shape/` copy, and the materializer never writes over a copy. It merged with no conflict. Only adopt-project.py changed on both sides. Every other file the merge changes against this branch is main's, and every file it changes against main is this branch's. In adopt-project.py the merge's diff against this branch is main's own diff (559 lines), and its diff against main is this branch's own diff (784 lines). Main's twenty hunks there, by where they land: 1-2. the module docstring: `check` validates each entry's path and keeps no path in the root where the shape writes (6f45bf9, 71b58a1); 3. the `from shape_materialize import` block: `CASE_ASIDE`, `MOVE_THE_LEG`, `first_and_more` (71b58a1); 4-6. `_leg_findings`: a docstring line, the call to `_root_shape_findings`, and the new block after it (`ROOT_BLOCKS_SHAPE`, `ROOT_BLOCKS_COPY`, `_KeptTree`, `_shape_blocks`, `_blocked_sentence`, `_fix_the_kept_path`, `_root_shape_findings`) (71b58a1); 7. after `_submodule_findings`: `NOT_EXTRACTED`, `entry_path_problem`, `_misread`, `_refused_by_policy` and `_unsafe_path_findings` (6f45bf9); 8-11. `_entry_findings`: its docstring, and the entries' path findings first (6f45bf9); 12-13. `_extract_leg`: `literal:` on every `--paths-from-file` line (6f45bf9). This branch changed `_extract_leg` as well, further down: it reads the commit count and the digest before `_push_the_leg`. The two sets of lines do not touch, and the merged function has both; 14-15. `_mount_the_legs`: `git --literal-pathspecs rm` (6f45bf9); 16-17. `_leg_paths`: collected, not judged (6f45bf9); 18-20. `_refuse_what_check_finds`: its docstring and remediation (6f45bf9, 71b58a1). The new findings of 6f45bf9 and 71b58a1 are `_entry_findings`, which `cmd_execute` reaches only through `_refuse_what_check_finds`, before `_create_leg_remotes`. So they are refused before any leg exists, and no notice is due. The materializer's new `materialize-copy-blocked` is raised after both legs are pushed, inside `_commit_the_split`, when a run reaches the materializer without `check`'s finding. That makes it a late failure, which this branch reports with the notice and exit 2. Main's `test_with_check_skipped_execute_stops_at_the_copy_and_overwrites_nothing` expects it to escape `cmd_execute`, so this merge breaks that test, as 9092fda's merge of #200 broke the mount-guard tests. The next commit makes the test read the reported refusal. No top-level name is bound twice in adopt-project.py, scaffold-project.py, scripts/repo_shape.py, scripts/shape_materialize.py or tests/conftest.py. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, #238, #196, #254, #242 and #262/#263 merge with three adopt-project.py hunks kept on both sides, and #262's `_CASE_ASIDE` import kept as the name's one binding origin/main moved b30595c -> 71b58a1: - be2790e (#204 via #227), update-shape.py; - 6d8d88b (#203 via #236), family.py; - ede15ce (#238 via #287), shape-doctor.py; - 6f45bf9 (#196 via #231): adopt-project.py `entry_path_problem`, scripts/shape_materialize.py; - b421e6b (#254 via #281): scripts/shape_materialize.py, bump-leg.py, update-shape.py; - c0ed615 (#242 via #308), family.py; - 71b58a1 (#262 and #263 via #313): adopt-project.py `_root_shape_findings` and scripts/shape_materialize.py `copy_in_the_way`. It fixes what this branch's pull request listed as follow-ups (i) and (ii): a source's own `shape/` where the shape writes its copy, and a plain FILE where the shape needs a directory. Conflicts, all in adopt-project.py, each resolved by keeping both sides: - The module docstring's `check` paragraph. 71b58a1 adds "no path kept in the root where the shape writes ..." before the submodule clause, and this branch adds the symlink clause after it. Both are kept, in that order. - `_entry_findings`' docstring. 6f45bf9 and 71b58a1 give the order "each entry's path, then coverage, then each entry's `leg:`, the two mount paths and what the root keeps where the shape writes, then the source's own submodules". This branch adds "then each symlink the plan keeps in the root that a shape file cannot be written through". - `_entry_findings`' return line. 6f45bf9 returns `unsafe_remediation + remediation`, and this branch appends `_root_symlink_findings(plan, source, tree)` to the findings. The merge returns both. Found after the textual merge, and resolved here so that no commit binds a name twice: 71b58a1 (through 65a9262) imports #198's phrase as `_CASE_ASIDE` in the "#172's shape half" import block, and b37159a had imported it on a line of its own. Main's import is kept; this branch's line and its five-line comment go. scripts/shape_materialize.py merges with no conflict. `place` gets 71b58a1's `copy_in_the_way` inside its collision block and this branch's `_refuse_unwritable` after it, as 71b58a1's message foresaw. Checked on the merged tree: - `_entry_findings` still hands `_leg_findings` the tree, and the symlink findings still come last in its list. - AST: no top-level name is bound twice in adopt-project.py, scaffold-project.py, scripts/repo_shape.py or scripts/shape_materialize.py, and none is loaded but bound nowhere. Main changed the signature of no function this branch calls. - Both sides are intact by +/- line sets, apart from the three hunks and the import above. The next two commits fix what this merge breaks, each with a revert line. #262's rule and this branch's both name a symlink kept AT the copy under `shape/`. And where the shape's copy lands is now `copy_in_the_way`'s question, asked before this branch's own check of it. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Lane: openreposhape-4 (openRepoShape-4)
Claim: #238 (comment)
What changes
shape-doctor.py'smanifest,pinsandfamilyrows (and the unloaded-manifest row #205 added) print a NEXT command. When a root has noscripts/validate-<x>.pyof its own, that command ran the standard's TEMPLATE copy as a bare command, and the template importsrepo_shapefrom beside itself, where there is none: pasted from any directory it died onModuleNotFoundError: No module named 'repo_shape'with exit 1, which is also a validator's exit for "findings". The doctor's own child only ran it becauserun_validatorputs<standard>/scriptson the child'sPYTHONPATH, so the row quoted a process the reader's paste was not.validator_commandspells the line, for the reader's platform, from the oneown_copythatvalidator_forreturned:PYTHONPATH=<standard>/scripts python3 <template> --root <root>in sh,$env:PYTHONPATH = '<standard>\scripts'; python <template> --root <root>in PowerShell.validator_rowandunloaded_manifest_rowboth build their NEXT through it.quote_argtakes a keyword-onlyalwaysfor the PowerShell value, which follows=and would otherwise be run as a command.validator_scripts(shape, own_copy)decides the directory AND the condition once: the standard'sscripts/for the template copy, nothing for the root's OWN copy.run_validatorsetsPYTHONPATHonly when a caller asks (pythonpath), andvalidator_rowasks with that answer. So the run and the printed line applyPYTHONPATHunder one condition.repo_shapefrom beside itself (it lostscripts/repo_shape.py) is now a FINDING quoting the validator'sModuleNotFoundError, where the doctor used to say its gate passes, or quote the validator's own finding, over a gate that dies on the import when the project runs it. Its own-copy NEXT line is unchanged byte for byte.run_validatorcallers (check_naming,check_not_a_root_naming,check_machine) get noPYTHONPATH: the naming script inserts its own directory ahead of itsrepo_shapeimport and the machine preflight never importsrepo_shapeat all.check_manifestis not one of them; it goes throughvalidator_row.run_validator's docstring, which said the machine preflight "findsrepo_shapeitself" (it needs nothing from it).Review history
This is a REPLAY, not a merge, of the two commits reviewed on
fix/doctor-template-validator-next(stacked on PR #232's head1c9282e, #205), onto main after #232 squash-landed asd94d155. A merge of main would have kept a second copy ofMANIFEST_ROW,refusal_line,unloaded_manifest_rowandwaiting_rowoutside the conflict markers.59724c9: review MERGEABLE WITH NITS (review238-59724c9). Its S1 (the run and the printed line appliedPYTHONPATHunder different conditions for a root's own copy) was taken; its S2 (replay, never merge) is this replay.88c81e0: the S1 fix round and its PowerShell test. Re-check MERGEABLE WITH NITS (review238-88c81e0), no MUST. Its S2 (the head's message said UNTESTED and carriedWIP(...)) is why the replayed commit carries a corrected message; its S1 (main moved, shape-doctor.py: a project.yaml or family.yaml that is there and does not load is INVALID (manifest) or INVALID (family), exit 1, quoting load_yaml's own refusal and naming the project's own validator, instead of NOT A SHAPE ROOT, exit 2, with an adopt-project.py plan NEXT and a Triad row calling an assembly root a single repository (#205) #232 conflicted) is why this waited for shape-doctor.py: a project.yaml or family.yaml that is there and does not load is INVALID (manifest) or INVALID (family), exit 1, quoting load_yaml's own refusal and naming the project's own validator, instead of NOT A SHAPE ROOT, exit 2, with an adopt-project.py plan NEXT and a Triad row calling an assembly root a single repository (#205) #232.git range-diff 1c9282e..88c81e0 d94d155..HEAD~1shows the first commit=and the second!with a message-only difference (no file differs);git patch-idis equal for both pairs and for the net diff.fix/doctor-template-validator-next-on-main; the old branch is left where it is.Follow-ups, none in this PR: the re-check's N1 (an operator's own exported
PYTHONPATHstill reaches a root's own validator, so it can bring the false pass back for that operator), N2 (unloaded_manifest_row's NEXT promises "refuses it the same way until it loads", and pasted over a root that also lostscripts/repo_shape.pyit dies on the import), O5 (the installedopenRepoShape --doctorprints NEXT lines that name a clone its EXIT trap has removed), and review238-59724c9's O1-O4.Tests
env GIT_AUTHOR_NAME=t GIT_AUTHOR_EMAIL=t@t GIT_COMMITTER_NAME=t GIT_COMMITTER_EMAIL=t@t PATH=<gh shim that exits 97>:<git-filter-repo 2.47.0>:$PATH /usr/bin/python3 -m pytest tests/test_shape_doctor.py tests/test_shape_advisory.py tests/test_cli_reference.py -q: 295 passed, 3 skipped in 236.55s, over the final tree (2ed5dd0). The 3 skips aretest_a_template_validators_next_runs_when_pasted_into_powershell, which runs only on a Windows host. The reviewers' pre-replay merge run was 285 passed, 3 skipped; main has gained tests since.NOTE: that PowerShell test has not run anywhere yet. tests-windows is its first run, so this head is green only when
tests,tests-macosANDtests-windowsall pass.Closes #238
Summary by Sourcery
Make validator findings and remediation commands consistent across doctor runs and pasted shell or PowerShell invocations.
New Features:
Bug Fixes:
repo_shapeimports from a project's own validator as findings instead of incorrectly passing the gate.Enhancements:
Tests: