Repository navigation
shape-doctor.py: a validator's verdict is a printed FINDING or REFUSED and any other non-zero exit is a crash, so a gate killed by a signal, ended by os._exit(1), holding conflict markers or failing argparse no longer "reports a finding", a crash of the standard's own naming script is CANNOT ANSWER instead of INVALID (naming), and a pass that printed WARNING or note lines is a note quoting them while a red row quotes its finding and never a warning (#410, #448) - #557
Merged
Merged
Conversation
…USED` and any other non-zero exit is a crash, so a gate killed by a signal, ended by `os._exit(1)`, holding conflict markers or failing argparse no longer "reports a finding", a crash of the standard's own naming script is CANNOT ANSWER instead of `INVALID (naming)`, and a pass that printed `WARNING` or ` note ` lines is a `note` quoting them while a red row quotes its finding and never a warning (#410, #448) One rule for every child the doctor reads, from the v1.1.0 triage of #410 that Brett Heap approved at 14:31:28Z on 2026-10-14: a verdict is a printed `FINDING` or `REFUSED`, anything else non-zero is a crash, and a WARNING or note line on a pass is a `note`. `crashed()` no longer asks for a `Traceback (most recent call last):` line. Every finding and refusal the shipped validators and the naming script choose to report is a line starting `FINDING` or `REFUSED`, printed before the exit 1 or 2 that goes with it, so a non-zero exit with neither line is a child that stopped before it said anything. That brings in #447: a negative exit (SIGKILL -9, SIGTERM -15), `os._exit(1)` with nothing printed, a `SyntaxError` from merge-conflict markers (no traceback header), `can't open file` and an argparse `usage:` error, each of which read "reports a finding" or "refuses". The quoted line is the output's last, or, with no output, words saying so that name the signal. A FINDING printed before the child died still stands, as #357 ruled. Both `naming` rows now ask `crashed()` (#410). The naming script is always this checkout's, so a crash carries `detail.crashed` and `detail.environment`, like a crashed template validator: an assembly root is CANNOT ANSWER, exit 3, and a directory that is not a root stays NOT A SHAPE ROOT, exit 2, because `verdict_for` decides that first. The text report prints its `shape-doctor-cannot-answer` refusal only under a CANNOT ANSWER verdict, so none appears under NOT A SHAPE ROOT. `check_naming` keeps `--quiet`: the `manifest` row now carries the same chain WARNING. On exit 0, `validator_row` reads ` skip `, `WARNING ` and ` note ` blocks with one reader (`reported`), each into its own `detail` key (`skipped`, `warnings`, `notes`), quoted in the reason in the order printed. So validate-manifest.py's `WARNING naming-referent-chain` (#448) and validate-pins.py's ` note neutral-pin-source-collides` (#460's pins-note line, comment 6136245436) reach the report as a `note` that moves no verdict. `REPORT_ITEM` learns ` note `, so a note after a skip is no longer read as that skip's text. A red row's quoted line is the first FINDING or REFUSED, else the first WARNING, else the last line, so a manifest row no longer quotes an earlier leg's WARNING as its reason (#448's decision 4). `with_gitlink_repairs` and `with_merged_away_rows` read the whole output and skip a crashed row, so the wider crash rule only keeps them off rows that printed no finding. Measured on 5ed3e5d: the stock naming script still raises `UnicodeEncodeError` printing a name that is not UTF-8 under the doctor's `PYTHONIOENCODING=utf-8`, so #410's case (1) is still there. The doctor's row now calls it a crash of this checkout's script; the script itself is unchanged, by the brief's ruling. Tests: tests/test_doctor_validator_verdicts.py, new, on #357's harness. tests/test_pin_source_env_collisions.py's colliding-pair test expected the `pins` row to be `ok` over that note; it now expects a `note`. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThe PR centralizes child-validator interpretation around printed verdict lines: non-zero runs without Sequence diagram for child validator crash handlingsequenceDiagram
participant Doctor
participant Validator
Doctor->>Validator: run_validator()
Validator-->>Doctor: exit code and output
Doctor->>Doctor: crashed(code, output)
alt FINDING or REFUSED printed
Doctor->>Doctor: Keep validator verdict
Doctor-->>Doctor: Create finding or refusal row
else Non-zero without verdict
Doctor->>Doctor: Mark detail.crashed
Doctor->>Doctor: Quote last line or signal_name()
Doctor-->>Doctor: Create crash row with CRASH_WHY
end
Flow diagram for validator verdict classificationflowchart TD
A["Run child validator"] --> B{"Exit code is 0?"}
B -->|Yes| C["reported(): read skip, WARNING, and note blocks"]
C --> D{"Reports found?"}
D -->|No| E["OK row"]
D -->|Yes| F["NOTE row with details and quoted reports"]
B -->|No| G{"Output contains FINDING or REFUSED?"}
G -->|Yes| H["Verdict row; quote first verdict line"]
G -->|No| I["Crash row; quote last output line or signal"]
Flow diagram for naming-script crash classificationflowchart TD
A["Run naming script"] --> B["crashed(code, output)"]
B -->|"No crash"| C["Use naming verdict"]
B -->|"Crash"| D["naming_crashed_row()"]
D --> E["Set detail.crashed and detail.environment"]
E --> F{"Assembly root?"}
F -->|Yes| G["CANNOT ANSWER"]
F -->|No| H["NOT A SHAPE ROOT"]
G --> I["Report CANNOT ANSWER refusal"]
H --> J["Do not print cannot-answer refusal"]
Flow diagram for successful validator reportsflowchart TD
A["Validator exits 0"] --> B["reported(output)"]
B --> C{"skip, WARNING, or note blocks?"}
C -->|No| D["OK row"]
C -->|Yes| E["Store skipped, warnings, or notes"]
E --> F["passed_but() builds quoted reason"]
F --> G["NOTE row with no NEXT and no verdict movement"]
Flow diagram for selecting a failing row's quoted lineflowchart TD
A["run_validator() receives output"] --> B{"Any FINDING or REFUSED line?"}
B -->|Yes| C["Quote first FINDING or REFUSED"]
B -->|No| D{"Any WARNING line?"}
D -->|Yes| E["Quote first WARNING"]
D -->|No| F["Quote last non-empty line"]
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
This was referenced Oct 14, 2026
… merge for ruling 27 This is a clean merge of main dd3f23a for Brett Heap's ruling 27, with no hand resolution: git applied it on its own, and the merge commit's tree equals the tree `git merge-tree --write-tree 430c13f dd3f23a` printed (03e9650). Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
brettheap
marked this pull request as ready for review
October 15, 2026 13:29
|
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 9 hours by commenting |
brettheap
pushed a commit
that referenced
this pull request
Oct 15, 2026
…ean merge for ruling 27 This is a clean merge of main a2243f1 (#557, on #560) with no hand resolution: `git merge` auto-merged shape-doctor.py and tests/test_doctor_legs_members_next.py and reported no conflict, and its tree, e0e7f1e, equals what `git merge-tree --write-tree 66a9353 origin/main` prints (T). T is also the tree the targeted set passed on, committed in a scratch worktree before this merge existed: 997 passed, 3 skipped (the three PowerShell tests, Windows only). Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
brettheap
pushed a commit
that referenced
this pull request
Oct 15, 2026
…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>
This was referenced Oct 15, 2026
brettheap
pushed a commit
that referenced
this pull request
Oct 15, 2026
… fix/540-pin-source-unread This merges origin/main 2bf8fd4 as the shared store held it at 2026-10-15T16:50Z, thirteen landings past the 7d9bec9 this branch last merged: #555 (shape-doctor's manifest kinds), #565 (scaffold's `scaffold-remote-exists`), #581 (adopt's `check` of a kept pin path and an unwritable work directory), #568, #559, #557, #560, #580, #556, #558, #567, #579 and #562. It is done so that the PR's CI merge ref and this round's rewording run on current main. The merge was clean, with no conflict hunk. `adopt-project.py`, `scaffold-project.py`, `scripts/shape_materialize.py`, `docs/cli.md` and `tests/test_adopt_root_symlinks.py` took main's changes beside this PR's by the textual merge, and `scripts/render-cli-reference.py --check` passes on the result. Main: 2bf8fd4. 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 16, 2026
…ests, 57 of 82 milestone issues closed), with #583, #596, #614, #619, #594, #617 and #618 in their areas, #596's Git Bash facts for its placeholders, release.yml's step 1 whole, and every pull request cited beside its issue (RV630 SHOULD 3, 5, 6, nits 7, 8) - SHOULD 5: step 1 matches release.yml's header: "always on a `release/v<X.Y>` branch, which no push run tests", "A `release/v<X.Y>` branch serves while it is still at that commit", and that a tag's dispatch running at the tag's commit is GitHub's documented behaviour, not yet seen here. - SHOULD 6: #598 beside #595, #592 beside #593, #460 on #557's line, and #589's line says "where that clone is still on the machine and is no partial clone" again. The counts and the open-issue list are re-derived from `git log --first-parent bfed143..origin/main` (49) and the milestone (25 open, 57 closed: #342, #392, #393, #421, #423, #426, #432, #433, #434, #443, #444, #454, #455 and #629 joined it). New lines under adopt-project.py for #583 (#520, #420), #619 (#530), #618 (#418), #614 (#527), #594 (#526) and #617 (#525); #596 (#585) under the one-line install, CI, native Windows, the docs and the known limits, where the two placeholders for it were. - SHOULD 3: the known limit about "the installed doctor's temporary clone" is gone; the README, the handbook and AGENTS.md no longer say it. - SHOULD 1: the filter-repo line says install.sh's order, apt and dnf included. - nit 7: the release process's preconditions are said as the process this release follows; the workflow checks the environments, and nothing checks the rehearsal. - nit 8: the known limits are wrapped. - "the first on native Windows, where the one line cannot run" says PowerShell cannot run it, since Git Bash does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap
added a commit
that referenced
this pull request
Oct 16, 2026
….py: a project's own `neutral_product_pins:` or a leg's `naming.referent_chain:` that is not a list is `validate-manifest.py`'s finding for that field in every gate that reads it, so `5` no longer ends `make validate` in a `TypeError` and a scalar `openXdox` is no longer read as its characters (#443) (#631) Four readers of a project's own declarations iterated whatever was there, each as `for x in (value or [])`: the naming gate's `_pins_from_project` and `_chain_from_leg`, `validate-manifest.py`'s `_declared_pins`, and `validate-pins.py`'s `PinSources`. So `neutral_product_pins: 5` (or `true`, or `1.5`) ended all three gates in `TypeError: 'int' object is not iterable`, exit 1. `validate-manifest.py` died one line before its own `manifest-neutral-product-pins` finding for that value could print. A leg's `naming.referent_chain: 5` ended the naming gate the same way, though `validate-manifest.py` already had the finding for it. `make validate` runs the naming gate first, so its traceback was the first thing a person read. Since #557 the doctor answered that crash `CANNOT ANSWER`, exit 3, which outranked the manifest's own finding. A scalar `openXdox` was read as the pins `X`, `d`, `e`, `n`, `o`, `p`, `x`: the naming gate classified and exited 0, and `validate-pins.py` refused `neutral-pin-missing` for a pin on `'o'`. A mapping was read as its keys in either field. What changed. Each reader now takes a value that is not a list as declaring nothing, as `validate-manifest.py`'s `_recorded_chain` already read a chain that is not one: no pins, and no chain. A string is still a chain's one link, under that function's own string rule. Each gate that reads the field then says `validate-manifest.py`'s own sentence for it, with the same code: FINDING manifest-neutral-product-pins: neutral_product_pins is 5, expected a list of neutral product names FINDING naming-referent-chain: leg 'assembly': referent_chain is 5, expected a list of neutral-product names In detail: - The naming gate gets `_pins_field_findings`, said before any leg's finding in `_targets_from_project`, and `_chain_field_findings`, said first in `_leg_target`. That function names the leg by its declared `role:`, as `validate-manifest.py` names it. `_chain_from_leg` is now `_recorded_chain` rule for rule. - `validate-manifest.py`'s `_declared_pins` returns the empty set for a value that is not a list. Its `_findings` is untouched, so its existing finding is now reached. - `validate-pins.py`'s `_check_neutral_product_pins` asks before `PinSources` reads the value, records the finding, and checks no pin. A finding is exit 1 in each gate, and `make validate` now stops at its first command on the sentence. The doctor's three rows quote it, and its verdict is `INVALID (naming, manifest, pins)` for the pins, or `INVALID (naming, manifest)` for a chain, exit 1. Decisions the issue left to the author: - A finding, not a refusal. The issue offers either exit. A non-list field is a fault in the manifest a person can fix, the same kind of fault `validate-manifest.py` already reports with exit 1 and `validate-pins.py`'s `neutral-pin-entry-malformed` reports with exit 1. - One sentence. The two existing sentences are reused, code and words, so every gate that reads the field says the same thing and the doctor quotes the same line in each row. - Guards in each validator, not a new helper in `scripts/repo_shape.py` (the issue's option 4). That is the smallest change that makes every case right. A shared helper would put a fourth pinned file in the change and make three validators import a new name from it, so a project would have to take all four copies together, and #444's branch is changing that file's chain readers now. The words stay validate-manifest.py's own, and a test holds the naming gate's two sentences equal to that file's, so a change to one breaks the test. - `0`, `false` and `''` are findings in every gate. `validate-manifest.py` already said they are not a list. The naming gate and `validate-pins.py` read them as an empty list (`or []`) and exited 0. Now all three agree: absent or null declares nothing, and anything else that is not a list is the finding. #338's fix ruled the same for a link's manifest. For a chain, `''` is a string, so it stays an empty record with no finding, as `_naming_chain_field_finding` has it. Entries inside a list that are not names (`[5]`, `[[a]]`) were not examined by #443 and are untouched. Who this reaches. All three files are pinned copies in every scaffolded root's `contracts/shape-pin.yaml`. Each has one copy in this repository and no test records its digest or blob, so nothing else moves with them. A project takes them through `update-shape.py apply`, as three `upstream-changed` rows. The doctor's `naming` row runs the standard's copy, so it says the sentence at once. Its `manifest` and `pins` rows run the project's own copies, so over a project that has not applied this they still say the copy "crashed before reaching a verdict" with the `TypeError`. Even so, the verdict is `INVALID`, exit 1, and no longer `CANNOT ANSWER`, because the crash that marked this checkout as unable to ask was the naming gate's. Tests. New file tests/test_non_list_project_fields.py, 71 cases, on tests/test_manifest_validator.py's harness (`edit` and the scaffolded assembly leg's `naming:` block, which it imports) and tests/test_shape_doctor.py's session standard for the doctor. The cases: - The pure readers over every value the issue measured. Nothing is read as characters or keys, and the naming gate's sentences equal `validate-manifest.py`'s. - Each gate the project runs, from its root, over `5`, `true`, `1.5`, `openXdox`, `{a: b}`, `0`, `false` and `''` in the pins, and over the first six in a chain: exit 1, the sentence once, no traceback and no `neutral-pin-missing`. - The controls measured on main: absent, null, `[openXdox]` in the pins; `[]`, null, `openXdox`, `''` and `[openXdox]` in a chain. Each keeps its exit in every gate and says neither sentence. - A chain on the spec leg: both gates say `leg 'spec'`, as the sentence names the leg the chain is recorded on. - The order: with `neutral_product_pins: openXdox` (a scalar) and a chain that begins at `openXdox`, the naming gate says the pins sentence before the broken-chain line it explains. - `make validate` stops at the naming gate on the sentence. - The doctor's rows and verdict for `5` in each field, and for `openXdox` in the pins. Closes #443 Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Lane: openreposhape-4 (openRepoShape-4)
Claim: #410 (comment)
Claim: #448 (comment)
Claim: #447 (comment)
What changed and why
This PR applies one rule to every child the doctor reads. It comes from the v1.1.0 triage on #410 (comment), which Brett Heap approved at 14:31:28Z on 2026-10-14:
The change is in
shape-doctor.pyonly.crashed()(shape-doctor.py, after #357'scrashed(), still reads a validator killed by a signal (a negative exit, -9 or -15) or one that left byos._exit(1)with nothing printed as one that "reports a finding", becausecrashed()needs a traceback line, so a root's own gate ended by the OOM killer or a timeout's SIGTERM is a FINDING about the project, and the standard's template killed the same way is not CANNOT ANSWER #447). It no longer needs aTraceback (most recent call last):line. A non-zero exit with noFINDINGorREFUSEDline anywhere in the output is a crash. Each of these used to read "reports a finding" or "refuses":os._exit(1)with nothing printed;SyntaxError, which prints no traceback header);can't open file;usage:error (exit 2).The quoted line is the output's last line. With no output, it says so and names the signal. A FINDING printed before the child died still stands, as shape-doctor.py: a validator that crashed says it crashed instead of "reports a finding", and is CANNOT ANSWER when it is the standard's template, which a standard whose path holds the
PYTHONPATHseparator no longer runs at all; a neutral product's digestvalidate-pins.pyskipped is anotenaming the product, from a child that can no longer reachghor a proxy; and a root whose manifest does not load and whosescripts/repo_shape.pyis gone is told to restore that file before its gate (#278, #297, #298) #357 ruled. The verb a verdict row picks by its code ("refuses" for 2) is now reached only by a child that printed one of the two words.The naming rows (shape-doctor.py's two
namingrows read only the naming script's exit code, so a crash of the STANDARD's ownvalidate-repository-naming.pyis a FINDING about the project (INVALID (naming), exit 1, where #357 makes a crashed template CANNOT ANSWER), and a directory whose name is not UTF-8 getsnaming FINDING Atl?as: UnicodeEncodeError: ..., the script's traceback quoted as a fact about the name #410). Both rows askcrashed(). The naming script is always this checkout's, so a crash carriesdetail.crashedanddetail.environment, as a crashed template validator does:CANNOT ANSWER, exit 3. The reason ends "that script is this checkout's, not the project's, so nothing here is a statement about the project";NOT A SHAPE ROOT, exit 2, becauseverdict_fordecides that beforecannot_answer;--explainline, as before, and thewhyis a crash row's;report()prints itsREFUSED shape-doctor-cannot-answerlines only under aCANNOT ANSWERverdict. Without that, the not-a-root case printed a cannot-answer refusal underNOT A SHAPE ROOT (exit 2).check_namingkeeps--quiet. Themanifestrow now carries validate-manifest.py's WARNING about the same chain link, so the fact is said once.A pass that warned or noted (shape-doctor.py's
manifestrow saysok ... passeswhen the project's ownvalidate-manifest.pyexits 0 after printingWARNING naming-referent-chain: ... not reachable here, and the warning appears nowhere in the text or--jsonreport, so a referent chain nothing on this disk could check reads as checked underCOMPLIANT: the class of theskip#357 makes anote, in the form #357 still drops #448, Tracking: shape-doctor.py, reporting a repository's compliance #460's pins-note line). On exit 0,validator_rowreads theskip,WARNINGandnoteblocks with one reader,reported(). Each kind goes into its owndetailkey:skipped,warningsornotes. The reason quotes them in the order printed, after "passes, but".note, so it moves no verdict, and it has no NEXT.REPORT_ITEMlearnsnoteand is the reader's only definition of an item, so a note printed after a skip is no longer read as that skip's text.skipped()stays, asreported()'s skips. A row that only skipped reads exactly as before.The quoted line of a red row (shape-doctor.py's
manifestrow saysok ... passeswhen the project's ownvalidate-manifest.pyexits 0 after printingWARNING naming-referent-chain: ... not reachable here, and the warning appears nowhere in the text or--jsonreport, so a referent chain nothing on this disk could check reads as checked underCOMPLIANT: the class of theskip#357 makes anote, in the form #357 still drops #448, decision 4).run_validatorquotes the firstFINDINGorREFUSEDline, else the firstWARNING, else the last line. A red manifest row no longer quotes an earlier leg's WARNING as its reason.What else was checked:
with_gitlink_repairsandwith_merged_away_rowsread the wholeoutputwithFINDING_LINEand return a crashed row untouched. The wider crash rule keeps them only off rows that printed no finding. The quoted-line change only alters therow.reasonthey extend, which now quotes a finding.machinerow's quote is unchanged, because the preflight prints noWARNINGline.--helptext changed, sodocs/cli.mddid not either.Measured: #410's case (1) is not gone on main
The brief expected the stock naming script to classify a name that is not UTF-8 without a traceback. On 5ed3e5d here (Python 3.12.3,
LC_ALL=C.UTF-8, and thePYTHONIOENCODING=utf-8the doctor gives every child), the result was:scripts/validate-repository-naming.py --explain -- $'Atl\xffas'exits 1 withUnicodeEncodeError: 'utf-8' codec can't encode character '\udcff'. Its--explainblock prints the name to a strict stdout.naming: FINDING | Atl?as: UnicodeEncodeError: ....With this PR, that row is the crash row (
detail.crashed,detail.environment, "crashed before reaching a verdict"), and the verdict is stillNOT A SHAPE ROOT. The script itself is unchanged: the brief rules out editing it, and #441 (lane 5) does not touch its printing. So #410's decision 3 (the script's own crash) is still open and needs an owner. The regression test accepts either true row: the crash row now, or thenaming-unclassifiedFINDING once the script prints such a name. It never accepts the exception quoted as a finding.Tests
New file
tests/test_doctor_validator_verdicts.py, built on #357's harness (tests/test_doctor_validator_rows.py), with its fixtures imported. It has 22 tests:crashed(), still reads a validator killed by a signal (a negative exit, -9 or -15) or one that left byos._exit(1)with nothing printed as one that "reports a finding", becausecrashed()needs a traceback line, so a root's own gate ended by the OOM killer or a timeout's SIGTERM is a FINDING about the project, and the standard's template killed the same way is not CANNOT ANSWER #447:validate-pins.pyended by SIGKILL or SIGTERM (POSIX only),os._exit(1), merge-conflict markers, or an argparseusage:error. Each row is a crash row withdetail.crashedand the expected exit; the verdict is DRIFTED, exit 1; the NEXT is the root's own gate.os._exit(1)isdetail.environment, CANNOT ANSWER, exit 3.crashed()on constructed outputs.namingrows read only the naming script's exit code, so a crash of the STANDARD's ownvalidate-repository-naming.pyis a FINDING about the project (INVALID (naming), exit 1, where #357 makes a crashed template CANNOT ANSWER), and a directory whose name is not UTF-8 getsnaming FINDING Atl?as: UnicodeEncodeError: ..., the script's traceback quoted as a fact about the name #410:ok. The NEXT run shows the exception, and the text report prints the refusal.manifestrow saysok ... passeswhen the project's ownvalidate-manifest.pyexits 0 after printingWARNING naming-referent-chain: ... not reachable here, and the warning appears nowhere in the text or--jsonreport, so a referent chain nothing on this disk could check reads as checked underCOMPLIANT: the class of theskip#357 makes anote, in the form #357 still drops #448:manifestrow is anotequotingnaming-referent-chain,detail.warningsholds it, and the verdict is COMPLIANT, exit 0 (POSIX only: the layer'sgh apineeds shape-doctor.py: a validator that crashed says it crashed instead of "reports a finding", and is CANNOT ANSWER when it is the standard's template, which a standard whose path holds thePYTHONPATHseparator no longer runs at all; a neutral product's digestvalidate-pins.pyskipped is anotenaming the product, from a child that can no longer reachghor a proxy; and a root whose manifest does not load and whosescripts/repo_shape.pyis gone is told to restore that file before its gate (#278, #297, #298) #357's shebang stub).SHAPE_PIN_SOURCE_OPENXDOX(all platforms):ok.naming.roleset tocode: the row quotesFINDING naming-roleand not the WARNING (POSIX only). The test first checks that the validator printed the WARNING before the FINDING.run_validator's quoted line on constructed outputs (all platforms).noteand a two-lineskipin each order, from a stub validator, each land in their own key and neither swallows the other;reported()andpassed_but().One existing test changed in place:
tests/test_pin_source_env_collisions.py::test_a_project_declaring_a_colliding_pair_takes_a_shape_update. It asserted that thepinsrow staysokover a validator that passes and prints theneutral-pin-source-collidesnote. Before and after the update, that row is now anotewith the note indetail.notesand quoted in the reason. The verdict still equals the control's.Targeted run (Linux, py-bench, pytest 9.1.1, frvenv first on PATH), over 430c13f:
Mutants, each run against its target tests and then restored, all killed:
Tracebackrequirement incrashed()crashed()unit testreported()unit testrun_validatorquoted-line casesenvironmentfrom the naming crashREPORT_ITEMwithoutnotereported()unit testShared files
git merge-tree --write-treeof this head with each of these exits 0 (clean):origin/main5ed3e5d;--org <your-org>where itsoriginnames no organisationplancan read, so pasting it is no longer refusedadopt-no-org(#545) #553,l3-545-doctor-way-inc436bb4. It insertsorigin_orgabovedef check_not_a_root_naming; this PR leaves thatdefline and its docstring alone and starts at therun_validatorcall;machinerow of a machine that is not ready names the preflight checks that failed, in the[!!]lines the preflight printed, so its reason no longer ends onthis machine is not ready yet (see above).with nothing above it (#549) #552,l3-549-doctor-machine-rowbf00f1a. It editscheck_what_is_here's tail andcheck_machine, which this PR does not touch;l3-544-test-hygiene7135da6. This PR does not edittests/test_doctor_validator_rows.py, and Tests: about a dozen fixtures still clone a just-pushed local bare remote by plain path (the #440-class macOS flake), with small test-only fixes alongside #544 keepsclone_just_pushed, which the new file imports.Lane 4's P1 (
fix/doctor-next-detached-commits, #413/#414) ownssubmodule_update_next,check_legs,members_verdictanddetached, and this PR edits none of them. P1 is not on origin yet, so there is no merge-tree result for it.v1.1.0: lands only after the v1.0.0 tag.
Closes #410
Closes #448
Closes #447
Lane 3, as #460's keeper: this PR closes #460's pins-note line (comment 6136245436); please tick it there when this lands.
🤖 Generated with Claude Code
Summary by Sourcery
Make shape-doctor validator verdict handling reflect explicit child output, distinguishing findings, crashes, and informational notes accurately.
New Features:
Bug Fixes:
Enhancements:
Tests: