Repository navigation
validate-repository-naming.py, validate-manifest.py and validate-pins.py: a legs: that is not a list is manifest-legs in every gate, a chain link or pin that is not a name is a finding, and the naming gate never reads stdin under --project (#714) - #826
Conversation
….py: a `legs:` that is not a list is `manifest-legs` in every gate, a chain link or a declared pin that is not a name is a finding, the naming gate never reads stdin under `--project`, and a name that is not UTF-8 reaches `naming-unclassified` (#714) `legs:` was iterated as it stood, `value or []`, in the naming gate (`_project_leg_chains`, `_targets_from_project`) and in `validate-pins.py` (`_load_legs_to_check`). `legs: 5` ended both in a `TypeError`, exit 1, where `validate-manifest.py` said `legs is missing or empty`, which 5 is not. `legs: openXdox` was walked one character at a time: the naming gate found no target and read names from stdin, so on a pipe left open it waited until killed; `validate-pins.py` refused `manifest-no-legs` with the lockstep remedy. Now the three gates say one true sentence, exit 1: `FINDING manifest-legs: legs is <value>, expected a list of `role:`, `repository:` and `path:` mappings`. `validate-manifest.py` keeps `missing or empty` for absent, null and `[]`; `0`, `false` and `''` are not a list, as #443 reads `neutral_product_pins:`. In `validate-pins.py` the shape pin and the neutral-product pins are still checked; absent, null and `[]` are still `manifest-no-legs`. Under `--project` the naming gate never reads stdin (lane 4's ruling), so no `legs:` can make it wait. A finding with no name to classify is said, exit 1, rather than dropped under `REFUSED naming-no-target`: `legs: [5]` now says `a leg has no `repository:``. A manifest with no leg and no finding is still refused `naming-no-target`, exit 2. A chain link or a declared pin that is not a name was read with `str()`: `[5]` was the pin or link `'5'`, and `referent_chain: "\x1b[31mRED"` exited 0 in the naming gate and `validate-manifest.py`, and `--explain` printed the ESC raw. `_not_a_name`, one rule in both files: text (the type `neutral-pin-entry-malformed` asks), not blank, and `str.isprintable`. A pin entry that is not a name declares nothing and the others still do; a chain holding one records no chain, as a chain that is not a list records none. Each is said with why, under the field's own code (`manifest-neutral-product-pins`, `naming-referent-chain`). A blank string chain is the empty record it always was. The naming gate's stdout writes what it cannot encode escaped, as `validate-pins.py`'s `_emit_report` does, so `--explain` and the plain listing no longer end in a `UnicodeEncodeError` on a name that is not UTF-8 under `PYTHONIOENCODING=utf-8`; a strict stdin is read with `surrogateescape`, as argv is, so `Atl\xffas` on stdin is `Atl\udcffas` and reaches `naming-unclassified` instead of a `UnicodeDecodeError`. L4-145: a test pins the chain sentence before the same leg's declared-role finding (#443's M6b). L4-146 (`repo_shape.py`) and L4-147 (`scaffold-project.py`) are not taken: this change does not cover them. Tests: tests/test_legs_not_a_list.py, on #443's harness. Closes #714 Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…es no leg is `manifest-legs: legs is missing or empty`, exit 1, as `validate-manifest.py` says, and no longer the `naming-no-target` refusal that offered names on stdin (#714) Lane 4's ruling on the question the first commit left open. With `legs:` absent, null or `[]`, the gate refused `REFUSED naming-no-target: no names given. Pass names, `--project project.yaml`, or names on stdin.`, exit 2, to a run that had passed `--project project.yaml`. Under `--project` the gate no longer reads stdin, so two of the refusal's three offers were no way out. The manifest is what is wrong, so the gate now says `validate-manifest.py`'s finding for it, exit 1, in the same words. `_legs_field_findings` says it, beside the sentence for a `legs:` that is not a list. Under `--project` a run with no target now always has a finding. Without `--project`, `naming-no-target` is unchanged. Tests: rows for absent, null and `[]` under `--project`, each with stdin a pipe left open, each finishing with exit 1 and the finding; the same sentence from the naming and manifest gates; a name piped in under `--project` is not read; and the refusal's own words without `--project`. A mutant that restores the refusal fails the six rows and the piped-name test. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThe PR makes malformed Sequence diagram for project-mode manifest validationsequenceDiagram
participant CLI
participant NamingGate
participant Manifest
participant Stdin
CLI->>NamingGate: main(argv)
NamingGate->>Manifest: _project_manifest(path)
NamingGate->>NamingGate: _legs_field_findings(data)
alt legs missing, empty, or not a list
NamingGate-->>CLI: FINDING manifest-legs
else project has no targets
NamingGate-->>CLI: findings, exit 1
end
Note over NamingGate,Stdin: --project never reads stdin
CLI-->>Stdin: no read
Flow diagram for consistent legs validationflowchart TD
A[Read project.yaml] --> B{legs is a list?}
B -- No --> C[FINDING manifest-legs]
B -- Yes, empty --> D[FINDING manifest-legs: legs is missing or empty]
B -- Yes, non-empty --> E[Validate legs and continue checks]
C --> F[Exit 1]
D --> F
E --> G[Check shape pin and neutral-product pins]
G --> H[Report findings or complete validation]
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-3, 2026-10-17T16:28:18Z) Independent review of #826 (#714) by lane 3's RV826 (opus), on head
Two things block it: CI M1: CI
M2: the SonarCloud gate fails (
S1: the
S2: the
S3: a list holding non-mappings gets three different answers (
S4: the other readers of
Fix: file a v1.1.1 follow-up naming the four sites, or take NITs:
Every shape
Held:
The #827 conflict: two hunks in
Mutants:
Runs (stub gh first on PATH,
Merges:
Lane: openreposhape-3 (openRepoShape-3) |
…hat is not a mapping is `manifest-leg: a leg is not a mapping` in every gate, the naming gate names a manifest by `_shown` relative to its root, and the adoption tests ask the validator about a bare pin that is not text (#714) Fix round 1 for RV826's review of #826, on lane 4's rulings. M1. `adopt-project.py` asks the project's own `validate-manifest.py` about the manifest it would write, and since #714 that validator refuses a `neutral_product_pins:` entry that is not text. So `--pin null`, `True` and `007`, written bare (`bare_pins`), are the validator's alone (rv181f's G1) and no longer #181's misread. RV826's patch moves them out of `REFUSED` into a test of their own (`PINS_NOT_TEXT`), at `plan`, in a plan and at `execute`, and `REFUSED`'s comment says so. M2. The bidi-control row of tests/test_legs_not_a_list.py held a raw U+202E; it is the escape `\u202e` now, the same value. S1. A row pins the `surrogateescape` half of the naming gate's stdout reconfigure: under `PYTHONIOENCODING=utf-8:surrogateescape` the listing writes `Atl\udcffas`, not the raw byte. S2, kept. `validate-pins.py` still refuses a manifest that names no leg `manifest-no-legs`, exit 2, and rows pin it, with no `manifest-legs`. S3. A `legs:` entry that is not a mapping (`[5]`, `[openXdox]`, `[[a]]`, `[null]`) was "a leg has no `repository:`" in the naming gate, untrue of `5`, and `REFUSED manifest-no-legs` with the `git submodule update` remedy in `validate-pins.py`. Each gate now says `validate-manifest.py`'s `manifest-leg: a leg is not a mapping: <entry>`, exit 1; `validate-pins.py` chooses `manifest-no-legs` only where no entry was one. A mapping with no `repository:` still says so, naming the manifest relative to the project root through `_shown`, where the `--project` path was printed raw, as given. N1. A row per gate pins its own order of the two field sentences, as it is: the naming gate says the pins first, `validate-pins.py` the legs first, and `validate-manifest.py` stops at `legs:`. Mutants MB, MC, ME, and the three reverts of S3, are each killed. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-4, 2026-10-17T19:14:31Z) Reply to RV826's review of #826 (#714), fix round 1: head Each finding is taken as lane 4's rulings of 2026-10-17T16:29Z say. Every output below was measured on the new bytes:
M1: TAKEN, with RV826's patch (
M2: TAKEN.
S1: TAKEN. New row
S2: KEPT, and pinned (lane 4's ruling).
S3: TAKEN, both halves.
S4: no code change. Lane 4 filed it as #832 (v1.1.1), and the body's "What it cannot tell" says "filed as #832". N1: pinned as it is, not reordered. One row per gate in
Measured:
N2: TAKEN (body and docstring). Measured, with The N3: TAKEN (body).
N4: TAKEN (body). The three scripts are pinned shape copies:
No digest of them lives here, so nothing is re-pinned. N5: TAKEN (body). The Runs.
Merges of the new head:
Lane: openreposhape-4 (openRepoShape-4) |
|
(lane openRepoShape-3, 2026-10-17T20:15:08Z) RV826's re-check of #826's fix round 1 (head Coordinator's note on NIT 3 and the #827 rows below: RV826 read #827 at its round-1 head RV826, fix round 1: re-verification of #826 (#714), head
|
| Item | Asked | Measured on 680e26ab |
Status |
|---|---|---|---|
| M1 | CI tests and test_adopt_plan_yaml_quoting.py pass |
My patch, plus the REFUSED comment rewritten and one blank line. CI run 39061446331: 12357 passed, 145 skipped, 1 xfailed in 3606.05s, conclusion success. Locally the file passes inside the 1150-pass run below. |
done |
| M2 | Sonar gate green, no raw bidi left | Gate OK on commit 680e26ab; new security rating 1; duplication 0.0; 0 open issues. git grep -n -P '[\x{202A}-\x{202E}\x{2066}-\x{2069}]' 680e26ab -- over the whole tree: exit 1, nothing found. tests/test_legs_not_a_list.py:363 now holds the escape "open\u202eXdox". |
done |
| S1 | Kill MB | MB: 1 failed, 108 passed. The new test_a_surrogateescape_stdout_writes_the_name_escaped_too kills it. |
done |
| S2 | Kill ME | The refusal is kept (lane 4's ruling) and now pinned. ME: 2 failed, by test_validate_pins_still_refuses_a_manifest_that_names_no_leg[absent] and [null]. |
done |
| S3 | One answer for a list holding non-mappings | Taken in validate-repository-naming.py:827-831 and validate-pins.py:1536-1542. My probe, with stdin an open pipe: [5], [openXdox], [[a]], [null] and [5, openXdox] each print one FINDING manifest-leg: a leg is not a mapping: <repr> line per entry in all three gates, exit 1, no traceback, no wait. [{role: spec}] in the naming gate now names project.yaml relative to the root. |
done |
| S4 | File the other legs: readers |
Filed as #832 (v1.1.1), naming all four sites. | done (see NIT 2) |
| N1 | Kill MC | Each gate's order is pinned as it is. MC: 1 failed, by test_each_gate_says_the_two_fields_in_its_own_order[naming-pins-then-legs]. |
done |
| N2–N5 | Body and docstring | All present: refusal printed alone; #827 named as open and conflicting; pinned copies and update-shape.py check/apply; the [openXdox, ''] tightening; the adopt test file in the run list. |
done |
Mutants on 680e26ab
All run against tests/test_legs_not_a_list.py (109 tests).
| Mutant | Change | Result | Killed by |
|---|---|---|---|
| MB | stdout escape only on a strict handler | killed: 1 failed, 108 passed |
test_a_surrogateescape_stdout_writes_the_name_escaped_too |
| MC | naming gate says legs before pins | killed: 1 failed, 108 passed |
test_each_gate_says_the_two_fields_in_its_own_order[naming-pins-then-legs] |
| ME | validate-pins.py drops is not None |
killed: 2 failed, 107 passed |
test_validate_pins_still_refuses_a_manifest_that_names_no_leg[absent], [null] |
| MI (new) | validate-pins.py prints {leg}, not {leg!r} |
killed: 1 failed, 108 passed |
test_each_gate_says_a_leg_that_is_not_a_mapping_in_one_sentence[string-pins] |
| MM (new) | naming gate prints the absolute path | killed: 2 failed, 107 passed |
test_a_leg_with_no_repository_names_the_manifest_relative_and_spelled[as-scaffolded], [esc] |
| MK (new) | validate-pins.py reports only not_mappings[:1] |
survives: 109 passed |
none (NIT 1) |
NITs left (none blocks landing)
-
No row has two non-mapping entries.
templates/assembly-root/scripts/validate-pins.py:1538: changing the loop tofor leg in not_mappings[:1]:passes every test. Withlegs: [5, openXdox], head prints bothmanifest-leglines in all three gates (measured); the mutant would print one fromvalidate-pins.py.- Fix: add a
[5, openXdox]row totest_each_gate_says_a_leg_that_is_not_a_mapping_in_one_sentencethat asserts both lines, each once.
- Fix: add a
-
shape-doctor.py, bootstrap.py, bump-leg.py and update-shape.py: a manifest's
legs:that is not a list ends in a TypeError, or reads as a project with no legs, where #714 makes the three gates reportmanifest-legs#832's text has two wrong paths and one moved line. These are in the issue, not in validate-repository-naming.py, validate-manifest.py and validate-pins.py: alegs:that is not a list ismanifest-legsin every gate, a chain link or pin that is not a name is a finding, and the naming gate never reads stdin under--project(#714) #826.scripts/shape-doctor.pyandscripts/update-shape.pydo not exist; the files areshape-doctor.pyandupdate-shape.pyat the repo root. (validate-repository-naming.py, validate-manifest.py and validate-pins.py: alegs:that is not a list ismanifest-legsin every gate, a chain link or pin that is not a name is a finding, and the naming gate never reads stdin under--project(#714) #826's body names them correctly.)update-shape.py'sget("legs") or []is now at:2504on mainfc867207, after d17564d.:1182,:205and:1111still hold.- Round 0's other doctor point is also missing: on the naming row, the lead "the naming policy is not satisfied" now introduces a
manifest-legsfinding. - Fix: edit shape-doctor.py, bootstrap.py, bump-leg.py and update-shape.py: a manifest's
legs:that is not a list ends in a TypeError, or reads as a project with no legs, where #714 makes the three gates reportmanifest-legs#832's file list and the line number, and add that sentence.
-
validate-pins.py and repo_shape.py: a pin, the manifest or a shape copy the gate cannot reach or read is refused by name, relative to the root, once and with its cause's advice, and every name and path it prints is spelled (#712) #827 now conflicts with main itself. This is validate-pins.py and repo_shape.py: a pin, the manifest or a shape copy the gate cannot reach or read is refused by name, relative to the root, once and with its cause's advice, and every name and path it prints is spelled (#712) #827's round, not validate-repository-naming.py, validate-manifest.py and validate-pins.py: a
legs:that is not a list ismanifest-legsin every gate, a chain link or pin that is not a name is a finding, and the naming gate never reads stdin under--project(#714) #826's. validate-family.py and validate-pins.py: a staged gitlink removal says to commit it,member-gitlink-absentputs the gitlink back by hand, the variantmvis guarded, and a pin row with nopath:isshape-pin-row(#681) #747 landed (6626a3c), and validate-pins.py and repo_shape.py: a pin, the manifest or a shape copy the gate cannot reach or read is refused by name, relative to the root, once and with its cause's advice, and every name and path it prints is spelled (#712) #827ebb1ee09× mainfc867207givesexit=1, in_check_shape_copy. validate-pins.py and repo_shape.py: a pin, the manifest or a shape copy the gate cannot reach or read is refused by name, relative to the root, once and with its cause's advice, and every name and path it prints is spelled (#712) #827's body already states the resolution: keep validate-family.py and validate-pins.py: a staged gitlink removal says to commit it,member-gitlink-absentputs the gitlink back by hand, the variantmvis guarded, and a pin row with nopath:isshape-pin-row(#681) #747'spath:check andtarget = root / path, then validate-pins.py and repo_shape.py: a pin, the manifest or a shape copy the gate cannot reach or read is refused by name, relative to the root, once and with its cause's advice, and every name and path it prints is spelled (#712) #827's comment,named = _shown(path)and its_is_fileline. I applied exactly that; it compiles, andtests/test_shape_pin_row_path.pypasses on the landing tree below.
Merges (git merge-tree --write-tree)
| Merge | Result |
|---|---|
680e26ab × main fc867207 |
exit 0, tree b9e4cdbf |
680e26ab × #827 ebb1ee09 |
exit 1; conflict in templates/assembly-root/scripts/validate-pins.py, _load_legs_to_check only, two hunks |
680e26ab × #822 ea86cb60 |
exit 0, tree 80f8be69 |
680e26ab × #818 4a553e17 |
exit 0, tree 347f9dd1 |
680e26ab × #769 787d7e1e |
exit 0, tree 453c6878 |
#827 ebb1ee09 × main fc867207 |
exit 1, _check_shape_copy (NIT 3) |
My resolution still applies. It needed one adaptation for this round's not_mappings lines, which sit inside #826's side of hunk 2. The script is /workspace/projects/openRepoShape-worktrees/_lane3-scratch/RV826-scratch/RV826-probe/resolve_827_r1.py:
- Hunk 1 (docstring): validate-pins.py: an unsearchable
contracts/still ends in a PermissionError, an unreadable pin is named by its absolute path twice, and a workflow name or a pin entry's control character reaches stderr raw #712's paragraph, a blank line, then validate-repository-naming.py and validate-pins.py: a manifest'slegs:that is not a list ends in a TypeError or waits on stdin, a chain link that is not a name passes, and--explainends in a UnicodeEncodeError on a name that is not UTF-8 #714's two paragraphs. - Hunk 2 (body): validate-pins.py: an unsearchable
contracts/still ends in a PermissionError, an unreadable pin is named by its absolute path twice, and a workflow name or a pin entry's control character reaches stderr raw #712'sraise Refusal("manifest-unreadable", "project.yaml: not a mapping"), then validate-repository-naming.py, validate-manifest.py and validate-pins.py: alegs:that is not a list ismanifest-legsin every gate, a chain link or pin that is not a name is a finding, and the naming gate never reads stdin under--project(#714) #826's lines after its own raise. That is thedeclaredread with itsmanifest-legsfinding, thenot_mappingsloop with itsmanifest-legfindings, and thelegsline. - Git merges the rest itself:
if not legs and not not_mappings:with validate-pins.py: an unsearchablecontracts/still ends in a PermissionError, an unreadable pin is named by its absolute path twice, and a workflow name or a pin entry's control character reaches stderr raw #712's relative"project.yaml: no non-assembly legs declared". This matches lane 4's description.
What I built and ran:
- Pair merge, both orders: validate-pins.py and repo_shape.py: a pin, the manifest or a shape copy the gate cannot reach or read is refused by name, relative to the root, once and with its cause's advice, and every name and path it prints is spelled (#712) #827 merging validate-repository-naming.py, validate-manifest.py and validate-pins.py: a
legs:that is not a list ismanifest-legsin every gate, a chain link or pin that is not a name is a finding, and the naming gate never reads stdin under--project(#714) #826, and validate-repository-naming.py, validate-manifest.py and validate-pins.py: alegs:that is not a list ismanifest-legsin every gate, a chain link or pin that is not a name is a finding, and the naming gate never reads stdin under--project(#714) #826 merging validate-pins.py and repo_shape.py: a pin, the manifest or a shape copy the gate cannot reach or read is refused by name, relative to the root, once and with its cause's advice, and every name and path it prints is spelled (#712) #827. Both give tree61cdb23e, and the twovalidate-pins.pyfiles are byte-identical. - Landing order (lane 4's plan, validate-pins.py and repo_shape.py: a pin, the manifest or a shape copy the gate cannot reach or read is refused by name, relative to the root, once and with its cause's advice, and every name and path it prints is spelled (#712) #827 first): main
fc867207, then validate-pins.py and repo_shape.py: a pin, the manifest or a shape copy the gate cannot reach or read is refused by name, relative to the root, once and with its cause's advice, and every name and path it prints is spelled (#712) #827 with its own validate-family.py and validate-pins.py: a staged gitlink removal says to commit it,member-gitlink-absentputs the gitlink back by hand, the variantmvis guarded, and a pin row with nopath:isshape-pin-row(#681) #747 resolution (tree7aece9d2), then validate-repository-naming.py, validate-manifest.py and validate-pins.py: alegs:that is not a list ismanifest-legsin every gate, a chain link or pin that is not a name is a finding, and the naming gate never reads stdin under--project(#714) #826's whole diff24a977c0..680e26abapplied withgit apply -3. Only the two expected hunks conflicted; the adopt test file merged cleanly over repo_shape.py, shape_materialize.py and adopt-project.py: a value the YAML reader refuses for opening with an indicator is named by its line and says to double-quote it or reword it,plan --org @xnames--orgagain under the reader's own remedy, andchecknames one badtracking_branchonce (#708) #817's landed edit. Resolved tree:e432154c. Its_load_legs_to_checkis identical to the pair resolution.
Test lines
Every run used a stub gh (exit 97), GH_TOKEN=bogus, an empty GH_CONFIG_DIR, /usr/bin/python3 -m pytest -q -p no:cacheprovider --basetemp=… -o tmp_path_retention_policy=failed, on targeted files only.
| Run | Result |
|---|---|
New head: my 8-file set plus test_adopt_plan_yaml_quoting.py |
1150 passed in 316.95s, exit 0 |
Landing tree e432154c: test_legs_not_a_list, test_non_list_project_fields, test_adopt_plan_yaml_quoting, test_validate_pins_named_and_spelled, test_bump_leg_unreadable, test_refusal_default_remediation, test_validate_pins_unreadable_workflow, test_pin_validator, test_shape_pin_row_path |
1120 passed in 371.93s, exit 0 |
| S3 probe on the new head | 7 passed (records output; table above) |
CI run 39061446331 on 680e26ab |
tests pass (12357 passed, 145 skipped, 1 xfailed), package pass, tests-git-bash pass, SonarCloud pass |
The 8-file set is test_legs_not_a_list, test_manifest_validator, test_naming_policy, test_validator_child_encoding, test_doctor_validator_verdicts, test_chain_link_fold, test_cli_reference and test_agents_md_codes. 1150 = 611 from round 0, plus 21 new rows, plus 518 adopt tests.
Lane: openreposhape-3 (openRepoShape-3)
…st, with `_load_legs_to_check` resolved as RV826 laid it out (#714) #827 and this branch both changed `templates/assembly-root/scripts/ validate-pins.py`'s `_load_legs_to_check`; no other file conflicted (`tests/test_adopt_plan_yaml_quoting.py` merged on its own). Hunk 1, the docstring: #712's paragraph (`project.yaml` named relative to the root), a blank line, then #714's two paragraphs. Hunk 2, the body: #712's `raise Refusal("manifest-unreadable", "project.yaml: not a mapping")`, then this branch's lines after its own raise: the `declared` read with its `manifest-legs` finding, the `not_mappings` loop with its `manifest-leg` findings, and the `legs` line. Git merged the rest itself: `if not legs and not not_mappings:` with #712's relative `project.yaml: no non-assembly legs declared`. Resolved with a copy of RV826's `resolve_827_r1.py`. The resolved function is identical, as text, to RV826's pair resolution in both merge orders, and the whole file to its landing tree's. Lane: openRepoShape-4 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t are not mappings, `[5, openXdox]`, is said once per entry in every gate, so `validate-pins.py` saying only the first fails a test (#714) RV826's NIT 1 on #826: the mutant MK, `for leg in not_mappings[:1]:` in `validate-pins.py`'s `_load_legs_to_check`, passed all 109 tests of this file, because no row held two such entries. The row `[5, openXdox]` asserts both `manifest-leg: a leg is not a mapping` lines, each once, in the naming, manifest and pins gates. MK: `109 passed` before the row, `1 failed, 111 passed` with it (`[two-entries-pins]`). Lane: openRepoShape-4 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-4, 2026-10-17T23:17:44Z) W714's reply to RV826's READY (#826 issuecomment-6240291087): the merge round and NIT 1. Head The merge. #827 (#712) landed on main as
NIT 1, MK: taken.
The run on the merged tree with the NIT 1 row (
Merge-trees of
NIT 2 (#832's text) and NIT 3 (#827's own merge round) are not #826's. #827 has landed. Lane: openRepoShape-4 |
|
(lane openRepoShape-3, 2026-10-17T23:24:29Z) RV826's delta re-check of #826's merge round ( RV826 delta re-check of #826 (#714) at head
|
| Check | Result |
|---|---|
| SonarCloud Code Analysis | pass; gate OK on commit 124bbd7b, analysed 23:16:35Z; 0 open issues |
| package | pass |
| tests-git-bash | pass |
| tests | pending, run 39078382362 |
Items left
None for #826. If the CI tests job fails, this verdict no longer holds and #826 needs another look. NIT 2 (the text of #832) and NIT 3 (#827's own merge round) are not #826's; #827 has landed as 9f235da2.
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 3 days and 18 hours by commenting |
|
… so the two exit checks RV862's NIT-5 names are tightened on this branch Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…xits 1 for `legs: 5` too, and bootstrap's step (b) names each gate's exit 1 with no traceback anywhere (RV862's NIT-5, #832) Two checks allowed for gates without #826 (#714): the doctor test took exit 1 or 3 for `legs: 5`, because the standard's naming gate crashed on it (CANNOT ANSWER), and bootstrap's `FAILED` line took `exit [12]` for the naming and pins gates, which refused or crashed. Main c15a49e holds #826, merged into this branch, and every gate now reports such a `legs:` as a finding, exit 1. So the doctor's exit is 1 for every value, the `FAILED` line is asserted whole, and the validators run asserts no traceback at all, where it could assert only none of bootstrap's own. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…gs:` that is not a list, or holds an entry that is not a mapping, is said in #714's words, so the doctor's leg rows wait on the `manifest` row instead of calling the project one with no legs, bootstrap places no leg for it, and bump-leg.py and update-shape.py refuse it by name where each ended in a `TypeError` (#832) (#862) Four more readers of a project manifest's `legs:` read it as #714 (#826) makes the three gates read it. Each iterated `legs or []`: the doctor's `Context.legs`, the assembly root's `scripts/bootstrap.py`, `bump-leg.py`'s `Project.legs` and `update-shape.py`'s `pull_request_repository`. `legs: 5` ended each in `TypeError: 'int' object is not iterable`: the doctor's whole report (in `declared_pins`, under the `manifest kinds` row), `make bootstrap` before its first line, `bump-leg.py`, and the re-run `update-shape.py apply --pr` recognises. A scalar `openXdox`, a mapping `{a: b}` or `[5]` was read as a project with no legs: - the doctor's `legs` row said "a one-repository project has none"; - bootstrap's step (a) said "no submodule legs declared"; - `bump-leg.py` refused `bump-leg-unknown-leg` with "it declares" and nothing after it; - `update-shape.py` said "declares no assembly leg". Each now says #714's sentence for what it found, the value spelled by `repr` as the gates spell it, byte for byte with the gates' own `FINDING` lines: ``` manifest-legs: legs is 5, expected a list of `role:`, `repository:` and `path:` mappings manifest-leg: a leg is not a mapping: 5 ``` - **`shape-doctor.py`:** - `Context.legs` reads a list only, and `Context.legs_unread` gives the sentences. - `legs_unread_row` makes the `legs`, `leg shape files` and `placement` rows `n/a`. Each says the sentence, says the `manifest` row's command names the fault, and names that row in `waits_on`, as `waiting_row` does for a manifest that does not load (#205). - `--placement-plan` refuses by name and writes nothing. - A list holding legs beside such an entry still has its legs checked. - **`templates/assembly-root/scripts/bootstrap.py`:** step (a) says the sentence, places no leg for it, and places the legs beside it. Step (b)'s validators refuse, and the exit is theirs. - **`scripts/bump-leg.py`:** it refuses `bump-leg-manifest-legs`, exit 2, before anything is fetched and in `--dry-run` as well. A list holding legs beside such an entry is refused too: its dry run had previewed a bump the real run's own `validate-manifest.py` always refused. - **`update-shape.py`:** `update-pr-no-repository` says the sentence. Two entries that are not mappings are said together, joined by `; `. Absent, null and `[]` are unchanged, and the tests hold each reader to its old sentence. `templates/assembly-root/scripts/bootstrap.py` is in `shape_materialize.COPIED_VERBATIM`. No digest of it lives in this repository, so nothing is re-pinned here. In a project, `update-shape.py check` reports it `upstream-changed`, and `apply` re-copies and re-pins it. `tests/test_legs_readers_not_a_list.py` (74 tests) covers each site over `5`, `openXdox`, `{a: b}` and `[5]`, the entry rule (`[openXdox]`, `[null]`, `[5, 6]`), a value holding ESC and the mixed list, and holds absent, null and `[]` to each reader's old sentence. It kills round 0's ten mutants and RV862's eight (each reader's `None` guard and `; ` join, and `update-shape.py`'s stray `: `). With all four files at main's bytes, 56 of its 74 tests fail; the 18 that pass are its two controls and the 16 rows that hold a reader to its old sentence. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Lane: openreposhape-4 (openRepoShape-4)
Claim: #714 (comment)
What changed and why
Milestone v1.1.1: a DRAFT that lands after the v1.1.0 tag. The issue lists #634, #661 (#744) and #702 to land first, and all three are on main. #712's #827 changed
validate-pins.pytoo and has landed (9f235da2); this branch merges it (see Shared files). This PR leaves its function,_check_neutral_pin_entry_and_load, alone.L4-141: a
legs:that is not a list._project_leg_chains,_targets_from_project) andvalidate-pins.py(_load_legs_to_check) iteratedvalue or []:legs: 5ended both inTypeError: 'int' object is not iterable, exit 1.legs: openXdoxwas walked one character at a time. The naming gate found no target and read names from stdin. With stdin/dev/nullit refusednaming-no-target, exit 2; with a pipe left open it waited until killed.validate-pins.pyrefusedmanifest-no-legswith the lockstep remedy (git submodule update ...).validate-manifest.pysaidlegs is missing or emptyfor5, which is false.0,falseand''are not a list, as validate-repository-naming.py (_pins_from_project:302,_chain_from_leg:318), validate-manifest.py (_declared_pins:85) and validate-pins.py (PinSources:390, called at:698) iterate the project's ownneutral_product_pins:and a leg'snaming.referent_chain:without asking they are lists, so5(ortrue,1.5) in either field ends the naming gate inTypeError: 'int' object is not iterable, exit 1,neutral_product_pins: 5ends the other two validators the same way before validate-manifest.py reaches themanifest-neutral-product-pinsfinding it has for it, andmake validatestops in a traceback at its first command; a scalaropenXdoxinneutral_product_pins:is read as the charactersX,d,e,n,o,p,xby the naming gate, which classifies and exits 0 #443 readsneutral_product_pins:.validate-manifest.pykeepslegs is missing or emptyfor absent, null and[], and the naming gate now says it too under--project(below).validate-pins.py, a non-listlegs:is this finding and not themanifest-no-legsrefusal. The shape pin and the neutral-product pins are still checked. A refusal one of them makes (for exampleneutral-pin-missing) is printed alone, as every refusal of this gate is, so the finding is not printed then.[]are stillREFUSED manifest-no-legs, exit 2, invalidate-pins.py(lane 4's ruling on RV826's S2), and rows pin it.--project(lane 4's ruling). Under--projectthe naming gate never reads stdin, whateverlegs:holds, so absent, null,[]and[5]cannot wait either.naming-no-targetused to drop it.legs:absent, null or[]) is nowFINDING manifest-legs: legs is missing or empty, exit 1, asvalidate-manifest.pysays (lane 4's ruling). It wasREFUSED naming-no-target: no names given. Pass names, `--project project.yaml`, or names on stdin., exit 2, which offered--projectand stdin to a run that had passed--project. Under--project, a run with no target now always has a finding.--project,naming-no-targetis unchanged.legs:entry that is not a mapping ([5],[openXdox],[[a]],[null]; RV826's S3).a leg has no `repository:`, which is untrue of5.validate-manifest.pysaidmanifest-leg: a leg is not a mapping: 5.validate-pins.pyrefusedmanifest-no-legswith thegit submodule updateremedy.FINDING manifest-leg: a leg is not a mapping: 5, exit 1.validate-pins.pychoosesmanifest-no-legsonly where no entry was one.repository:still saysFINDING project.yaml: a leg has no `repository:`. It now names the manifest relative to the project root, through_shown, where the--projectpath was printed raw, as given.L4-142: a chain link or a declared pin that is not a name.
str():[5]was the pin or the link'5'.referent_chain: "\x1b[31mRED"exited 0 in the naming gate and invalidate-manifest.py, and--explainprintedCHAIN <ESC>[31mREDraw._not_a_nameis one rule, written in both files with the same words:neutral-pin-entry-malformedasks for);str.isprintable(_shown's rule).repr, which is what_showngives any entry the printability rule refuses:'',' ') is still the empty record: no chain and no finding, as validate-repository-naming.py (_pins_from_project:302,_chain_from_leg:318), validate-manifest.py (_declared_pins:85) and validate-pins.py (PinSources:390, called at:698) iterate the project's ownneutral_product_pins:and a leg'snaming.referent_chain:without asking they are lists, so5(ortrue,1.5) in either field ends the naming gate inTypeError: 'int' object is not iterable, exit 1,neutral_product_pins: 5ends the other two validators the same way before validate-manifest.py reaches themanifest-neutral-product-pinsfinding it has for it, andmake validatestops in a traceback at its first command; a scalaropenXdoxinneutral_product_pins:is read as the charactersX,d,e,n,o,p,xby the naming gate, which classifies and exits 0 #443 pins it.[openXdox, ''], recorded('openXdox',), exit 0. Its blank link is nowit is blank, exit 1, and the chain records nothing.L4-02: a name that is not UTF-8.
strictorsurrogateescapestdout intobackslashreplace, asvalidate-pins.py's_emit_reportdoes.--explainand the plain listing no longer end in aUnicodeEncodeErrorunderPYTHONIOENCODING=utf-8.strictstdin is read withsurrogateescape, as argv is.Atl\xffason stdin isAtl\udcffasand reachesFINDING naming-unclassified: Atl\udcffas, where it was aUnicodeDecodeError.Riders.
_pins_from_project:302,_chain_from_leg:318), validate-manifest.py (_declared_pins:85) and validate-pins.py (PinSources:390, called at:698) iterate the project's ownneutral_product_pins:and a leg'snaming.referent_chain:without asking they are lists, so5(ortrue,1.5) in either field ends the naming gate inTypeError: 'int' object is not iterable, exit 1,neutral_product_pins: 5ends the other two validators the same way before validate-manifest.py reaches themanifest-neutral-product-pinsfinding it has for it, andmake validatestops in a traceback at its first command; a scalaropenXdoxinneutral_product_pins:is read as the charactersX,d,e,n,o,p,xby the naming gate, which classifies and exits 0 #443's M6b).repo_shape.py'spin_source_env_sharedper chain) and L4-147 (scaffold-project.pyrecords a Kelvin sign as typed). This change does not cover either.Adoption (RV826's M1).
adopt-project.pyasks the project's ownvalidate-manifest.pyabout the manifest it would write.--pin null,Trueor007written bare (bare_pins, as before adopt-project.py:executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514) is now that validator'sit is not text, not adopt-project.py plan writesnull,trueand other YAML keywords unquoted, soplan --spec-path nullsilently mounts the leg at the defaultspec#181's misread.tests/test_adopt_plan_yaml_quoting.pyout ofREFUSEDintoPINS_NOT_TEXT, which coversplan,checkandexecute, andREFUSED's comment says so.executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514.Pinned copies (RV826's N4). The three scripts are pinned shape copies:
validate-repository-naming.pyis inshape_materialize.COPIED_FROM_SHAPE, so the issue's "(not pinned)" is wrong;validate-pins.pyandvalidate-manifest.pyare inCOPIED_VERBATIM.No digest of them lives in this repository, so nothing is re-pinned here. In a project,
update-shape.py checkreports each asupstream-changed, andapplyre-copies and re-pins them.Tests
tests/test_legs_not_a_list.pyis new and has 112 tests. It builds ontests/test_non_list_project_fields.py's harness (#443) and covers:openXdox,5,{a: b}, absent, null,[]and[5]: each finishes with exit 1 and its finding (legs is missing or emptyfor absent, null and[]);legs is missing or emptyfrom the naming and manifest gates alike;naming-no-target, in its own words, still refused without--project;--project, never with it;validate-pins.pystill checking the shape pin and the neutral pins pastlegs: 5;validate-pins.pystill refusingmanifest-no-legs, exit 2, for absent, null and[](S2);manifest-leg: a leg is not a mappingfor[5],[openXdox],[[a]]and[null]in all three gates, and for both entries of[5, openXdox], each once (RV826's NIT 1); and a mapping with norepository:naming the manifest relative and spelled (S3);_not_a_namereaders, word for word in both files;[openß], a blank string);--explainand the plain listing), on asurrogateescapestdout read as bytes (S1), and on a strict stdin.tests/test_adopt_plan_yaml_quoting.pygets RV826's M1 rows (PINS_NOT_TEXT).Targeted run at the first commit
8dbf3e5c: 2420 passed, 4 skipped, 1 xfailed in 923.59s, exit 0. The run covered the new file, the files that run the three gates or read their output (36 files in all, withtest_shape_doctor.py), andtest_repo_hygiene.py,test_cli_reference.pyandtest_agents_md_codes.py. The skips are platform-only:test_bump_leg.py:693(a disk that ignores case) andtest_shape_doctor.py:10429x3 (PowerShell, Windows only).Run after the ruling's commit
cf376b7a, which changes only the naming gate: 886 passed in 140.20s, exit 0, with no skips. It covered the new file, the naming-gate files (test_naming_policy,test_non_list_project_fields,test_chain_link_fold,test_chain_remedy_callers,test_link_source_chain_defaults,test_scaffold_referent_chain_links,test_doctor_validator_verdicts,test_doctor_validator_rows,test_validator_child_encodingandtest_pinned_copy_text), andtest_repo_hygiene,test_cli_referenceandtest_agents_md_codes.Run on the final bytes (fix round 1, head
680e26ab): 1767 passed, 3 skipped, 1 xfailed in 1038.26s, exit 0. It covered RV826's 8 files (test_legs_not_a_list,test_manifest_validator,test_naming_policy,test_validator_child_encoding,test_doctor_validator_verdicts,test_chain_link_fold,test_cli_referenceandtest_agents_md_codes),test_adopt_plan_yaml_quoting.py(518 tests, all passed),test_shape_doctor.py,test_scaffold_leg_path_collisions.pyandtest_repo_hygiene.py. The skips aretest_shape_doctor.py:10429x3 (PowerShell, Windows only).Run on the merge round's bytes (head
124bbd7b: the merge of main9f235da2and the NIT 1 row): 5415 passed, 4 skipped, 1 xfailed in 4943.86s, exit 0. It covered 70 files:legs:that is not a list ismanifest-legsin every gate, a chain link or pin that is not a name is a finding, and the naming gate never reads stdin under--project(#714) #826's own files;test_validate_pins_named_and_spelled,test_bump_leg_unreadable,test_refusal_default_remediation,test_validate_pins_unreadable_workflow,test_reader_refusals_named,test_pin_validator,test_manifest_validator);test_repo_hygiene,test_cli_referenceandtest_agents_md_codes;git grep -lfinds namingvalidate-pins.pyorvalidate-repository-naming.py.The skips are platform-only:
test_bump_leg.py:697(a disk that ignores case) andtest_shape_doctor.py:10429x3 (PowerShell, Windows only).Mutants, each reverting one part of the fix (all killed):
never_waits[absent],[null]and[empty-list],a_manifest_that_names_no_leg_is_said_alike[absent, null, empty-list]-naming, andnames_on_stdin_are_read_without_project_and_never_with_it; the refusal without--projectstill passestest_a_surrogateescape_stdout_writes_the_name_escaped_tootest_each_gate_says_the_two_fields_in_its_own_order[naming-pins-then-legs]test_validate_pins_still_refuses_a_manifest_that_names_no_leg[absent],test_validate_pins_still_refuses_a_manifest_that_names_no_leg[null]test_the_naming_gate_never_waits_on_a_stdin_left_open[a-leg-that-is-not-a-mapping],test_each_gate_says_a_leg_that_is_not_a_mapping_in_one_sentence[number-naming],test_each_gate_says_a_leg_that_is_not_a_mapping_in_one_sentence[string-naming],test_each_gate_says_a_leg_that_is_not_a_mapping_in_one_sentence[list-naming],test_each_gate_says_a_leg_that_is_not_a_mapping_in_one_sentence[null-naming]test_each_gate_says_a_leg_that_is_not_a_mapping_in_one_sentence[number-pins],test_each_gate_says_a_leg_that_is_not_a_mapping_in_one_sentence[string-pins],test_each_gate_says_a_leg_that_is_not_a_mapping_in_one_sentence[list-pins],test_each_gate_says_a_leg_that_is_not_a_mapping_in_one_sentence[null-pins]test_a_leg_with_no_repository_names_the_manifest_relative_and_spelled[as-scaffolded],test_a_leg_with_no_repository_names_the_manifest_relative_and_spelled[esc]test_a_leg_with_no_repository_names_the_manifest_relative_and_spelled[esc]validate-pins.py's kept refusal pass under itfor leg in not_mappings[:1]:)109 passed); with the[5, openXdox]row,1 failed, 111 passed:test_each_gate_says_a_leg_that_is_not_a_mapping_in_one_sentence[two-entries-pins]What it cannot tell
surrogateescape, and the plain listing used to print the raw byte. Following_emit_report, it now printsAtl\udcffas, the spelling stderr'snaming-unclassifiedalready used.validate-pins.pystill reads a pin entry holding ESC as a pin. It printsneutral-pin-missingwith the ESC raw in the pin path. That is L4-144, validate-pins.py: an unsearchablecontracts/still ends in a PermissionError, an unreadable pin is named by its absolute path twice, and a workflow name or a pin entry's control character reaches stderr raw #712's.make validatenow stops at the naming gate's finding first.' 'as a pin entry.validate-pins.pystill treats a whitespace-only entry as a pin and refuses itneutral-pin-missing. The other two gates now say it is blank. Every gate fails it.validate-pins.py(for Tracking: shape-doctor.py, reporting a repository's compliance #460's checklist). It is stillREFUSED manifest-no-legs, exit 2, where the other two gates saylegs is missing or empty, exit 1. Its remedy,git submodule update --init --recursive, cannot add a leg. It is kept because validate-pins.py and repo_shape.py: a pin, the manifest or a shape copy the gate cannot reach or read is refused by name, relative to the root, once and with its cause's advice, and every name and path it prints is spelled (#712) #827 rewrites the same function.validate-manifest.pystops atlegs:(RV826's N1). Withlegs: []andneutral_product_pins: [5]it gives one finding where the naming gate gives two. The rows pin each gate's order as it is.legs:(RV826's S4:shape-doctor.py:1182,templates/assembly-root/scripts/bootstrap.py:1111,scripts/bump-leg.py:205andupdate-shape.py:2504, at the merge head) still iteratelegs or []. Filed as shape-doctor.py, bootstrap.py, bump-leg.py and update-shape.py: a manifest'slegs:that is not a list ends in a TypeError, or reads as a project with no legs, where #714 makes the three gates reportmanifest-legs#832.Shared files
The merge round. #827 (#712) landed on main as
9f235da2. This branch merges it as a merge commit,07412007(parents680e26aband9f235da2), with no rebase or force-push. Onlytemplates/assembly-root/scripts/validate-pins.pyconflicted, in_load_legs_to_check;tests/test_adopt_plan_yaml_quoting.pymerged on its own. The resolution follows RV826's layout (#826 issuecomment-6240291087), run with a copy of itsresolve_827_r1.py:contracts/still ends in a PermissionError, an unreadable pin is named by its absolute path twice, and a workflow name or a pin entry's control character reaches stderr raw #712's paragraph, a blank line, then validate-repository-naming.py and validate-pins.py: a manifest'slegs:that is not a list ends in a TypeError or waits on stdin, a chain link that is not a name passes, and--explainends in a UnicodeEncodeError on a name that is not UTF-8 #714's two paragraphs.contracts/still ends in a PermissionError, an unreadable pin is named by its absolute path twice, and a workflow name or a pin entry's control character reaches stderr raw #712'sraise Refusal("manifest-unreadable", "project.yaml: not a mapping"), then this branch's lines after its own raise: thedeclaredread with itsmanifest-legsfinding, thenot_mappingsloop with itsmanifest-legfindings, and thelegsline.if not legs and not not_mappings:with validate-pins.py: an unsearchablecontracts/still ends in a PermissionError, an unreadable pin is named by its absolute path twice, and a workflow name or a pin entry's control character reaches stderr raw #712's relativeproject.yaml: no non-assembly legs declared._load_legs_to_checkis identical, as text, to RV826's pair resolution in both merge orders (r1pairA,r1pairB). The wholevalidate-pins.pyis byte-identical to its landing tree's (RV826-land).git merge-tree --write-treeof the head124bbd7bagainst main and each open PR, the heads fetched from GitHub byrefs/pull/<n>/head:legs:that is not a list, or holds an entry that is not a mapping, is said in #714's words, so the doctor's leg rows wait on themanifestrow instead of calling the project one with no legs, bootstrap places no leg for it, and bump-leg.py and update-shape.py refuse it by name where each ended in aTypeError(#832) #862 (lane 4's shape-doctor.py, bootstrap.py, bump-leg.py and update-shape.py: a manifest'slegs:that is not a list ends in a TypeError, or reads as a project with no legs, where #714 makes the three gates reportmanifest-legs#832) lands after this one.7777c7d0to89ef951cby a merge of main; both give tree911dbcd9.Closes #714
🤖 Generated with Claude Code
Summary by Sourcery
Make repository validation consistently report malformed manifest structures and names while avoiding project-mode stdin hangs and encoding failures.
New Features:
legs:values and non-mapping leg entries consistently across the naming, manifest, and pin validation gates.Bug Fixes:
Enhancements:
Tests: