Repository navigation
adopt-project.py: y() quotes what the plan's loader would read as a keyword, so --spec-path null, --id null and --tracking-branch true adopt as typed, and plan, check and execute refuse, before anything exists, a value the project's own loader would read back as something else from the line execute writes - #518
Conversation
…uld read back as something else, so `plan --spec-path null` no longer mounts the leg at `spec` and `--tracking-branch true` no longer reaches `execute` as a boolean (#181) `y()` wrote a string bare whenever it looked like a plain word, and `repo_shape`'s reader takes `null`, `Null`, `NULL`, `true`, `True`, `TRUE`, `false`, `False` and `FALSE` for a null and a boolean. On main `plan --spec-path null` wrote `spec_path: null`, which loads as no value, so `check` said "mount the legs at spec/" while the plan's own follow-up said `null/contracts` and `execute` registered the gitlink at `spec`; `--tracking-branch true` was read back as a boolean and the legs were created on a branch called `True`; and a source file named `null` was a plan entry with no path. `y()` now asks the loader instead of a list of words. A candidate is written bare only if `parse_yaml` gives the same string back in both places the writer puts one, after `key: ` and as a `- ` item (`- a:` is a one-key mapping, `k: a:` is not), and a line the reader refuses is a value it does not read back. The trailing-colon rule is that second question, so nothing else changed for a string that round-trips: a default plan on a fixed source is byte-identical to main's, and so is a plan of this repository. `yes`, `on`, `.inf` stay bare because this loader reads them as text. The same defect was in the one list of strings the plan writes without `y`: `--pin null` was written `pins: [null]`, which `Plan.load` drops, so the pin vanished. It goes through `y` too; `openGlass` and `opensoft/openGlass` are unchanged. `null` is a canonical one-segment leg path and not a Windows device name, so #169's check accepts it and `execute` mounts the leg at a directory called `null`: what was typed. Closes #181 Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…te` would write into `project.yaml` or a leg pin that the project's own loader reads back as something else, so `--spec-path null`, `--id true` and `--tracking-branch true` no longer end `adoption verified` before a bootstrap that fails or puts the legs on another branch (#181) 76a6153 made the PLAN say what was typed: `y()` quotes any string the plan's loader would misread. But `execute` writes the same values into the assembly root's templates as they stand, and `project.yaml` and `contracts/{spec,code}-pin.yaml` carry most of them bare (`id: {{PROJECT_ID}}`, `path: {{SPEC_PATH}}`, `tracking_branch: {{TRACKING_BRANCH}}`). So once the plan kept `null` as text, `--spec-path null` ended `adoption verified` and the first bootstrap failed on `path is None`; `--id true` and `--project True` on `id is True`; `--pin null` on `neutral-pin-entry-malformed`; and `--tracking-branch true` pushed the legs on `true` while bootstrap put them on a local `True` with no upstream (lane 2's review of 76a6153, P2-1). `2026`, `1.5` and `~` did the same on main already (#187's review). THE LOADER DECIDES. `loader_reading(line)` is the one question, asked by `y()`'s round trip (`_reads_back_as_text`, rewritten onto it; old and new bodies agree on 260,880 strings) and by the new gate `manifest_misreads`. The gate puts each value into every line of a templated YAML file (`shape_materialize.TEMPLATED`, the `.yaml` ones) that carries its placeholder, exactly as `execute` would, and asks the loader to read the line back; a pin is asked as the ` - <pin>` item `_template_values` writes. There is no list of words and no list of which placeholders are quoted: `elected_by: "{{ELECTED_BY}}"` is asked as the quoted line it is, so `Null` passes there and `CORP\tom` does not (the loader makes `\t` a tab). The values asked about: the id (named as derived when `plan` lowercased `--project`, or when the plan has no `id:`), the tracking branch, visibility, the elector, the election date, the reference, both leg paths and every pin. - `plan`: `REFUSED adopt-manifest-misread`, exit 2, before the plan is written; each sentence names the flag (and where `plan` took a value it filled in itself), the value, the line `execute` would write, what the loader reads it as, and "choose another value". - `check`: `FINDING plan-manifest-misread`, exit 1, with the remediation once after. - `execute`: refused through `_entry_findings` and #168's `_refuse_what_check_finds`, before any leg repository exists. Quoting the template placeholders instead was considered and not done: the same templates render every scaffolded project, and quoting is not enough on its own (`elected_by:` is quoted today and still misreads `CORP\tom`). THE REVIEWER'S QUESTION, TAKEN. A plan carrying an explicit null leg path (`spec_path: null`, `~`, or the key with nothing after it, which is what a plan written by main's `plan --spec-path null` says) was read as the default and mounted at `spec` while its own follow-ups said `null/contracts`. `_plan_leg_path` now hands back the None it holds, as #169 already does for an explicit empty value, and `check` and `execute` report `plan-bad-leg-path: legs.spec_path is not text: its value was read as no value at all`. `check`'s mount line leaves out a path that is not text instead of printing `None/`. The not-text sentence no longer says "so quote it": quoted, every such spelling is text the gate now refuses. P3-2: the test module's docstring said main refused a name ending in a line break as unparsable; on main 20e13f0 that plan reads the entry back as `trail` and `check` reports `plan-uncovered` and `plan-empty-entry`. Corrected. Tests: tests/test_adopt_plan_yaml_quoting.py replaces the old end-to-end block. Each refused value is refused at `plan` (18 flag values, plus an id `plan` derived from `--project` and an elector it took from `git config`), at `check` (20 plan values, plus an id derived from `legs.assembly`) and at `execute` before any leg exists (the same 20, needs_filter_repo). What is still accepted (`--spec-path legs/null`, `--tracking-branch 2026-q4`, `--id yes`, `--elected-by Null`) goes through `execute`, the recursive clone and `bootstrap.py`, and each leg is on `2026-q4` with `origin/2026-q4` as its upstream. The gate follows the loader when the loader learns a keyword, reads the template line for its quoting, and agrees with the three files rendered whole by `render` on every candidate. An explicit null leg path is a finding for `null`, `~` and nothing. tests/test_adopt_leg_paths.py: the not-text sentence has no "quote" and covers None. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #198 (20e13f0: #172 + #191, a leg's mount path must not be, hold or lie inside a path the assembly root holds, and `_leg_findings(plan, tree)` takes `tree` with no default), #201 (8df21e6: #179, the leg-path rule moved verbatim from adopt-project.py into scripts/shape_materialize.py, where scaffold-project.py asks it too) and #200 (a5b09c3: #174, the leg mount passes `--force` behind `_refuse_an_unmountable_path`). One merge, no rebase. Six conflicts, all in adopt-project.py: 1. Module docstring, `check`'s list: main's clause (neither equal to, around or inside a path the shape writes into the root, `shape/`, or a source path the plan keeps there), then this branch's (every value `execute` writes into the project's YAML one its loader reads back as that same text, #181), in one sentence. 2. The first `from shape_materialize import`: this branch's `TEMPLATED` and main's `assembly_root_paths`, both, in main's line layout. 3. The #169 section ("A leg's mount path is CANONICAL, or it is refused"): main replaced it with a pointer to `shape_materialize`, where the code now lives, and this branch had edited it in two places. Main's pointer is taken, and both edits are re-applied to the moved code in scripts/shape_materialize.py, unchanged in substance: `NOT_TEXT_KINDS` gains `type(None): "no value at all"` (an explicit null leg path, the reviewer's question on 76a6153), and `leg_path_problem`'s not-text sentence says "a leg path is text: write the path you mean, or remove the key to mount the leg at its role's own name" instead of "so quote it", with the comment saying why. Only a value read out of a plan file reaches that sentence: `plan`'s flags and scaffold-project.py's are always text, so the scaffold prints nothing new. 4. Two new sections inserted at one place after `_plan_leg_paths`: main's "A leg's mount path overlaps nothing the assembly root holds (#172)" first, beside the leg-path helpers it extends, then this branch's "What `execute` writes into the project is read back as written (#181)". Neither section is edited. 5. `_entry_findings`'s docstring: main's "and the two mount paths", then this branch's sentence on the misread findings. 6. `_entry_findings`'s body: main's `_leg_findings(plan, tree)` (#198 made `tree` required; dropping it raises TypeError on every `check` and `execute`), then this branch's `plan-manifest-misread` findings, whose remediation is still printed ahead of the submodule remediation. Outside the conflicts: main's second `from shape_materialize import` also takes `NOT_TEXT_KINDS`, which this branch's `_the_loader_made` reads, and its comment no longer says "`leg_path_problem` is not called here", because this branch's `_plan_fields` calls it. Everything else merged cleanly, tests/test_adopt_leg_paths.py included (main's `_leg_findings(plan, None)` and this branch's not-text test touch different lines). An explicit null leg path never reaches main's collision check or its mount guard: `_leg_findings` asks `_leg_collision_findings` only when `leg_path_problems` found nothing, and None is a `plan-bad-leg-path` there, and `_checked_plan_values` refuses it before `checked_value` and the mount. The resolution adds no line over 79 columns that neither parent has. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #173 (#165: verification passes over the two leg mounts and no other gitlink, with `adopt-root-submodule`, `adopt-unexpected-gitlink`, `adopt-duplicated` and `adopt-mount-missing`), which landed after this branch merged a5b09c3. No conflicts: git merged adopt-project.py and tests/test_adopt_submodules.py itself, and tests/test_adopt_verify.py is new. This branch's change set against main is line for line the one it had against a5b09c3. No rebase. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…way it was given and the way it was read, so `id: null` is no longer called "not in the plan", `CORP\tom` no longer looks like its own misreading, and a plan's `tracking_branch: 007` is no longer echoed as `'7'` (#181) The NITs of review181 (PR-READY at a0ff15b). Nothing is refused that was accepted, or accepted that was refused: only the sentences change. N3. A plan with `id: null` (what `main`'s `plan --project True --id null` writes) was named "id (not in the plan, so legs.assembly lowercased)". It is in the plan and sets nothing, and `Plan.get` reads it exactly as a missing key, so the derived id is now "not set in the plan" (`DERIVED_ID`, built by the new `_id_field`). N1. `--elected-by 'CORP\tom'` was refused as "is 'CORP\\tom' ... reads it back as the text 'CORP\tom'": `repr` doubled the backslash the person typed and spelled the loader's tab as the `\t` they typed, so each looked like the other. A value is now shown between backticks as it is typed, and a text reading names what a terminal shows as something else: "is `CORP\tom` ... reads it back as the text `CORP`, then a tab, then `om`" (`_worded`). The helper is not named `_spelled`: that is #166's path spelling, which escapes backslashes on purpose and is unchanged. N2. A hand-edited `tracking_branch: 007` or `TRUE` was named by `str` of what the loader read (`'7'`, `'True'`), which the plan never said. A plan value that is not text is now named by what was read, with its kind, then the text `execute` writes: "tracking_branch was read out of the plan as the number 7, ... and not as the text `7`". The same holds for an `id: 007` (now asked as read, not as `Plan.project_id`) and for a pin, which is taken as read rather than from `Plan.pins` and named "a pin in pins": the list was not what was read as true. N5. The import comment says `_the_loader_made` uses `NOT_TEXT_KINDS` only for a list or a mapping; the module docstring says `null`, `true` and `2026` misread where a template writes them bare, which `elected_by:` does not. The section comment gives the true reason the placeholders stay unquoted: it is left to a follow-up, because the same templates render every scaffolded project and quoting them changes both tools. N6. `'elected_by'`, `'elected_on'`, `'reference'`, `'PROJECT_ID'` and `' and '` reached three uses in this file: they are constants now, read by `_template_values` and `_refuse_unconsented_seeding` too (same text). The test file's repeats are constants as well. Tests: 405 in tests/test_adopt_plan_yaml_quoting.py (was 390). Each change was reverted alone in a scratch clone and the whole file run: N3 fails 4, the value as `repr` 69, a text reading as `repr` 11, a plan value made text first 8, the id as `Plan.project_id` 1, the pins from `Plan.pins` 1, a pin named `pins` 5. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #199 (4835fb4: the plan writer escapes every character that is not printable from the one table `repo_shape`'s reader resolves), #213 (404760f: `_split_flow` takes the character after a backslash inside a double-quoted flow item) and #208 (e7d1720: update-shape.py keeps the manifest's indentation). No textual conflict: this commit's tree is `git merge-tree --write-tree` of its two parents (0ac0750), and `_leg_findings(plan, tree)` keeps `tree`. `y` now asks `_reads_back_as_text` (this branch) and quotes with `repo_shape.double_quoted` (#199), and `_PLAIN_RE` ends at `\Z` (#199). Three SEMANTIC conflicts, fixed in the commit after this one so that this tree stays the mechanical merge: - `_shown` is defined twice: #199's `_shown(path)` for the plan report, and this branch's `_shown(line)` for a misread sentence. The later definition, this branch's, replaces #199's for all four of its callers. - #199's comment above `y` says a bare `null`, `true` or `false` is written unquoted, "#181's to fix": this branch quotes them. - tests/test_adopt_plan_yaml_quoting.py's docstring says the loader resolves `\n` and `\t` only and that `trail\n` splits over two lines; #199's reader also resolves `\r` and the code-point escapes, and writes `trail\n` on one line. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…so after the merge of #199 it no longer replaces #199's `_shown(path)` for the plan report, and the comments say what the merged writer and reader do (#181) The merge of main 4835fb4 had no textual conflict and three semantic ones, named in its message; this commit resolves them. - `_shown` was defined twice. #199's `_shown(path)` prints a name in `plan`'s and `check`'s report, raw unless it is not printable; this branch's `_shown(line)` quotes a template line in a misread sentence, between backticks. The later definition, this branch's, won for all four of #199's callers: at the merge commit 6b104cf five tests of tests/test_adopt_plan_escapes.py fail (the four names `plan` then `check`, and the source directory that is not UTF-8). The misread helper is `_line_shown` now; no top-level name is defined twice. - #199's comment above `y` said a bare `null`, `true` or `false` is written unquoted, "#181's to fix". `y` quotes them, asking the reader; the comment says so. - tests/test_adopt_plan_yaml_quoting.py's docstring said the loader resolves `\n` and `\t` only, and that `trail\n` splits over two lines. Since #199 the reader resolves `\r` and the code-point escapes too, and `y` writes `"trail\n"` on one line; the docstring says so. The gate needed no change to follow #199's reader: it asks the loader, so `--elected-by 'CORP\ralph'` and `--reference 'docs\x41.md'` are now refused, with the carriage return named (`CHARACTER_NAMES` gains it) and `docsA.md` shown as read. Both are test rows. `_print_what_will_happen` held two locals named `where`, #199's source and this branch's mount line; the second is `mounted`. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #197 (b538015: scaffold-project.py refuses a leg path over what the shape writes, through the overlap definition adopt-project.py asks, which moved into scripts/shape_materialize.py), #202 (150c41d: update-shape.py's `apply --add`) and #229 (39ac59c: the scaffold's leg mount passes `--force`). No textual conflict: this commit's tree is `git merge-tree --write-tree` of its two parents (da9dad2), and `_leg_findings(plan, tree)` keeps `tree`. No semantic one either: no top-level name in adopt-project.py, repo_shape.py, shape_materialize.py or scaffold-project.py is bound twice (imports included); #197's moved helpers keep the names this file called them by; and none of the new tests or scaffold lines asks the not-text leg-path sentence, `NOT_TEXT_KINDS` or the misread gate this branch changes. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #214 (06b5204: adopt-project.py and five other tools escape a name their stdout cannot encode, set once at the start of `main()`) and #217 (60d08a1: `shape_materialize.run()` and 26 other git and gh calls decode what the child wrote as UTF-8 whatever the locale). No textual conflict: this commit's tree is `git merge-tree --write-tree` of its two parents (8c198f2), and `_leg_findings(plan, tree)` keeps `tree`. No semantic one either: no top-level name in adopt-project.py, repo_shape.py, shape_materialize.py or scaffold-project.py is bound twice (imports included). #214 changes stdout's error handler only, and the misread sentences this branch writes go to stderr unchanged. #217 changes how a child's output is decoded, not the loader the gate asks. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #209 (2727b08: update-shape.py `apply --branch` prints the root's tracking branch as the `gh pr create --base`), #252 (4b2add8: repo_shape.py refuses a flow-mapping entry opening `? ` and reads an empty flow-mapping value as null), #195 (aaa40eb: adopt-project.py finds a `.gitmodules` git cannot read when the history holds a gitlink the tip does not), #210 (d89fffa: family.py `bump` to the current pin says `nothing to do`), #211 (eb6df75: shape-doctor.py's `manifest kinds` finding) and #248 (c3f4987: repo_shape.py keeps a block scalar's key and body as a pair). One merge, no rebase. One conflict, in adopt-project.py: 1. The import block: this branch's `import itertools` (`_worded` groups a value's printable characters with `itertools.groupby`) and #195's `import os` (`_exported_gitlink` passes git an `os.environ` with `GIT_GRAFT_FILE` set). Both are kept, in alphabetical order. This tree is `git merge-tree --write-tree` of the two parents (66cf0a2) with only that hunk's three marker lines removed. No semantic conflict: - No top-level name is bound twice in adopt-project.py, scaffold-project.py, scripts/repo_shape.py or scripts/shape_materialize.py, imports included. The names the two sides add to adopt-project.py are disjoint (33 here, 11 from #195), and the merged set is exactly the base's plus both. - `_leg_findings(plan, tree)` keeps `tree`. - #195's `_first_gitlink` calls #166's `_spelled(path)`, which this branch does not rebind: its own helpers are `_worded` and `_line_shown`. - #248 removes `_BLOCK_SENTINEL` from repo_shape.py; nothing here names it. - The gate still asks the real loader. `loader_reading` calls the `repo_shape.parse_yaml` that `execute` copies into the project (`COPIED_FROM_SHAPE`), and neither #252 nor #248 changed that function's interface. On both parents the gate gives exactly `parse_yaml`'s answer. It follows the new answers with no edit here: `tracking_branch: {a: }` is now a mapping where it was a refusal, `{? a: b}` is now refused where it was a mapping, and an item spelled as the old block-scalar marker is now the text it is. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #212 (5751460: `check` and `execute` spell every path a FINDING or a refusal names with `_spelled`, and `first_and_more` takes an optional `spell`), #251 (23e16ae: scaffold-project.py refuses a malformed `--referent-chain` link) and #206 (60588d5: family.py finds a member in any case). Main moved while the runs at the previous merge were going, and #212 changes adopt-project.py and scripts/shape_materialize.py, which this branch changes too, so it is merged again. One merge, no rebase. No textual conflict: this commit's tree is `git merge-tree --write-tree` of its two parents (7fe3ef6), and `_leg_findings(plan, tree)` keeps `tree`. No semantic one either: - No top-level name is bound twice in adopt-project.py, scaffold-project.py, scripts/repo_shape.py or scripts/shape_materialize.py, imports included. #212 adds no top-level name to adopt-project.py, and #251's four in scaffold-project.py are not this branch's. - #212 is about paths. This branch's `plan-manifest-misread` names a value and a template line, never a source path. The value goes through `_worded`, which names each character `str.isprintable` refuses, and the line through `_line_shown`, so the finding stays one line, which is #212's point. It still sits in `_entry_findings`, between #212's respelled leg findings and the submodule findings, and `execute` refuses on that same list. - This branch does not call `first_and_more`, whose new `spell` defaults to the old output. - None of the three commits changes scripts/repo_shape.py. The gate gives the same answers here as at the previous merge. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #207 (018ce0d: bump-leg.py rewrites and commits only the workflow files git tracks), #272 (67a398a: the descendant scaffold tests run validate-pins.py with --pin-source) and #205 (d94d155: shape-doctor.py calls a project.yaml or family.yaml that does not load INVALID). None of the three touches this branch's files. The lane's coordinator asked for the branch to be on current main, so it is merged. #212 (5751460) was already merged at 77218c8. One merge, no rebase. No textual conflict: this commit's tree is `git merge-tree --write-tree` of its two parents (8bfd548), and `_leg_findings(plan, tree)` keeps `tree`. No semantic one either: - No top-level name is bound twice in adopt-project.py, scaffold-project.py, scripts/repo_shape.py, scripts/shape_materialize.py or shape-doctor.py, imports included. #205 adds five names to shape-doctor.py, none of them this branch's. - tests/test_shape_doctor.py, which #205 extends, still hands the doctor's placement plans, whose `legs:` is a list, to adopt's `Plan` and `_leg_findings(plan, None)`. This branch's `_plan_leg_path` reads a list as it did, as the role's own name. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #215 (c2ff2b0: adopt-project.py finds a `.gitmodules` value git's submodule reader refuses), #241 (c3a9645: scaffold-project.py refuses a `--pin` whose file the shape writes itself), #284 (b30595c: repo_shape.py reads a quote as opening a quoted scalar only where one begins, and the materializer's `render` spells a value between a YAML template's double quotes with `double_quoted`) and #204 (be2790e: update-shape.py names the step that fails after `apply --branch`). One merge, no rebase. No textual conflict: this commit's tree is `git merge-tree --write-tree` of its two parents (405a63c), and `_leg_findings(plan, tree)` keeps `tree`. No top-level name is bound twice in adopt-project.py, scaffold-project.py, scripts/repo_shape.py, scripts/shape_materialize.py or shape-doctor.py, imports included; #215's eleven names, #284's three reader helpers and `double_quoted` in the materializer are none of this branch's. One SEMANTIC conflict, fixed in the commit after this one so that this tree stays the mechanical merge. `manifest_misreads` asks the loader about the line `execute` writes, and builds that line in `_written` by putting the value in its placeholder's place as it stands. #284's `render` no longer does that between a YAML template's double quotes: `elected_by: "{{ELECTED_BY}}"` and `reference: "{{REFERENCE}}"` get `double_quoted(value)`, which the loader reads back as typed. So at this tree the gate refuses `--elected-by 'CORP\tom'`, saying `execute` writes `elected_by: "CORP\tom"`, where it writes `"CORP\\tom"`. And it newly refuses `Robert "Bob" Smith` and `trail\`, which the previous head accepted, because #284's reader refuses the raw line the gate imagines. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…` as the materializer's `render` writes them since #284, the value spelled by `double_quoted` between the manifest's quotes, so `--elected-by 'CORP\tom'` and a name holding a `"` are no longer refused for a line `execute` does not write (#181) The merge of main be2790e brought #284, whose `render` spells a value between a YAML template's double quotes with `double_quoted`, the writer `repo_shape`'s reader takes back unchanged. `_written` still built that line with the value pasted in raw, so the gate asked about a line `execute` no longer writes. It refused `CORP\tom`, `docs\new.md` and `docs\x41.md` with a sentence about that line. It also newly refused `Robert "Bob" Smith` and a name ending in `\`, which the previous head accepted, because #284's reader refuses the raw line. - `_written` takes `render`'s two steps for one placeholder, in order: `"{{KEY}}"` becomes `double_quoted(value)`, then any other `{{KEY}}` the value as it is. Every templated file it reads is `.yaml`, which is what makes `render` take the first step. Since #284, no value between the manifest's quotes is misread; the bare placeholders are asked as before. - `FLAG_DEFAULTS` no longer names the elector `plan` takes from `git config user.name`: the manifest writes it between quotes, so no elector can be misread and the entry could not be reached. - The section comment says what the quoted line now reads back as. Tests (tests/test_adopt_plan_yaml_quoting.py), each where #284 changed what is true: - `REFUSED` loses its two rows `project.yaml` writes between quotes (`--elected-by 'CORP\tom'`, `--reference 'docs\new.md'`): 16 rows, so 16 refused at `plan` and 18 at `check` and `execute`. - `BETWEEN_THE_QUOTES`, new: the values the quotes misread before #284, `"`, a last `\` and a carriage return among them, are each asked as `render` writes them (`double_quoted`) and passed, for `elected_by` and `reference` (20 cases). - The `git config user.name` elector `CORP\tom` is taken by `plan`, written `"CORP\\tom"` into the plan, and passed by `check`. - `UNESCAPED`, which pins how a sentence shows a value the loader reads as OTHER TEXT (review181's N1), moves to the bare `tracking_branch:` placeholder with the value's own double quotes (a hand-edited plan's `'"CORP\tom"'`), where the loader still resolves the escape. It gains `a #b`, read as `a`. Its six escapes, the tab, the line break and the carriage return a sentence names among them, are the same. - The drift guard that renders the files whole with `render` and compares every candidate with the gate covers `reference` too. Revert lines, each measured alone on this commit's bytes, running test_adopt_plan_yaml_quoting, test_adopt_plan_escapes, test_adopt_leg_paths and test_template_quoted_values (900 tests): - `_written` pasting a quoted placeholder's value raw again: 23 fail (the 20 `BETWEEN_THE_QUOTES` cases, the `git config` elector, and the drift guard for `elected_by` and `reference`). - `FLAG_DEFAULTS` naming the elector again: 0 fail. No elector reaches a misread sentence since #284, which is why the entry goes. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in 6d8d88b (family.py `add` asks every refusal before it mounts anything) and ede15ce (shape-doctor.py's validator rows print the `PYTHONPATH` they ran under). Neither touches this branch's files; the lane's coordinator asked for the newest main, and tests/test_shape_doctor.py, which hands the doctor's placement plans to adopt's `Plan` and `_leg_findings`, is in this branch's targeted set. One merge, no rebase. No textual conflict: this commit's tree is `git merge-tree --write-tree` of its two parents (fc78b0b), and `_leg_findings(plan, tree)` keeps `tree`. No semantic one either: 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 or scripts/family.py, imports included, and neither commit touches the reader or `render`. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in 6f45bf9 (#196: `check` and `execute` judge an entry's path by one test, `entry_path_problem`, and `plan-unsafe-path` comes first), b421e6b (#254: bump-leg.py and update-shape.py hand the validators an explicit PYTHONIOENCODING), c0ed615 (#242: family.py `add` and `bump` under the operator's excludes) and 71b58a1 (#262, #263: `check` finds, and `execute` refuses, a path kept in the root where the shape writes). 6f45bf9 and 71b58a1 change adopt-project.py and scripts/shape_materialize.py, this branch's files. Merged by sha, one merge, no rebase. Three conflicts, all in adopt-project.py, each resolved by keeping both sides; the tree is otherwise `git merge-tree --write-tree`'s: - The module docstring's `check` paragraph: main adds "no path kept in the root where the shape writes" (71b58a1), this branch "every value `execute` writes into the project's YAML ... one its loader reads back as that same text (#181)". Both clauses kept, main's first, in the order `check` asks them. - `_entry_findings`'s docstring, which states the order of the findings: main's (each entry's path first, #196; what the root keeps where the shape writes, 71b58a1) with this branch's misread findings in their place, after the mount paths and before the source's own submodules. - `_entry_findings`'s return: main's `unsafe_remediation` and this branch's `MANIFEST_MISREAD_REMEDIATION`, both printed once, in the order of the findings they follow: the unsafe path's, then the misread's, then the submodules'. No semantic conflict found: - 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, scripts/family.py, scripts/bump-leg.py or update-shape.py, imports included. Main adds 18 names to adopt-project.py and 5 to shape_materialize.py, the branch none in common with them. #196's `_misread(path)` is a path git filter-repo would misread, not this branch's YAML question. - Main changes neither scripts/repo_shape.py nor templates/, nor `render`, `double_quoted` or `write_lf`, so the gate still asks the reader `execute` copies into every project, about the lines `render` writes. - This branch's edits to scripts/shape_materialize.py (None in `NOT_TEXT_KINDS`, the not-text sentence) are kept. - The one call main's new code makes into this branch's code is `_unsafe_path_findings`'s `y(entry.get('leg'))`, which names the leg as the plan writer spells it. - `_root_shape_findings` reads the plan's entries, never its leg paths, so an explicit null leg path (this branch's `plan-bad-leg-path`) does not reach it. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… writes it into a plan, so the merge of #215's `BOOLEAN_WORDS` no longer leaves the literal `"false"` written three times (S1192) (#181) The merge of main be2790e (3352be4) brought #215's `BOOLEAN_WORDS`, which spells `"false"` beside `y`'s own. `_the_loader_made`, this branch's, spelled it a third time: `"the boolean " + ("true" if reading else "false")`. The hand count of Sonar's rules was not made again after that merge, so it went unseen until the merge of 71b58a1. Counted on 2405670 against main 71b58a1: `'false'` 3x, main 2x. `y` writes a boolean as `true` or `false`, so every sentence is the same as before: `_the_loader_made` gives "the boolean true" and "the boolean false", which the REFUSED, CARRIED and READ_AS_NOT_TEXT rows assert. Counted again: in adopt-project.py the only literals of five or more characters used three times or more are the three main already used that often (`'utf-8'`, `'visibility'`, `'tracking_branch'`), and no function is over 15. Revert line: reverting this commit makes no test fail, because no sentence changes. It brings back `'false'` 3x, main 2x (S1192). Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ute` writes, whitespace after the pin and all, so a hand-edited `pins: ["openGlass "]` is no longer said to be written as `- openGlass` (#181) rv181f's F1, found at e337f44 and there since b26841b. `_pin_field` stripped the item line after the pin was in it, so the pin's own trailing whitespace was gone from the line the sentence names. `execute` writes the item as it stands (`write_lf` writes the text as given), so `check` said: a pin in pins is `openGlass `, which `execute` writes into project.yaml's neutral_product_pins as `- openGlass`, ... `_written` strips a template line before the value goes in, which keeps the value whole. `_pin_field` now strips only the item's indent, the same way. The decision does not change: the loader drops the whitespace either way, and that is why the pin is refused (rv181f: 0 of 87 decisions differ; the line shown was wrong in 29 of 29). Test: `test_a_pin_is_shown_in_the_line_execute_writes_whitespace_and_all` runs `check` on a hand-edited plan, for a pin with a space after it (the line shown between backticks as `- openGlass `) and one with a tab (shown as Python spells it, '- openGlass\t'). No test covered this before: rv181f's PINSTRIP revert row failed nothing. Revert line: `.lstrip()` back to `.strip()` fails both cases (2 failed). Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…er `chars`, so its loop no longer shadows the module's `run()` (#181) rv181f's F4. `itertools.groupby`'s groups were bound to `run`, the name of adopt-project.py's own subprocess helper. `_worded` calls no `run`, so nothing broke; the next edit to the function could have. A rename only: every sentence is the same. No test can fail with it reverted, and none is added. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in, by sha, the twelve commits after 71b58a1: c7c02fa (#299), 346079d (#216, adopt-project.py `plan`'s follow-up `git grep`), 34ecbc2 (#260, shape_materialize.py `render` in one pass), 86da808 (#253), 6f4ce47 (#302), 7491530 (#288), 7591455 (#279, tests/conftest.py), 87373a6 (#306), 8a14ac9 (#204), c1b6732 (#239), e43fa95 (#243, adopt-project.py's clone template and `adopt-mount-commit`) and a1b3a4b (#321). Three of them touch this branch's files. One merge, no rebase. No textual conflict: this commit's tree is `git merge-tree --write-tree` of its two parents (28c351f). 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, scripts/family.py, scripts/bump-leg.py or update-shape.py, imports included. Main adds four names to adopt-project.py and `YAML_PLACEHOLDER_RE` to shape_materialize.py, none of them this branch's, and no function is changed on both sides. One SEMANTIC conflict, fixed in the next commit so that this tree stays the mechanical merge. #260's `render` now replaces each placeholder in one pass, so a value is never searched again. `_written` still builds `execute`'s line in #284's two steps: first the quoted placeholder, and then the bare one over that output, which holds the value spelled by `double_quoted`. So for `--elected-by` or `--reference` holding its own placeholder, the gate asks about a line `execute` does not write, and refuses a value that reads back as typed: - `"{{ELECTED_BY}}"`: the gate builds `elected_by: "\""{{ELECTED_BY}}"\""`, and `render` writes `elected_by: "\"{{ELECTED_BY}}\""`; - `x{{ELECTED_BY}}`: `"xx{{ELECTED_BY}}"` against `"x{{ELECTED_BY}}"`; - `"{{REFERENCE}}"` for `--reference`, the same way. Every other probe value, and every bare placeholder, builds `render`'s line. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r` for the line `execute` writes, so since #260's one-pass `render` an `--elected-by` or `--reference` holding its own placeholder, such as `"{{ELECTED_BY}}"`, is no longer refused for a line `execute` never writes (#181) The merge of main a1b3a4b (bf6d656) brought #260 (34ecbc2): `render` puts each value in once and never searches it again. `_written` copied #284's rules in two steps, first the quoted placeholder and then the bare one, and the second ran over the first's output, which held the value. So a value holding its own token was put in twice, and the gate refused it, though `render` writes it as typed. At bf6d656, main's own `test_adopt_records_each_free_text_value_as_typed[every-key]` failed: `plan` refused with `adopt-manifest-misread`. - `_written` hands each template line that carries the placeholder to `render` itself, the same way the gate hands the line it builds to `loader_reading`. No copy of the materializer's rules is left to go stale. The table is the value, plus `_as_they_stand`: every other placeholder on the line given back as its own token, which `render` writes as it was, `double_quoted` spelling `{{KEY}}` as `"{{KEY}}"`. So the line differs from the template's by the one value, and `render` is never asked for a key it would refuse. - `render` and `PLACEHOLDER_RE` are imported from the materializer. Test (tests/test_adopt_plan_yaml_quoting.py): `HOLDING_A_TOKEN`, five values (each field's own token after a letter and between double quotes, and `{{NOPE}}`). Each is asked as `render` writes it, for `elected_by:` and for `reference:`, and passes (10 cases). Revert line: `_written` back to the two steps fails 5. These are the four own-token cases, `x{{ELECTED_BY}}` and `"{{ELECTED_BY}}"` for `elected_by` and the same two for `reference`, and main's `test_adopt_records_each_free_text_value_as_typed[every-key]`. With this commit, tests/test_template_values_single_pass.py is 164 passed. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s as far as writing, so a `tracking_branch` it refuses as `unsafe-value`, or a `visibility` gh takes no flag for, is no longer called misread under a remediation saying the run would end `adoption verified` (rv181f's F2, F2b) `_execute_gets_to_write(key, value)`, rv181f's prototype, is asked before `_plan_fields` builds a field for a MANIFEST_FIELDS key: - A `tracking_branch` must pass `checked_value`. `_checked_plan_values` refuses one it refuses as `unsafe-value` before `_refuse_what_check_finds` asks the gate (F2: 44 of rv181f's 540 sentences, all `tracking_branch`). - A `visibility` must be one of VISIBILITY_CHOICES. `_create_leg_remotes` hands any other to `gh repo create` as a flag before anything is written (F2b). `check` is not told whether `execute` will run with `--local-remote-dir`, so such a visibility is asked about in neither mode. TRACKING_KEY and VISIBILITY_KEY name the two keys once. tests/test_adopt_plan_yaml_quoting.py: - READ_AS_NOT_TEXT's list and mapping rows move from `tracking_branch` and `visibility` to `elected_on`, which `execute` writes as it stands. - UNESCAPED's 7 rows move from the TRACKING_BRANCH placeholder to ELECTED_ON (lines `elected_on: ...`), which a plan edited by hand carries as far as the gate. - EDITED_ONLY drops its `visibility: "true"` row (CARRIED 18 -> 17). - New, no marker: `check` names neither `tracking_branch: '"CORP\tom"'` nor `visibility: "true"` as misread, and prints no remediation for them. - New, with filter-repo: `execute` refuses that branch as `unsafe-value` before any leg exists, and `check` says nothing misread of it. - REMOTES names the local remotes directory once (S1192). The quoting file: 436 passed (435, less 2 CARRIED rows, plus 3 new). Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged by sha: origin/main at 16:30Z. Past a1b3a4b it brings these, among others: - f795cf7 (#255): `plan-unwritable-value`. - 161c6c9 (#245): relative `.gitmodules` urls. - a983e6a: `execute` verifies before it exits on a refused `gh pr create`. - 1f90a0f: the pin rewrite helpers in scripts/shape_materialize.py. - ab623dd: the session `gh` guard in tests/conftest.py. adopt-project.py conflicted in four hunks. Each resolution keeps both sides: 1. The module docstring's `check`. #181's clause about every value `execute` writes into the project's YAML now comes before the source's own submodules, which is where its finding comes. Main's relative url clause ends the sentence. 2. The `_entry_findings` docstring. This is main's text, with #181's misread values named after the root and before the submodules. 3. The `_entry_findings` return. - Findings: unsafe, coverage, legs and shape, misread (#181), submodules, unwritable (#255), relative url (#245). - Remediations: unsafe, misread, submodules, unwritable, relative url. - Main's NOTEs are third. 4. The mount line in `_print_what_will_happen`. #181's filter stays, so a leg path that is not text prints no `None/`, and main's `_shown` now wraps each mount and the branch. Everything else merged clean. At this merge, tests/test_adopt_template_values.py and tests/test_adopt_plan_yaml_quoting.py gave 514 passed. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, so a hand-edited `elected_on: "2026-09-02\nx"` or pin `"a\nb"` is one `plan-unwritable-value` finding, and `execute` refuses it under that code instead of `plan-manifest-misread` At the merge of main 1f90a0f (e8e0026), `check` named such a value twice. One finding was #255's `plan-unwritable-value`. The other was #181's `plan-manifest-misread`, under a remediation saying the run would still end `adoption verified`. That claim was false: `execute` refuses the plan before any leg exists. `execute` also refused it as `plan-manifest-misread`, the code of the first finding. tests/test_adopt_template_values.py did not show this (514 passed at the merge, with the quoting file): none of #255's rows is a value the loader also misreads. - `_execute_gets_to_write` returns False for a value `unwritable_reason` refuses. A `tracking_branch` goes through `checked_value` as before, which refuses every such value. A visibility goes through VISIBILITY_CHOICES as before, which hold no such value. - `_plan_fields` now asks `_execute_gets_to_write` about each pin too. - TRACKING_KEY in `_checked_plan_values`, and the four key constants in WRITTEN_PLAN_VALUES, replace their literals (S1192). tests/test_adopt_plan_yaml_quoting.py: - New, no marker: each of these is one `plan-unwritable-value` finding, with no misread finding and no misread remediation: - `elected_on: "2026-09-02\nx"`; - `visibility: "private\nx"`; - a pin `"a\nb"`; - a pin `"openGlass\t"`. At e8e0026 the date and both pins failed this test. - New, with filter-repo: `execute` refuses the date as `plan-unwritable-value`, with no misread finding, before any leg exists. At e8e0026 it refused the date as `plan-manifest-misread`. - PIN_WITH_A_TRAIL's tab row (rv181f's F1) is now a no-break space. Since #255, a pin holding a tab is #255's finding alone, so the tab moved to the table above. #255 writes a no-break space, the loader drops it, and the line is shown as Python spells it, just as the tab's line was. - `branches_of` names the split-branch check once (S1192). The quoting file and tests/test_adopt_template_values.py: 519 passed. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged by sha: origin/main at 16:31Z, one commit past the 1f90a0f merged at e8e0026. That commit is 92e29d9 (#319 via #377): `plan` asks about every source path `execute` will not extract as it is named. It touches adopt-project.py, tests/test_adopt_entry_paths.py, tests/test_adopt_literal_names.py, tests/test_adopt_plan_escapes.py and tests/test_adopt_submodules.py. No conflict: #319 changes the module docstring's `plan`, the entry questions and `cmd_plan`, none of which this branch changes. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged by sha: origin/main at 19:37Z. Past 92e29d9 it brings twelve commits. These four reach this branch's files or the reader its gate asks: - 3ef059c (#314, for #258 and #261): `plan-manifest-invalid`, asked of the project's own validate-manifest.py, through `_manifest_findings`. - cb7063f: the reader refuses a bracket in a plain flow scalar. - f5cb616: docstrings in scripts/shape_materialize.py. - dd8d0ea: `_elector` keeps a `user.name` byte that is not UTF-8, for #255. adopt-project.py conflicted in two hunks, both in `_entry_findings`. Each keeps both sides: 1. The docstring. The order is this branch's misread values (#181), the submodules, #255's unwritable values, then main's validator (#261), and the relative urls (#190) last. 2. The return. - Findings: unsafe, coverage, legs and shape, misread, submodules, unwritable, validator, relative url. - Remediations: the same order. - `_manifest_findings(plan)` is called as main calls it. Everything else merged clean. That includes `_checked_plan_inputs` and `_template_values`, which main split into `_template_table`. At this merge a value both gates judge is two findings: `id: "007"`, `id: "null"` and `elected_on: "20260902"`. Main's own `test_a_text_value_the_reader_misreads_is_one_finding_in_the_list_check_prints` fails for `id-007` and `elected-on-basic-format`, as its docstring says it will on a merge that does not compose the two. A `visibility` outside the three choices that is not text (`true`, `[secret]`) is no finding at all. The next commit composes them. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… plan value once: the validator is not asked about a value #181 reported, and is asked about every other, so `id: "007"` is one finding and not two, and `visibility: true` or `[secret]` is one and not none At the merge of main dd8d0ea (4c6e856), `_entry_findings` called `_manifest_findings(plan)` as main does. The two rules then disagreed: - **Two findings for one value.** `id: "007"`, `id: "null"` and `elected_on: "20260902"` were both #181's `plan-manifest-misread` and the validator's `plan-manifest-invalid`. Main's own `test_a_text_value_the_reader_misreads_is_one_finding_in_the_list_check_prints` failed for two of its rows. - **No finding for one value.** #181 does not judge a `visibility` outside the three choices (rv181f's F2b). Main's validator left every value the plan reads as not text to "the misread rule". So `visibility: true`, `[secret]` and `{a: b}` got no finding at all. - **The trap.** Main's own hook, `misreads_elsewhere=True`, would have dropped `visibility: "true"` too: it leaves out every value the reader misreads, not the values #181 reported. The composition: - `_plan_fields` returns each field with the placeholder it is about. - `_misread_findings` returns #181's FINDINGs and the placeholders they report. - `_manifest_findings(plan, reported)` leaves out exactly those placeholders and asks the validator about every other value. - A value the plan reads as not text is named by what was read (`_named_as_read`): "visibility, read out of the plan as the boolean true, is 'True', which the project's own validate-manifest.py refuses: manifest-visibility: visibility is True, ...". The validator's sentence quotes `str` of the value, which the plan never said (review181's N2). - `_execute_gets_to_write`'s docstring now says the validator names such a visibility in either mode. `--local-remote-dir` no longer writes it unasked. - `misread_placeholders` is no longer imported. - MANIFEST_PLAN_KEYS and MANIFEST_FLAGS are derived from #181's ID_PLACEHOLDER and MANIFEST_FIELDS, so each placeholder and each key is spelled once (S1192: the merge had made each placeholder 4 uses against main's 3). Probe of `check` over 26 edited values, before and after: - Every misread or invalid value is now exactly one finding. - A value #255 refuses (a line break) is still listed by #255 and the validator both, as main's `_entry_findings` docstring rules (#255 before #261). - A `tracking_branch` that `checked_value` refuses is still no finding in `check`, and `execute` refuses it as `unsafe-value`, as on main. Tests: - tests/test_adopt_plan_yaml_quoting.py - New, no marker: JUDGED_ONCE, 8 values. Each is one finding from the rule that is true of it. - New, no marker: NOT_TEXT_VISIBILITY, 2 cases, covering the naming. - New, with filter-repo: `execute` refuses `visibility: "true"` under the validator's code before any leg exists. - CARRIED's `check` and `execute` tests and READ_AS_NOT_TEXT now also assert no `plan-manifest-invalid`. - EDITED_ONLY's comment: since #261, `plan` refuses `--elected-on 20260902` on every Python. - tests/test_adopt_manifest_values.py (main's) - Three calls move from `misreads_elsewhere=True` to `reported`. - `visibility: [secret]` leaves `test_a_value_the_reader_reads_as_another_kind_is_not_this_questions`: it is the validator's now, named by what was read (JUDGED_ONCE). The quoting file, tests/test_adopt_manifest_values.py and tests/test_cli_reference.py: 543 passed. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main 75052cb, merged by sha: #188 with #171 and #177 (PR #445), on top of decff87 (#237), dba5fb0 (#383) and 245d775 (#412). Only 75052cb touches this branch's files, in adopt-project.py. It changes neither the reader (scripts/repo_shape.py), scripts/shape_materialize.py nor a YAML template, so the gate asks the same reader of the same lines. One conflict, in adopt-project.py, resolved keeping both sides: - The `from shape_materialize import (...)` list. This branch added PLACEHOLDER_RE, TEMPLATED and render, which `_written` and `_as_they_stand` use. Main dropped RULESET_HINT: 75052cb prints the adoption's own ADOPT_RULESET_HINT instead (#177), and nothing else uses the scaffold's. The list is main's, without RULESET_HINT, plus the three, each name once. The tree is otherwise `git merge-tree`'s. No function is changed on both sides. The top-level names are exactly both sides' (this branch's additions, main's 25, RULESET_HINT and `misread_placeholders` gone), and none is bound twice in adopt-project.py, scaffold-project.py or scripts/repo_shape.py. scripts/shape_materialize.py binds YamlError and parse_yaml twice, as main does. How #188 meets the gate. `_refuse_what_check_finds` is unchanged in place: it still runs before #188's `_refuse_an_occupied_work_dir` and `_refuse_a_split_branch_left_behind`, which run before the template table and the leg repositories. So this branch's `plan-manifest-misread`, and #261's `plan-manifest-invalid`, still refuse before anything exists, whatever the work directory or the source's branches hold. Probed: a plan carrying `id: "true"` or `visibility: "true"`, with a --work-dir that already holds the assembly's directory, or with a split branch in the source that the push cannot fast-forward, is refused under the gate's code, exit 2, with no leg repository made and the source's branches as they were. With a valid plan, the same two states are refused as `adopt-work-dir-occupied` and `adopt-branch-exists`. #188's late-failure reporting covers the steps once a leg repository exists, where this gate never refuses. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged by sha, e6fbd74, fetched at 22:23Z, on top of 180902f. It brings seven commits since 191105a, oldest first: cdcd2f0 (#509), fc48854 (#496), f391e58 (#435, the plan's field types), d52b100 (#506), 8cd7dbe (#347), d41cc29 (#402) and e6fbd74 (#515 for #514: `execute` writes each `neutral_product_pins:` entry so it reads back as the text `plan` recorded). No conflict: the tree is `git merge-tree`'s (8178281). One function changed on both sides, `_template_table`: this branch spells three plan keys by their constants (`ELECTED_ON_KEY`, `REFERENCE_KEY`, `ELECTED_BY_KEY`), and #514 takes the pins' list from `_neutral_product_pins(pins)`. Both are kept. No top-level name is bound twice in adopt-project.py; shape_materialize.py and family.py import `parse_yaml` and `YamlError` twice on both parents, the same objects. #435 comes first. `check` returns on `_plan_type_findings` before any of this branch's questions, and `execute` calls `_refuse_a_mistyped_plan` before it opens the source. Probed: `source:` or `legs:` set to 7, true or a list, and `allow_empty_legs:`, `paths:` or `pins:` set to something that is not a list (16 plans). Each is one finding at `check`, and `execute` refuses it before the work directory or a remote exists. None prints `manifest-misread` or #181's remediation, and none ends in a traceback. #514 quotes the one line this gate still found a misread on. This gate asks `_template_table` (`_pin_field`), so it asks the quoted line with no edit here. At this merge the quoting file, main's tests/test_adopt_manifest_values.py and tests/test_adopt_pins.py and tests/test_template_text_placeholders.py give 14 failed and 803 passed. They are the 14 rows round K4 named, each asking what #514 changes. The next commit makes them true. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged by sha, 89c6a6c, fetched at 22:55Z, on top of the merge of e6fbd74 (f54dce7). It brings two commits: 751cb2f (#507: scripts/family.py, two functions appended to scripts/repo_shape.py, `shell_quoted` and `shell_line`, setup-project.py, the family root's templates and tests) and 89c6a6c (#517 for #516: scaffold-project.py writes each `neutral_product_pins:` entry so it reads back, and `templates/assembly-root/contracts/neutral-product-pin.yaml` writes `product: "{{PIN_PRODUCT}}"` for both tools). No conflict: the tree is `git merge-tree`'s (f30b07d). One file changed on both sides, tests/test_template_text_placeholders.py, in different places, and both are kept: this branch's comment above `ADOPTIONS`, which says `plan` quotes `true` and `null` now, and #516's docstring paragraph, `neutral_pin_path` import, `TEXT_FIELDS` row for the pin's `product:` and the adoption test of the pin file. adopt-project.py, scripts/shape_materialize.py and this branch's other test files are not touched by either commit. The YAML reader in repo_shape.py is unchanged: #507 appends two shell-quoting functions after it. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…True@` and `007@` are planned, checked and adopted as given, and the misread gate's refusals, #479's branch questions and the validator-first order at `plan` are asked of lines written bare again, so no test of the gate passes with its change reverted (#181) Main's e6fbd74 (#514) quotes an item of the manifest's `neutral_product_pins:` list the project's reader would misread, and 89c6a6c (#516) quotes the pin file's `product:`. The gate asks the table `execute` writes the list from (`_pin_field`), so it asks the quoted line with no edit, and on the shipped templates it now finds nothing. The 14 tests that asked the bare line become true: - `--pin null@`, `True@` and `007@` move from `REFUSED` to `READ_BACK`: `plan` writes `product: "null"`, `check` passes it, and `execute` writes `- "null"` into the manifest and `product: "null"` into `contracts/null-pin.yaml`, each read back as the text given. - `test_plan_refuses_a_value_the_projects_loader_would_misread`, `test_check_reports_a_plan_carrying_one` and `test_execute_refuses_a_plan_carrying_one_before_any_leg_exists` run in this process over a line written bare: `REFUSED` is the branches `true`, `null` and `007` over `bare_template` and the pins `null`, `True` and `007` over the new `bare_pins`, the pins' list written as before #514. The check test also asks that the finding is the only one judged and the remediation is printed once. - `JUDGED_ONCE` loses `pin-text-misread` (its successor is the check test above) and `pin-the-reader-refuses`, and `JUDGED_BY_THE_VALIDATOR` loses `its-reader-refuses-the-file`. No value of any key the manifest is rendered from makes a manifest the validator's reader refuses on the shipped templates (probed, 3384 plans), so G2 is asked of the same pin `? x` over `bare_pins`, in `test_a_manifest_its_reader_refuses_is_the_validators_alone`. - The template-line test expects `- "Null"` and no finding. The table test drops its first assertion. Its stub that quoted each item would now quote an item twice, so it asks the real table (`- "null"`, passed) and then the list written bare (`- null`, refused). - The keyword-learning test fixed the pin's line before teaching the reader, so it refused a line `execute` no longer writes. It now asks the pin after teaching (`- "yes"`, passed) and a branch over `bare_template` (refused). - The two #273 pin tests that K7 and K8 revert run in this process over `bare_pins`, where a pin asked about would be named. rv181g's B1 at 180902f: since #259 no test held #479's composition or the validator-first order at `plan`. Added: `test_a_branch_execute_does_not_push_in_every_mode_is_never_misread` (the source lacks `true`, its `origin` lacks it, only `origin` has it; `plan` and `check` in this process over `bare_template`), `test_the_source_says_whether_execute_pushes_the_legs_to_a_branch` (`_pushed_in_every_mode` for four sources) and `test_plan_asks_the_validator_before_the_misread_rule` (`--id true`, with the validator given the same bare copy). The shipped #479 tests say they pass there because the line is quoted. #514's docstring on `_neutral_product_pins` said keywords `y` leaves bare reach it "only from a plan edited by hand, because `check` refuses the bare `product: true` that `plan` writes". With this branch's `y` that is false: `plan` writes `"true"` and `"null"` and `check` passes them. It says so now. That docstring is the only adopt-project.py change since the merges. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Sorry @brettheap, your pull request is larger than the review limit of 150,000 diff characters |
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. What shall we delve into next? Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Merged by sha, 8beb3d9, fetched at 03:08Z, on top of 9efe95a. It brings three commits since 89c6a6c, oldest first: 173c00e (#510 for #500: `openRepoShape --doctor`), cf118ff (#513 for #499: `--org` and `--project` must name the repository the source names) and 8beb3d9 (#512 for #501 and #502: update-shape.py, shape-doctor.py and `collision_follow_up` in scripts/shape_materialize.py). One conflict, one hunk, in adopt-project.py `_checked_plan_inputs`, the only unit both sides changed. Main passes `source` to `_check_names` for #513's own-name rule; this branch follows that call with #181's comment and its two refusals (`_refuse_unholdable_values`, then `_refuse_misread_values`). Resolved as main's call with `source`, then this branch's comment and two calls, unchanged in order. Main's other change to the function, `_refuse_another_repository` before any pin's digest is read, merged cleanly above it. So #513 refuses a plan for another repository before #181 asks any value. The comment in the hunk is brought up to date here (rv181g's N-7): since #259 and #514 a value the loader would misread is only one on a line written bare, no longer a pin's product. No code line differs from rv181g's proposed resolution (d55b975, the same merge with cf118ff, whose adopt-project.py is byte for byte 8beb3d9's). Every other path is `git merge-tree --write-tree 9efe95a 8beb3d9`'s. scripts/shape_materialize.py merges cleanly: #512 changes `collision_follow_up` alone, a function this branch does not touch. No top-level name is bound twice in adopt-project.py, and no name is read and never bound; shape_materialize.py and family.py import `parse_yaml` and `YamlError` twice, as on main. With this merge alone five tests of this branch's quoting file fail: their source's `origin` is a path ending in `seed`, which #513 reads as the repository and refuses `plan --org testorg --project Northwind` as `adopt-not-the-source`. The next commit puts that `origin` under `testorg/Northwind`, as cf118ff did for tests/test_adopt_tracking_branch.py. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…` puts the source's `origin` under `testorg/Northwind`, so the five tests that plan from it are no longer refused as `adopt-not-the-source`, and adopt-project.py's comments no longer say a pin's product is written bare (#181) M1 (rv181g, K5). #513 reads a path `origin`'s last two directories as the source's repository and refuses a plan for another. `with_origin` made `origin` a directory named `seed`, so `plan --org testorg --project Northwind` was refused as `adopt-not-the-source` before #181 asked anything, and five tests failed at the merge: test_a_misread_branch_origin_lacks_is_479s_finding_alone, test_a_misread_branch_only_origin_has_is_named_by_neither, test_execute_refuses_a_branch_only_origin_has_under_a_local_remote and test_a_branch_execute_does_not_push_in_every_mode_is_never_misread [origin-lacks-it] and [only-origin-has-it]. `origin` is now `<tmp>/testorg/Northwind`, as main's cf118ff did for tests/test_adopt_tracking_branch.py. The text is rv181g's scratch commit 163fef9's, blob for blob. The stale comments (rv181g's N-1 to N-6 and N-8; N-7 is in the merge). Each said a pin's product is still written bare, or that a pin `? x` makes a manifest its reader refuses. Since #514 neither is true of the shipped code. Each old text occurred exactly once. None is printed: only the module docstring's first line feeds `--help`, and `render-cli-reference.py --check` is ok. With docstrings blanked, the module's AST is unchanged. - N-1, the module docstring: "where a line is written bare (since #259 and #514 no line `execute` writes them into is)". - N-2, N-3 and N-4, the section comment above the gate. - N-5, `_pin_field`'s docstring. - N-6, `_misread_findings`' docstring. - N-8, the #261 section comment. Sonar's new smells on 9efe95a. - S9073: seven composite asserts in the quoting file are split into plain asserts with the same meaning. They are in `assert_reads_back`, test_a_float_a_bool_an_int_and_none_are_still_typed_values (two), test_the_writer_follows_the_loader_when_the_loader_learns_a_keyword, test_plan_refuses_a_value_the_projects_loader_would_misread, test_the_misread_rule_names_an_id_it_derived_from_the_assembly and test_a_source_file_named_like_a_keyword_is_one_entry_with_that_path. `assert_reads_back` builds its message once, and both asserts give it. - S5778: the `pytest.raises` block in test_the_misread_rule_names_an_id_plan_derived_by_where_it_came_from held two calls. The `argparse.Namespace` source is now built before the block. - S1192: the third `"project.yaml"` was this branch's own line, `MANIFEST_YAML = "project.yaml"`. Nothing has read it since 180902f took the pin's line from `_template_table`, so it is removed and the module is back to main's two. Pointing it at `SHAPE_MANIFEST` instead would fail at import, because that constant is bound more than 1,600 lines later. Windows. A pytest plugin outside the repository made text-mode writes in the test process write `\r\n`, as on Windows: `io.open` and `open` with no `newline`, so `Path.write_text` too, and text-mode `input=` to a process. At PR #504's 9e8c672 it fails exactly the 17 tests of tests/test_adopt_root_symlinks.py the Windows job failed, with its message, and nothing else in that file. Over this branch's four changed test files, in both modes, it fails nothing. No `os.name` branch, `chmod`, text-mode stdin or path split is among their added lines. So nothing here changes for Windows. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged by sha, b0826e0, fetched at 12:45Z, on top of 6c9e585. It brings one commit since 8beb3d9: b0826e0 (#504 for #183 and #178: `check` follows each symlink the plan keeps where the shape writes its files, and `execute` refuses one the shape cannot write through before any leg exists). Three conflict hunks, all in adopt-project.py. Each keeps both sides: - The `from shape_materialize import (...)` list. This branch imports `PLACEHOLDER_RE`, `TEMPLATED` and `render`; #504 imports `SYMLINK_HOPS` (moved there from this module), `TOO_MANY_SYMLINKS` and `template_files`. Each name is imported once, in the list's own order (constants, then classes, then functions, each alphabetical), and each is read in the module. - `_entry_findings`' docstring, the order of the findings: the values `execute` would write for the loader to read back as something else (#181) after the mount paths, then the source's own submodules, then each symlink kept in the root that a shape file cannot be written through (#178, #183), then the rest as both sides have it. - `_entry_findings`' return. This branch's comment and its `tracking` and `misread` lines are kept. The findings are `findings + misread + split + _root_symlink_findings(plan, source, tree) + unwritable_findings + value_findings + tracking_findings + moved + field_findings`. The remediations are this branch's list, unchanged: #504 adds a finding and no remediation to it. Outside the hunks git merged both sides cleanly where both touched one unit: the module docstring holds #181's sentence on the values `execute` writes and #504's on symlinks kept in the root, and `y` holds #181's `_reads_back_as_text` question and #504's `FALSE_WORD`. #504 puts `work_root` before `cloned` in `Source.__init__`. Its own `Source.open` passes both, and every other call (all in tests) passes four arguments by position, so none shifts. scripts/shape_materialize.py merges cleanly, and no unit of it changed on both sides. Every other path is `git merge-tree --write-tree 6c9e585 b0826e0`'s, and adopt-project.py is its blob outside the three conflict blocks. No top-level name is bound twice in adopt-project.py, none is read and never bound, and no imported name goes unread. shape_materialize.py and family.py import `parse_yaml` and `YamlError` twice, as on main. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…`, `eol_line` and `with_eol_block`, are a `from shape_materialize import` statement of their own, so a pull request that adds names to the shared list above it merges without a hand resolution (#471) The first `from shape_materialize import (...)` statement is the list every change to this file's imports edits: #504 added `SYMLINK_HOPS`, `TOO_MANY_SYMLINKS` and `template_files` to it, and #518 (#181) adds `PLACEHOLDER_RE`, `TEMPLATED` and `render`. Git treats two changes to adjacent lines of one block as one conflict, so #471's names on lines of that same list made #518 conflict there a second time, over names it never touched, and whoever merged it had to rewrite the list by hand. The four names now sit in a statement of their own, after the one that imports #172's shape half, with that statement and a comment between them and the shared list. The shared list reads as main holds it. Nothing else changes: the set of names `adopt-project.py` takes from `shape_materialize` is the same 48, parsed with `ast` before and after, and every other statement of the module is the same. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Merged by sha, 3121945, fetched at 13:35Z, on top of b602c16. It brings one commit since b0826e0: 3121945 (#494 for #370: a refusal built without a remedy prints its `REFUSED` line alone, because `Refusal`'s default remediation is now none, and the assembly root's `validate-pins.py` gives its own refusals the lockstep remedy). No conflict. The tree is `git merge-tree --write-tree b602c16 3121945`'s (4a502cd) on every path, and no top-level unit of adopt-project.py, scripts/repo_shape.py, scripts/family.py or shape-doctor.py changed on both sides. #494 changes adopt-project.py in `_failed_loudly` alone. This branch's refusals each name their own remediation, so none falls back to the new empty default: `plan`'s `_refuse_unholdable_values` through `refuse_manifest_values` (`f"{remedy} {tail}"`), `plan`'s `_refuse_misread_values` (`MANIFEST_MISREAD_REMEDIATION`, then "No plan was written."), and `execute`'s `_refuse_what_check_finds`, whose list carries the misread remediation after its own. Every other `Refusal` in a unit this branch changed passes one too. No top-level name is bound twice in adopt-project.py, none is read and never bound, and no imported name goes unread. shape_materialize.py and family.py import `parse_yaml` and `YamlError` twice, as on main. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-yaml-keywords: clean, and its tree is 88c22f0, the tree of the scratch merge a9439715 every Phase A run was made on Main 5ed3e5d is #511 (lane 4) for #471, landed at 16:08:28Z, merged here by sha. The split appends one marked block of `/<copy> text eol=lf` lines to a source's own root `.gitattributes` and verifies that file as the source's bytes followed by exactly that block; `update-shape.py apply --add` extends it; the Windows job installs git-filter-repo 2.47.0. No conflict. The tree is 88c22f0, which is `git merge-tree --write-tree 42362de 5ed3e5d`, and the tree of a9439715bfb50186565526cdddc828f57b14409f, the scratch commit made before #511 landed from 42362de and #511's predicted squash bd78e56e (tree d58ea4b, which 5ed3e5d's tree is). So every Phase A result at a9439715 holds here: - one unit changed on both sides, `cmd_plan`, keeping this branch's validator-first comment and #511's `attributes_link`; every other unit is the one side's that changed it; no name in adopt-project.py is bound twice; - #511 adds no `check` finding and no refusal before any leg exists, and changes no template, no `render` and no line `execute` writes into the project's YAML; - the targeted sets, both modes, exit 0: 1928/1833 in 14 files, 3869/3686 in 65 more, 963/953 in 11, 364 in 10, 248/223 in five, and 37 in #511's tests/test_adopt_attributes_eol_block.py; the quoting file is 524; - the Windows newline emulation over this branch's four test files, test_adopt_root_symlinks and test_adopt_attributes_eol_block: 1280 passed, and 1207 passed with 73 skipped, 0 failed; - byte identity for the three flag sets and for a source with its own `.gitattributes`; the class fuzz (3146 rows, #181 names none, no false or double clause); the why, pin-file and residual drivers unchanged; - the composition probes with #504, #513 and #511. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
…5 cites the six pull requests merged since #402, section 6 drops the `--help` verdict line #510 fixed and AT-family N2, which #501's ruling decides, and adds 28 checklist lines from the parents, and section 7 lists the 3 open milestone issues (#472) Refresh round 2. GitHub was read from 2026-10-14T03:11:22Z to 03:18:32Z; main was 8beb3d9 (#512) throughout. Every merge commit of the 223 merged pull requests is an ancestor of it. Section 5 adds, from each squash message and PR body: - adopt-project.py: #515 (a pinned `007` listed as text) and #513 (a new "The source's own name" bullet: `adopt-not-the-source`, `plan-not-the-source`, rename first); - update-shape.py and bump-leg.py: #512 (a new bullet: no NEXT passes `--accept-local`; `merged-away` rows dropped by `apply`); - setup, scaffold, family and the shared helpers: #517 (a `--pin` product listed as text) and #507 (`family-landing-in-standard`, `family-on-tracking-branch`, a quoted NEXT that pushes the branch); - shape-doctor.py: #510 (the installed doctor keeps its clone, and passes `--json` and `--placement-plan` through). The bullet's "writes nothing into the repository" now adds "but a `--placement-plan` file you name": a relative plan file given from inside the repository is written there, as #460's new line says. Section 6 drops two lines and adds 28; no known-limit issue closed, so the 47 issues stay: - #457, 16 lines: L2-14, which #515 makes true of main; 10 of lane 4's 14 lines from #513 (00:06Z), two of them, a backslash `origin` and a trailing slash, in one bullet; and L1-K1, K2, K3, K7, K9 and K10 from lane 1 (01:17Z). - #458: AT-family N2 is dropped, as lane 4's status note there says (00:48Z); 2 lines added from #512. - #459, 3 lines that #507 and #517 make true of main: the validate-family remedies line (03:27Z), the README `--into` line (11:46Z) and L2-15. - #460: the `openRepoShape --help` verdicts line is dropped (#510 fixed it); the `shape currency` line is reworded, because since #402 the `apply --branch` is the row's WHY and since #512 it has no `--accept-local`, but under `--branch` it is still refused `update-branch-uncommitted` before `update-local-drift`; 7 of lane 4's 8 lines from #510 (23:34Z) are added. Left out, by round 1's rules: #513's `insteadOf` line and README sentence (wording), its comments line (comment wording), its extra `git config` read (no effect a person meets); L1-K4 (cannot arise today); L1-K5, K6 and K8, which describe #518's and #504's branches; and #510's `--help` synopsis column (cosmetic). #458's 03:26Z restatement is folded into its existing `repo_shape.py` line: #507 removed the instance it added. Section 7 lists #370, #471 and #472, the milestone's three open issues of 47, and says #178, #181 and #183 are not in the milestone. What was read at 8beb3d9, by `git show` or `git grep` of the code or text each line cites: - section 5: all six new sentences (adopt-project.py:6986-7030 and :7812-7960; update-shape.py:287 and :1939-1969; shape-doctor.py:3238-3243 and :3298-3314; scaffold-project.py:1293-1330 and the neutral pin template's line 30; scripts/family.py:620-720 and :925-960; openRepoShape:97-111, :489-490 and :540-558); - #457: L2-14, the backslash and trailing-slash line (`_remote_repository`, adopt-project.py:1164-1182), `check`'s missing rename-first remedy (:5137), L1-K3 (scripts/repo_shape.py:2048), L1-K9 (adopt-project.py:3316-3333); - #458: both new lines (shape-doctor.py:2005-2060 and :3222-3227), and N2's drop (update-shape.py:2915-2919 and `apply_command`); - #459: all three (validate-family.py:356-358 and :1066-1071; README.md:861- 862, docs/handbook.html:1247; scaffold-project.py:1313-1330 against adopt-project.py:7017-7030); - #460: the dropped and reworded lines (openRepoShape:97-100, docs/cli.md: 160-163; shape-doctor.py:3306-3314 and `cmd_apply`'s refusal order), the adoption NEXT with no `--org` (shape-doctor.py:6141-6143), the two `--placement-plan` line (openRepoShape:540-558, shape-doctor.py:6828), the "writes nothing" line (openRepoShape:55 and :605, README.md:1112, docs/handbook.html:1467) and the kept-clone line (openRepoShape:490); - kept lines whose code a landing touched: #458's "not hand-edited" commit and title-less `gh pr create` (update-shape.py:2349-2353, :3796-3797), AT-family N3, #460's `=word` line (`UNQUOTED_POSIX` admits `=`), and #459's unquoted setup `cd` and step (7) (setup-project.py:2023-2029, :2169). The rest rest on the posting lane's measurement, not re-read here: of #457's lines, lane 4's mirror path, `--source` after the clone, recorded `source:`, `ATLAS`, fork, `straße` and null-`org:` lines (measured at cf118ff; adopt-project.py is the same at 8beb3d9), and lane 1's L1-K1, K2, K7 and K10 (measured on 8beb3d9); of #460's, the TERM, interrupted-run and drifted-project-test lines (measured at 173c00e; `openRepoShape` is the same at 8beb3d9). The file is 7,354 words, from 6,611: section 5 is 2,291 (2,085), section 6 4,351 (3,607) and section 7 109 (316). No test names `docs/release-notes/` (`git grep -n release-notes origin/main -- tests/` finds nothing), but two tests in tests/test_repo_hygiene.py read every tracked `.md` through `git ls-files`: the host-absolute-path test (`HOST_ABSOLUTE_PATH`, :1496-1527) and the "<word> naming families" count (:1545-1601). Their two patterns, applied to this commit's file, match nothing. No test was run. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…5 cites #504 and #494, section 6 drops #178 and #183, which #504 closed, adds the four limits #504 disclosed for #457 (J1, J2, J3, reftable) now that they describe main, and narrows two lines #504 and #494 made partly untrue, and section 7 lists the 2 open milestone issues (#472) Refresh round 3a, against main 3121945 (#494, merged 2026-10-14T13:21:57Z). Two pull requests merged after round 2's 8beb3d9, and both merge commits are on main, as are all 225: - #504 (b0826e0, lane 1, closes #178 and #183) under adopt-project.py, as a new bullet, "Symlinks the root keeps": `plan-root-symlink` and `adopt-symlinks-off`, both before any leg. - #494 (3121945, lane 3, closes #370) under the shared helpers, where #370's parent #459 puts it: a refusal with no remedy of its own prints its `REFUSED` line alone, `validate-pins.py` keeps the lockstep remedy, and `family.py add` refuses `gitmodules-unreadable`. Lane 3 asked on #473 (13:22Z) that #370 be placed in the notes. Each line was written from the PR body and its squash message, and its central claim read in main's code: `_entry_findings` and `_root_symlink_findings` (adopt-project.py:4701, :5717), `_refuse_symlinks_checked_out_as_files` (:5824) before `_create_leg_remotes` (:7542, :7580); `Refusal`'s empty default (scripts/repo_shape.py:166-174), `validate-pins.py`'s handler (templates/assembly-root/scripts/validate-pins.py:1181-1184), family.py's `gitmodules-unreadable` (:1912-1926), and the doctor rows that take `exc.remediation or None` (shape-doctor.py:4546). Section 6: - Dropped #178 and #183, closed by #504. - Added L1-J1, L1-J2, L1-J3 (#457, 2026-10-12T23:36Z) and L1-K8 (#457, 2026-10-14T01:17Z). Round 2 left them out because they described #504's branch. #504's body lists all four as its known limits. J2's `check` half (the refusal and its false sentence), J1's `check` half (`plan ok`) and J3's `check` halves were run at 3121945 in an offline probe. J1's and J3's `execute` halves rest on lane 1's measurement and the code (`_has` and `CLONE_GIT_TREES` at :5427-5438; `adopt-symlinks-off` at :5824-5866). K8 is read from the code only (`CLONE_GIT`, :5201-5206), and its line says it is untested. - Narrowed L1-K1's line. A link at `contracts/shape-pin.yaml` that leads nowhere is now a `plan-root-symlink` finding, since the walk follows `SHAPE_PIN` (shape_materialize.py's `assembly_root_paths`). The probe ran it, and a real file there still passes `check`. The line now says a file, or a link there to a declared pin's file, as K1 measured. - Narrowed #408's line. The issue's own repro, a policy file that does not parse, carried `repo_shape`'s default lockstep remedy, which #494 removed, so the `placement` row now has no NEXT. Only a policy file that is not UTF-8 still gets prose there (`load_yaml`'s own remedy, which holds no backtick). An in-process probe of `PathPolicy.load` with main's and 8beb3d9's repo_shape.py shows both. - Kept L1-K2, K9 and K10, which lane 1 measured as the same on #504's branch. The five parents have no new comment and an unchanged body since round 2's read (03:11Z to 03:18Z). Their sub-issues still list every open, unmilestoned one: 45 (#457 10, #458 6, #459 17, #460 9, #461 3). Section 7: the milestone is 45 closed and 2 open (#471, #472). #370 left the list. Of the three issues section 7 named as outside the milestone, only #181 is open. No claim is made about an unlanded pull request (#511, #518), and nothing is said about a tag or a date. 7,354 words before, 7,628 after. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…5 cites #511 and drops the Windows qualifier now that the Windows job runs the adoption suites, section 6 cites the v1.1.0 issue of each line the triage scheduled, drops the five lines closed on their parents and #408, and adds #511's line-ending limits, and section 7 lists #472 alone (#472) Refresh round 3b, against main 5ed3e5d (#511, merged 2026-10-14T16:08:28Z, closing #471). All 226 merge commits are on main. Section 5. #511 is cited twice: under adopt-project.py, as a new bullet on a source's own `.gitattributes`, and under the test suite and CI, in the "Three operating systems" bullet. "Every source path is verified by blob sha" gains the exception AGENTS.md step 7 now states. The claims were read in main's code: - `.github/workflows/tests.yml`:171-182, where `tests-windows` installs `git-filter-repo==2.47.0`; :209, where it runs the whole suite; and :118-125, where it runs on pull requests, not on a push to main; - `scripts/shape_materialize.py`: `EOL_BLOCK_HEAD` (:701), `eol_line` (:712-721), `eol_block_paths` (:724-729), `with_eol_block` (:738-744), and `_append_eol_block` (:813-831), which writes through no symlink, as called from `materialize_assembly_root` (:2256-2257); - `adopt-project.py`: `_land_the_eol_block` (:8081-8124, called at :8063) and the split message's `APPENDED TO` (:7953); - `update-shape.py`: `_extend_the_eol_block` (:3061-3089). The qualifier. Main's workflow now does what the unqualified sentences say, so both `<!-- drop the Windows qualifier once #471 lands as proposed -->` comments and the two sentences they marked are gone. Section 6, re-read after lane 3's v1.1.0 triage (#473, 16:03:48Z, on Brett Heap's approval at 14:31:28Z): - 74 lines marked `→ #N` or "rides with #N" on their parent now end with that number, as in (#524). For each parent, the set of numbers cited equals the set its marks name. One sentence in the section's opening says what the number is. - Dropped, each closed on its parent: `--project ATLAS` and a fork's `origin` (#457, ruled behaviour); the scaffold's three working trees (#459, fixed by #505 and kept documented); and the `shape currency` row's uncommitted-copy line (#460, obsolete since #402 and #512). Also #408, closed as fixed by #494. - Added, under #457: the symlinked root `.gitattributes`, the nested one (#530), and a root adopted before #511 (lane 4, 2026-10-12T23:25Z); then #511's three posted at 16:09:31Z: a copy written through a symlinked directory, a CRLF `.gitattributes` under a rule other than `text=auto`, and a `filter=` or `working-tree-encoding=` rule. Its fourth, a test helper that feeds text on stdin, is left out: no person meets it. These rest on lane 4's measurement (V511c at 1ce4f45, whose tree is main's); `eol_line` writing only `text eol=lf` and the symlink skip were read in code. - Reworded: - AT-scaffold N9 (#458), with lane 4's 16:10:03Z note. `cmd_apply` always stages the pin and the manifest (`update-shape.py`:3280-3281), and `_land` prints them all (:3224). - The annotated-tag line (#459). It is now ruled that `bump --to` peel it (#541), so "One rule is still to be chosen" is gone. - The Windows symlink line (#457), narrowed to its parent's own words. #511's end-to-end test of a source that tracks a symlinked `.gitattributes` under `core.symlinks=false` carries only `needs_filter_repo` (tests/test_adopt_ignored_shape_files.py:862-863), so it now runs on Windows. - Left out, new: the three CI lines new on #461 (the merge queue and two CI costs). They are this repository's CI, not a limit of the shape. - Lane 1 has posted no #504 batch, so D13 stays out. Section 7: v1.0.0 is 46 closed and #472 open. #181 is still outside the milestone. No claim is made about an unlanded pull request (#518, #551), and nothing is said about a tag or a date. 7,628 words before, 7,906 after. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…5 cites #518, section 6 drops #181's line, which #518 closed, adds the three limits lane 4's reviews posted on #458 and #460 after the triage and extends two #544 lines with what #461's body now says, and section 7 no longer says #181 is outside the milestone (#472) Refresh round 3c, the last before the v1.0.0 tag. Main is b8b7062 (#518, merged 2026-10-14T18:50:03Z, closing #181), the only merge since round 3b's 5ed3e5d. All 227 merge commits are ancestors of it. The notes describe nothing on an unlanded pull request. Section 5: one adopt-project.py bullet, "Keywords as typed", written from #518's squash message with its body beside it. Read in main's code: adopt-project.py `_PLAIN_RE` (:580), `loader_reading` (:583), `_reads_back_as_text` (:603), `y` (:625-637), `MANIFEST_MISREAD_REMEDIATION` (:2216), `_misread_findings` (:2587, `plan-manifest-misread`), `_refuse_misread_values` (:2939, `adopt-manifest-misread`), `_entry_findings`' return (:5354), `_refuse_what_check_finds` (:7576, called :8206); scripts/shape_materialize.py `leg_path_problem` (:3492, a null leg path is "not text"); templates/assembly-root/project.yaml quotes each placeholder the gate asks about (:31, :42, :46, :47, :62, :67, :159, :163). Run offline, with a stub `gh` that was never called, at b8b7062 and at 5ed3e5d: `--spec-path null` mounts the leg at `null/` (at 5ed3e5d, at `spec`), `--id null` writes `id: "null"` (`"northwind"`), `--tracking-branch true` on a source with that branch puts both legs on `true` (refused `plan-tracking-branch-missing`), a hand-edited `spec_path: null` is `plan-bad-leg-path` (`plan ok`), and the default flags adopt as before. The gate's "names none today" rests on the templates' quotes and on lane 1's fuzz (286 values, 0 rows), not on a run. Section 6: #181's line is dropped (a source with files named `null` and `false` now plans them quoted, asks the ordinary `plan-unresolved` question, and adopts once it is answered; probed). #457 has no new comment and an unchanged body since 16:09:31Z. L1-J1, J2, J3, K1 and K8 hold on main (`check` halves probed; K8 read at adopt-project.py:5861), K3 (`id: 007` written as `"7"`) and K7 (two findings for a tab in a pin's product) probed again. K4 and K6 stay out: nothing a person meets. Added from lane 4's 2026-10-14T18:32Z posts, which are untriaged and so carry no number: on #458 a quoted or spaced `files:` or `shape:` key (read at update-shape.py:1378 and :1502, and run in process for the pin; `check`'s exit and the manifest half rest on R558's measurement) and `bump-leg.py --dry-run` over a pin its validators refuse (read at bump-leg.py:1475 against :1388-1396; the tab-line example rests on R561); on #460 an unparsable standard policy file (shape-doctor.py:1723 read; the exit 2 rests on R557). Left out: #458's two R556 lines (a wording imprecision; and a refusal that is by design on main, bootstrap.py:275), #459's two R557 lines (each repeats a line the notes already hold, #443's and the `UnicodeEncodeError` one), and #461's optional hygiene-test line. #461's other two new lines extend the two #544 lines: four `git submodule update --init` runs in two test files and test_adopt_plan_yaml_quoting.py:2036 (read), and tests/test_cli_reference.py's `--help` children (render-cli-reference.py's `child_env` sets no PYTHONDONTWRITEBYTECODE; the `.pyc` itself rests on #554's review). Section 7: the sentence about #181 is gone. The v1.0.0 milestone stands at 1 open (#472) and 46 closed. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…e `execute` has just pushed the split into, which #518 added, is a `cloned_just_pushed`, so no plain-path clone of a just-pushed remote is left that main brought in (#544) What: after `execute --local-remote-dir` the test cloned the source at the split branch by its plain path with `git -c protocol.file.allow=always clone --recurse-submodules -b`. It is now `cloned_just_pushed(source, tmp_path / "clone", SPLIT_BRANCH)`, the same options: recursive, `-b`, the legs read through the `file://` rewrite (they sit in `tmp_path / "remotes"`, beside the source). `FILE_PROTOCOL` is no longer imported there; nothing else in the file used it. Why: the landing condition of #554, which said to move #518's clone at the pre-landing merge of main. A re-grep of every `"clone"` call in tests/ finds no other new plain-path clone in what the merge brought in (test_adopt_leg_paths.py, test_adopt_manifest_values.py, test_template_text_placeholders.py, test_adopt_plan_yaml_quoting.py). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
….0 contains and promises, how to pin it by commit, what changed on the way grouped by the five tracking areas, and every known limit as the input that triggers it and what the person sees (#472) (#473) #472 asks for one file that says what v1.0.0 covers and lists its known limits. `docs/release-notes/v1.0.0.md` is that file. Its eight sections are: 1. what openRepoShape is, and what 1.0 promises: every procedure in AGENTS.md works end to end for ordinary repositories on Linux, macOS and Windows, with park and resume under WSL2 on Windows; 2. what 1.0 contains, one line per procedure; 3. requirements and platforms; 4. how to pin 1.0.0, naming its commit rather than its tag; 5. what changed on the way: all 227 merged pull requests, #1 to #518, grouped by theme under the five tracking areas (#457 to #461); 6. the known limits: 43 open issues and 124 checklist lines from those five parents. Each line names the input that triggers the limit and what the person sees, and ends with the `v1.1.0` issue that schedules it, where there is one; 7. "Before tagging": the `v1.0.0` milestone, 1 open (#472, which this closes) and 46 closed; 8. links to the merged pull requests and the milestone. The notes describe main at `b8b70627`. The last merge that section 5 covers is #518, at 2026-10-14T18:50:03Z. The first draft was `cbe44fe`. Six later rounds brought it up to main as pull requests landed, the last one after #518. The file is new and nothing else changes. README.md stays at 1422 lines and AGENTS.md at 490. No rule in `tests/test_repo_hygiene.py` asks for a README Layout or handbook line for a file under `docs/`. Two rules read every tracked `.md`: the host-absolute-path scan and the "<word> naming families" count. They find nothing in this file. The hygiene tests gave 50 passed on the branch, and main's two patterns, applied to the file, match nothing. Section 7 and the `<release-commit>` placeholders in section 4 are for the tag. The GitHub release body takes this text with `<release-commit>` replaced by this commit's full sha and section 7 removed. The file keeps both, because it cannot name the commit that adds it. Closes #472 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>



Lane: openreposhape-1 (openRepoShape-1)
Claim: #181 (comment)
What
One rule, applied in two places: a value is never written where the project's own YAML loader (
scripts/repo_shape.py) will read it back as something else. The loader decides each case. Nothing here keeps a list of words.The plan writer (76a6153).
y()writes a string bare only if the plan's loader gives the same string back. It asks in both places the writer puts a string: afterkey:, and as a-item (- a:is a one-key mapping,k: a:is not). A line the loader refuses counts as not read back. Anything else is quoted with #199'srepo_shape.double_quoted.null,Null,NULL,true,True,TRUE,false,False,FALSE.yes,no,on,off,.inf,.nan. This loader reads them as text. PyYAML would read them as booleans and floats; the project's own loader decides, as it does for the manifest'sneutral_product_pins:entries since adopt-project.py:executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514 (Tracking: adopt-project.py, converting an existing repository in place #457 (comment)).--pin <name>is accepted byplan,checkandexecute("adoption verified"), but writes only the name intoneutral_product_pins:and nocontracts/<name>-pin.yaml, so the adopted root's ownvalidate-pins.py(and itsvalidate.yml) refuses withneutral-pin-missingfrom the first run #273 each is a mapping, and_pin_lineswrites its product, repository and digest source throughy, so a productnullis writtenproduct: "null".--pin openGlass@<commit> --pin opensoft/openCode@<commit> --pin-source <clone> --allow-empty-leg code; and--spec-path legs/spec --code-path legs/code --tracking-branch develop. Compared: the plan's bytes,plan's andcheck's output,execute --local-remote-dir's output and every file of the split branch (32, 34 and 32), with temp paths and shas normalized. A stubghwas never called.The gate on what
executewrites (b26841b to 94242a2, and 180902f).executeputs the plan's values into the project's own YAML files. Each command refuses a value the project's loader would read back as something else from the lineexecutewrites:plan:REFUSED adopt-manifest-misread, exit 2, before the plan is written;check:FINDING plan-manifest-misread, exit 1, with the remediation printed once after the findings;execute: refused through adopt-project.pyexecuterefuses onlyplan-unresolvedandplan-stale, so a plancheckrejects as uncovered or with a bad leg creates and pushes both legs before it fails verification #168's_refuse_what_check_finds, before any leg repository exists.What it finds on main now: nothing, on the shipped templates. Main's 47b1e06 (#259), merged here in 5c04ca2, writes
id,elected_on,visibility,tracking_branch, both legpaths and the leg pins'submodule_pathbetween double quotes. Thererenderspells a value withdouble_quoted, and the loader reads it back as typed. Main's e6fbd74 (#514), merged here in f54dce7, writes an item of the manifest'sneutral_product_pins:list between double quotes where the project's loader would misread it bare, and 89c6a6c (#516), merged in 8ce572b, quotes the neutral pin file'sproduct:. The gate asks the lineexecutewrites, so on main's templates it finds no value misread: the class fuzz under Tests names none. Before those merges it refused--tracking-branch true,nulland007, and then--pin null@<commit>,True@<commit>and007@<commit>; now each is planned, checked and written as given (READ_BACK, under Tests). On a line written bare, as the templates were before #259 and the pins' list before #514, it says:checksays the same of "the product of a pin in pins". The remediation saysexecutewrites these values "into the project's own YAML files as they stand", not "intoproject.yamland leg pins": a pin's product goes into the manifest and the product's own pin file, not a leg pin.Why the gate stays whole (the lane's ruling in round K4):
executewrites (G1 (A)). Where every line reads back, it says nothing: that is the rule working. Main keeps scaffold-project.py and adopt-project.py ask the project's YAML reader and validate-manifest.py about the manifest's values before anything exists, so--id null,--tracking-branch trueand a blank--elected-byare refused (#258, #261) #314's scaffold twin after templates/assembly-root/project.yaml and the two leg pins write eight text placeholders bare (id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259 on the same grounds. Itstests/test_scaffold_manifest_values.pysays the question "is still asked", and that a template line written bare "is refused again, with no edit to the rule"._template_table, the tableexecutewrites the manifest from (180902f)._pin_fieldbuilds the table with_manifest_valuesfrom the plan with that one pin;execute's_template_valueswrites the project's files from a table_template_tablebuilds the same way.renderputs the item into the template's line (_written)._pin_item, the copy of how an item is spelled that_template_tableand the gate shared, is gone, and_template_table'sNEUTRAL_PRODUCT_PINSline was main's text again. So lane 2's adopt-project.py:executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514, which quotes those items, silenced the gate on pins with no edit to the rule when it was merged here (f54dce7), and 14 of this branch's tests flipped (under "Composes with adopt-project.py:executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514"). 9efe95a made adopt-project.py:executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514's own docstring true of this branch'sy(). Since then this branch's ownadopt-project.pychanges are comments and docstrings that still said a pin's product is written bare, the unusedMANIFEST_YAMLremoved (6c9e585), and the resolutions of its conflicted merges with main (a22b7f9, b602c16; under Merges).planasks the plan it is about to write, built once:_refuse_unholdable_valuesreturns it (_flag_plan), and_refuse_misread_valuesand_flag_fieldstake it. Main'stest_plan_asks_what_check_asks_of_the_same_keysholdsplanto one_flag_plan.bare_template, a copy of the templates with templates/assembly-root/project.yaml and the two leg pins write eight text placeholders bare (id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259's lines bare again, andbare_pins, the pins' list written 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, in this process (under Tests). They are the derived id (DERIVED_ID,FLAG_DEFAULTS), a set id and a branch read as not text, git's question about a branch and whether the source is asked about one, what the source answers (_pushed_in_every_mode) and whatplan,checkandexecutedo with that answer, the validator asked before this gate atplan, the default branch's name, the escapesUNESCAPEDnames, the validator asked about no manifest and a manifest its reader refuses, the valuesexecutenever writes, and each refusal itself: atplan, atcheckand atexecute, a pin's product named as such, and no pin named beside a adopt-project.py--pin <name>is accepted byplan,checkandexecute("adoption verified"), but writes only the name intoneutral_product_pins:and nocontracts/<name>-pin.yaml, so the adopted root's ownvalidate-pins.py(and itsvalidate.yml) refuses withneutral-pin-missingfrom the first run #273 finding.How the gate decides.
manifest_misreadsbuilds each lineexecutewrites for a value: every line of a templated YAML file that carries the value's placeholder (the.yamlfiles inshape_materialize.TEMPLATED), handed with the value to the materializer's ownrender(d38e143). No copy ofrender's rules is kept here.loader_reading, the same questiony()asks, to read that line back.loader_readingcallsrepo_shape.parse_yaml, from the same fileexecutecopies into every project (COPIED_FROM_SHAPE).renderspells it withdouble_quoted(repo_shape.py: a quote opens a quoted scalar only where one begins and must close there, sok: [it's, a]reads two items,k: it's fine # notedrops its comment, and an unclosed"abcor a mid-key[is refused, not misread (#249, #250) #284) and puts it in once (adopt-project.py and scaffold-project.py substitute the template placeholders one after another, so a free-text value holding another placeholder's token is expanded (--elected-by '{{SPEC_PATH}}'is recorded aselected_by: "spec") or, after the legs are pushed, refused astemplate-unsubstitutedwith "a defect in the substitution table of the tool you ran" #260). The gate follows the loader with no edit of its own: adopt-project.py plan writes a plan its own loader cannot parse when a source name holds a line break (the macOSIcon\rfile), socheckrefuses the whole plan asyaml-unparsablewith no entry to edit #199's escapes, scripts/repo_shape.py reads? a: bin a flow mapping as the literal key? a, where the block reader refuses explicit keys, and refuses{a: }and{a:}asnot a flow collection: ''instead of reading{'a': None}#252's flow mappings, scripts/repo_shape.py: the double-quoted reader that #247 adds (fix/adopt-plan-linebreak-names, not yet on main) resolves\x00inside a block-scalar KEY, and_parse_mappingsplits the NUL-joined sentinel record, so"a\x00b": |reads as the keyawith the bodyb<NUL>textwhere main reads the literal keya\x00b#248's block scalars and repo_shape.py: a quote opens a quoted scalar only where one begins and must close there, sok: [it's, a]reads two items,k: it's fine # notedrops its comment, and an unclosed"abcor a mid-key[is refused, not misread (#249, #250) #284's quote boundaries.planlowercased--project, or when the plan sets none (noid:, orid: null).The project's own validator is asked first (f38d19a; rv181f's G1 and G2). Main's #314 asks the project's own
validate-manifest.pyabout the same values:plan-manifest-invalidatcheckandexecute,adopt-manifest-invalidatplan. This gate's remediation says thatexecutewrites the values it names as they stand, and that the run would still endadoption verified. So a value is this gate's only where all three hold:executegets as far as writing it, in every mode it runs in (_execute_gets_to_write, below).plan,_refuse_unholdable_valuesruns inside_checked_plan_inputs, before this gate. Atcheck,_misread_findingsasks_validator_judgedfirst: it renders the manifest the plan would make and asksmanifest_invalid's own question (shape_materialize.manifest_judged), and the gate leaves to it every value one of its findings is about (FINDING_PLACEHOLDER).? xwhile the pins' list was written bare; since adopt-project.py:executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514 no plan value was found to make such a manifest of the shipped templates, probed over every key the manifest is rendered from), or it makes a finding about no one value (a leg path../x), this gate names none of the plan's values, and the one finding is the validator's, as on main.checknever reads a plan'selected_on:when the plan has noreference:, soplan okis followed by anexecutethat creates both leg repositories and only then refuseselection-date-malformed, naming--elected-on, a flag neither command takes #294's since 965420b:checkaskselection_date, one it refuses iselection-date-malformed,_manifest_valuesrenders no manifest, and neither the validator nor this gate is asked.A value
executenever gets as far as writing, in every mode, is not asked about (_execute_gets_to_write). A sentence about it would say thatexecutewrites it and that the run would still endadoption verified, and neither would be true:tracking_branchthatchecked_valuerefuses:executerefuses it asunsafe-valuebefore any leg exists (e391b18, rv181f's F2);tracking_branchthe loader misreads that git will not create, by scaffold-project.py holds--tracking-branchonly tochecked_value's alphabet, so a name git refuses as a branch (~,a..b,x.lock, a trailing/) passes--dry-runand, against GitHub, dies at the firstgit init -baftergh repo createhas made all three repositories #283'sbranch_problem:~(6a3a6b3, rv181f's H1). Git is asked only about a branch the loader misreads;tracking_branchthe loader misreads thatexecutedoes not push the legs to in every mode (69a743e, 94242a2): adopt-project.py: a source whose default branch is notmainis adopted with both legs pushed, then its pull request is refused (--base main), and the printed way out repeats--base main#479'splan-tracking-branch-missing, or one only the source'soriginhas. The source is asked only about a branch the loader misreads;visibilitythat is notprivate,publicorinternal, text or not: in gh mode,execute's default,executehands it togh repo createas--<visibility>before anything is written (e391b18, rv181f's F2b);pins:has a finding of adopt-project.py--pin <name>is accepted byplan,checkandexecute("adoption verified"), but writes only the name intoneutral_product_pins:and nocontracts/<name>-pin.yaml, so the adopted root's ownvalidate-pins.py(and itsvalidate.yml) refuses withneutral-pin-missingfrom the first run #273's (plan-pin-unrecorded,plan-pin-malformed,plan-pin-collides);--elected-by,--referenceor the--sourcedirectory passesplanandcheck, andexecutepushes both legs before it dies withUnicodeEncodeErrorinwrite_lf#255'splan-unwritable-valuenames it, andexecuterefuses the plan before any leg exists (dae165d).Since #259 the loader reads every tracking branch and visibility back from its quoted line, and since #514 every pin's product from the list, so these clauses change what the gate says only of a line written bare.
How each refusal reads (5aee63a, the NITs of review181).
executewould write and what the loader reads it as, and says "choose another value".reprspells it. A text reading names each character a terminal shows as something else: "reads it back as the textCORP, then a tab, thenom".strof it ("tracking_branch was read out of the plan as the number 7"). A pin is "the product of a pin in pins", and atplan"the product of a --pin". A tracking branchplantook from the source's default branch (adopt-project.py: a source whose default branch is notmainis adopted with both legs pushed, then its pull request is refused (--base main), and the printed way out repeats--base main#479) is "--tracking-branch (not given, so the source's default branch)".An explicit null leg path in a plan. This is
spec_path: null,~, or the key with nothing after it, which is what main'splan --spec-path nullwrites.checkandexecuteused to read it as the defaultspec. Now it isplan-bad-leg-path: legs.spec_path is not text: its value was read as no value at all, and a leg path is text: write the path you mean, or remove the key to mount the leg at its role's own name.check's mount line no longer printsNone/orTrue/.spec_path: true. Quoted, the plan reads that spelling back as text, and since templates/assembly-root/project.yaml and the two leg pins write eight text placeholders bare (id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259executemounts the leg at a directory of that very name (true/,null/), which is the path meant only if it was meant. So it asks for the path meant, or for no key at all. Since scaffold-project.py: a leg's mount path must be canonical or it is refused before anything is created, so--spec-path spec/no longer scaffolds a project whose recursive clone fails once the work directory is gone (#179) #201 that sentence lives inscripts/shape_materialize.py; the scaffold cannot reach it, because its flags are always text.Why (repro on main 5ed3e5d)
Each case runs
plan,check,execute --local-remote-dir,git clone --recurse-submodules, then the clone's ownscripts/bootstrap.py, on the suite's standard source. A branch given to--tracking-branchis one the source has (#479). A stubghstood first on PATH; its only calls came from a bootstrapped root's ownvalidate-pins.py, asking for the digest of a pin it has no checkout of. Since #259 the templates quote whatexecutewrites, so on main--spec-path 2026,1.5and~,--tracking-branch 007and--id 2026adopt and bootstrap as typed, and since #514 and #516 so does--pin 007@<commit>. What is left on main is the plan: its writer putsnull,trueandTrueinto the plan bare, and the plan's own loader reads them back as no value or a boolean. On main 5ed3e5d the driver's output is 89c6a6c's line for line, once temp paths and shas are normalized.adoption verifiedand bootstrap ok, but with another value, and nothing says so:--spec-path null(alsoNull,NULL): the plan saysspec_path: null,checksaysplan ok, and the leg is mounted atspec.--code-path nullmounts atcode.--tracking-branch null: both legs are onmain, and so is the manifest'stracking_branch.--id null: the project's id isnorthwind.--project Null: the plan'slegs.assemblyandidare read as no value, and the manifest saysid: none.Refused at
check, about a value nobody typed:--tracking-branch true:plan-tracking-branch-missing, "tracking_branchisTrue, which is no branch of the source (its branches:main,true; ...)".--spec-path trueorFalse:plan-bad-leg-path, "... read as true or false, and a path is written as text, so quote it".--id trueand--project True:plan-bad-id, "id:is 'True' and must match^[a-z0-9][a-z0-9-]*$".--pin null@<commit>:plan-pin-malformed, "pins:entry 1 records noproduct:", from a plan that saysproduct: null;--pin True@<commit>: "recordsproduct: True, which is not text".--elected-by null:plan-manifest-invalid, "elected_by is ''", from a plan that sayselected_by: null.On this branch (7d6e13c, the same driver, whose output is 9efe95a's line for line, normalized), every value above adopts and bootstraps as typed.
--spec-path null,Null,NULL,trueandFalsemount the leg atnull/,Null/,NULL/,true/andFalse/, and--code-path nullatnull/.--tracking-branch trueandnullput both legs ontrueandnull, each with itsorigin/upstream.--id nulland--project Nullmake the idnull,--id trueand--project Truemake ittrue, and--elected-by nulliselected_by: "null".--pin null@<commit>andTrue@<commit>adopt with the manifest's- "null"and- "True"and the pin files'product: "null"andproduct: "True".--spec-path legs/null --tracking-branch 2026-q4 --id yes --elected-by Nulladopts and bootstraps too, each leg on2026-q4; on main the same run stops atcheckwithelected_by is ''.Tests
tests/test_adopt_plan_yaml_quoting.pyis new, with 524 tests, all through the realrepo_shapeloader:nulland leavesopenGlassbare.READ_BACK, 20 rows).--tracking-branchtrue,nulland007, each on a source that has it (adopt-project.py: a source whose default branch is notmainis adopted with both legs pushed, then its pull request is refused (--base main), and the printed way out repeats--base main#479); the eight--spec-pathvaluesnull,Null,NULL,~,true,False,2026and1.5, and--code-path null;--id trueandnull;--project TrueandNull; a source whose default branch istrue; and, since adopt-project.py:executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514,--pin null@<commit>,True@and007@, each digest read from a stand-in clone with--pin-source(adopt-project.py--pin <name>is accepted byplan,checkandexecute("adoption verified"), but writes only the name intoneutral_product_pins:and nocontracts/<name>-pin.yaml, so the adopted root's ownvalidate-pins.py(and itsvalidate.yml) refuses withneutral-pin-missingfrom the first run #273).test_a_value_the_template_quotes_is_planned_and_checked_as_givenasks each throughplanandcheck, andtest_execute_writes_a_value_the_template_quotes_as_giventhroughexecute --local-remote-dir: the plan, the split'sproject.yaml, a leg pin'ssubmodule_path, a pin's owncontracts/<product>-pin.yaml(since scaffold-project.py:--pin 007@<commit>exits 0 and scaffolds a root whose ownvalidate-pins.pyrefusesentry 7, becauseneutral_product_pins:entries (and the pin file'sproduct:) are written bare #516) and the legs' branch read the value back as given, with no finding, and the manifest's list holds- "null",- "True"and- "007".planrefused each of these before the merges, as this gate's or the validator's.REFUSED, six rows, in this process). The branchestrue,nulland007, on a source that has each, overbare_template; the pin productsnull,Trueand007overbare_pins. Atplanas--tracking-branchor--pin <product>@<40 hex>(REFUSED adopt-manifest-misread, no plan written), atcheckas a plan carrying it (one finding, this gate's, with its remediation once), and atexecutebefore any leg exists (needs_filter_repo).bare_templateandbare_pins.bare_templatecopies the templates the gate reads (ASSEMBLY_TEMPLATE) with templates/assembly-root/project.yaml and the two leg pins write eight text placeholders bare (id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259's six placeholders bare again and puts the copy in their place;bare_pinsmakes_template_tablewrite the pins' list as it did before adopt-project.py:executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514. Each first asserts that main's quotes are where it expects them. BesidesREFUSED, 47 tests in 16 functions ask the branches only a bare line reaches, directly or throughplan,checkandexecutein this process: the idplanderived from--project, and the id derived fromlegs.assemblyfour ways; a set id and a branch read as not text, named by what was read (READ_AS_NOT_TEXT:007,TRUE,1.50andid: 007); the valuesexecutenever writes (NEVER_WRITTEN, 17); git asked only about a misread branch; the source asked only about a misread branch; adopt-project.py: a source whose default branch is notmainis adopted with both legs pushed, then its pull request is refused (--base main), and the printed way out repeats--base main#479's three branchesexecutedoes not push to in every mode;planasking the validator before this gate; the default branch named by where it came from; the validator asked about no manifest, and a manifest its reader refuses; the sevenUNESCAPEDvalues shown as typed and as read; a pin beside a adopt-project.py--pin <name>is accepted byplan,checkandexecute("adoption verified"), but writes only the name intoneutral_product_pins:and nocontracts/<name>-pin.yaml, so the adopted root's ownvalidate-pins.py(and itsvalidate.yml) refuses withneutral-pin-missingfrom the first run #273 finding, and a pin ending in whitespace; a loader taught a new keyword; and scaffold-project.py: a leg path the project's own YAML reader would read back as another type (null,~,true,False,2026,1.5) is refused before anything is created, under--dry-runalike, through the one definition adopt-project.py asks, so--spec-path 2026no longer creates three repositories whose first bootstrap failsmanifest-leg-path, andadopt-project.py executeno longer saysadoption verifiedbefore that bootstrap fails (#225) #266's leg-path rule (below).? xrows adopt-project.py:executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514's merge removed):test_a_branch_execute_does_not_push_in_every_mode_is_never_misread(the source lackstrue, its fetchedoriginlacks it, or onlyoriginhas it:planandcheckin this process overbare_templategive adopt-project.py: a source whose default branch is notmainis adopted with both legs pushed, then its pull request is refused (--base main), and the printed way out repeats--base main#479's note and finding alone, orplan ok, and never a misread);test_the_source_says_whether_execute_pushes_the_legs_to_a_branch(_pushed_in_every_modefor four sources: the source lacks the branch,originlacks it, onlyoriginhas it, both have it);test_plan_asks_the_validator_before_the_misread_rule(--id true, with the validator given the same bare copy:adopt-manifest-invalidalone, no plan written); andtest_a_manifest_its_reader_refuses_is_the_validators_alone(G2's pin? xoverbare_pins).with_originputs the source'soriginundertestorg/Northwind, the repositoryplanis given (rv181g's M1). Since adopt-project.py:--organd--projectmust name the repository the source names, compared without case, soplanrefuses--project BillingServiceforacme/billing-serviceasadopt-not-the-sourceinstead of writing a plan whoseexecuteacts onacme/BillingService,checkfinds andexecuterefuses a plan edited to name another asplan-not-the-source, and a source whose own name the naming policy refuses is told to rename it first, never to retype--project(#499) #513 (cf118ff),planreads a pathorigin's last two directories as the source's repository and refuses a plan for another, so withoriginin a directory namedseedfive tests were refused asadopt-not-the-sourcebefore this gate was asked; cf118ff made the same change totests/test_adopt_tracking_branch.py. Seven compositeasserts are split into plain ones with the same meaning (SonarCloud's S9073), and onepytest.raisesblock now holds only the call that raises (S5778).executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514's and templates/assembly-root/project.yaml and the two leg pins write eight text placeholders bare (id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259's merges: the threeREFUSEDtests,READ_BACK's pin rows, the template-line, table, agreement and keyword-learning tests, the two adopt-project.py--pin <name>is accepted byplan,checkandexecute("adoption verified"), but writes only the name intoneutral_product_pins:and nocontracts/<name>-pin.yaml, so the adopted root's ownvalidate-pins.py(and itsvalidate.yml) refuses withneutral-pin-missingfrom the first run #273 pin tests, four tests of a derived id, a value read as not text and a branch taken from the source, andmanifest_judged's cases. Most now ask a line written bare in this process (bare_template,bare_pins) or read the quoted one back. Each change is listed test by test in this description's edit history (the body at 9efe95a, with which adopt-project.py:y()quotes what the plan's loader would read as a keyword, so--spec-path null,--id nulland--tracking-branch trueadopt as typed, andplan,checkandexecuterefuse, before anything exists, a value the project's own loader would read back as something else from the lineexecutewrites #518 was opened).test_plan_refuses_a_value_the_validator_refuses_in_its_words_alone,test_check_leaves_a_value_the_validator_refuses_to_itandtest_execute_refuses_a_value_the_validator_refuses_as_the_validators, whose values areREAD_BACK's--spec-path,--code-pathand--idrows now. The rule they held, the validator asked first and a value it refuses its alone (rv181f's G1), is held atcheckby the never-writes visibility rows together with_execute_gets_to_write(either reverted alone fails none, both together 15), and atplanbytest_plan_asks_the_validator_before_the_misread_rule. Alsotest_plan_leaves_an_id_it_derived_from_the_project_to_the_validator(READ_BACK's--projectrows) andtest_the_misread_rule_names_a_set_id_by_what_was_read(test_the_misread_rule_names_a_plan_value_that_is_not_text_by_what_was_read[id='007']).JUDGED_ONCElosesid: "007",tracking_branch: "007"andlegs.spec_path: "null"(main'sTEXT_THE_TEMPLATE_NOW_HOLDS, andREAD_BACK), and at adopt-project.py:executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514's merge its pins"null"(test_check_reports_a_plan_carrying_one's pin rows) and? x;JUDGED_BY_THE_VALIDATORloses? xtoo (both totest_a_manifest_its_reader_refuses_is_the_validators_alone).NOT_TEXT_INVALIDlosesid: 007(READ_AS_NOT_TEXT).executedoes not write it in every mode:checknames none ofNEVER_WRITTENmisread and prints no misread remediation:tracking_branch: '"CORP\tom"',tracking_branch: "~",visibility: "true", the eleven visibility texts the loader reads as no value ("",'',"null","Null","NULL","~","#","#x"," "," ",|-) and three choices in their own quotes.plan --tracking-branch ~writes the plan, as on main. A date, a visibility and two pins' products no file can hold are each adopt-project.py: since #247 a byte that is not UTF-8 in--elected-by,--referenceor the--sourcedirectory passesplanandcheck, andexecutepushes both legs before it dies withUnicodeEncodeErrorinwrite_lf#255'splan-unwritable-value(a pin beside adopt-project.py--pin <name>is accepted byplan,checkandexecute("adoption verified"), but writes only the name intoneutral_product_pins:and nocontracts/<name>-pin.yaml, so the adopted root's ownvalidate-pins.py(and itsvalidate.yml) refuses withneutral-pin-missingfrom the first run #273'splan-pin-malformed), with no misread finding.JUDGED_ONCE, 5): the validator's, each: a blank elector, andvisibilityset tosecret,"true",trueor[secret].manifest_judgedis tested in four cases here and in the G2 test, and the sixLATE_DATESare each adopt-project.py:checknever reads a plan'selected_on:when the plan has noreference:, soplan okis followed by anexecutethat creates both leg repositories and only then refuseselection-date-malformed, naming--elected-on, a flag neither command takes #294'selection-date-malformedalone. Withgit-filter-repo,executerefusesvisibility: "true"under the validator's code before any leg exists. Main'stests/test_adopt_manifest_values.pyasserts the order:_misread_findingsis[]where the validator judges a value.k: [it's, a]reads two items,k: it's fine # notedrops its comment, and an unclosed"abcor a mid-key[is refused, not misread (#249, #250) #284 and put in once since adopt-project.py and scaffold-project.py substitute the template placeholders one after another, so a free-text value holding another placeholder's token is expanded (--elected-by '{{SPEC_PATH}}'is recorded aselected_by: "spec") or, after the legs are pushed, refused astemplate-unsubstitutedwith "a defect in the substitution table of the tool you ran" #260: ten values between the manifest's quotes (CORP\tom,docs\new.md,Robert "Bob" Smith, a last\, ...) forelected_by:andreference:(20 cases), and five values holding a placeholder's token (10 cases), each asked asrenderwrites it, and each passes. The electorplantakes fromgit config user.name,CORP\tom, is written"CORP\\tom"and passescheck.mainis adopted with both legs pushed, then its pull request is refused (--base main), and the printed way out repeats--base main#479 and adopt-project.py--pin <name>is accepted byplan,checkandexecute("adoption verified"), but writes only the name intoneutral_product_pins:and nocontracts/<name>-pin.yaml, so the adopted root's ownvalidate-pins.py(and itsvalidate.yml) refuses withneutral-pin-missingfrom the first run #273 (69a743e, 94242a2): on the shipped templates,planandcheckname a branch the source lacks, or its fetchedoriginlacks, as adopt-project.py: a source whose default branch is notmainis adopted with both legs pushed, then its pull request is refused (--base main), and the printed way out repeats--base main#479's finding alone, and one onlyoriginhas as neither, andexecute --local-remote-dirrefuses that one before any leg exists. Since templates/assembly-root/project.yaml and the two leg pins write eight text placeholders bare (id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259 the branch reads back from its quoted line, so these tests no longer reach_a_branch_execute_writes,_pushed_in_every_modeorunwritten;test_a_branch_execute_does_not_push_in_every_mode_is_never_misread(the same three cases overbare_template) andtest_the_source_says_whether_execute_pushes_the_legs_to_a_branch(four sources) do. A pin beside a adopt-project.py--pin <name>is accepted byplan,checkandexecute("adoption verified"), but writes only the name intoneutral_product_pins:and nocontracts/<name>-pin.yaml, so the adopted root's ownvalidate-pins.py(and itsvalidate.yml) refuses withneutral-pin-missingfrom the first run #273 finding is not named, and a pin ending in whitespace is adopt-project.py--pin <name>is accepted byplan,checkandexecute("adoption verified"), but writes only the name intoneutral_product_pins:and nocontracts/<name>-pin.yaml, so the adopted root's ownvalidate-pins.py(and itsvalidate.yml) refuses withneutral-pin-missingfrom the first run #273'splan-pin-malformedalone, both asked overbare_pinsnow; a product that is not text is adopt-project.py--pin <name>is accepted byplan,checkandexecute("adoption verified"), but writes only the name intoneutral_product_pins:and nocontracts/<name>-pin.yaml, so the adopted root's ownvalidate-pins.py(and itsvalidate.yml) refuses withneutral-pin-missingfrom the first run #273'splan-pin-malformedalone.--spec-path legs/null --tracking-branch 2026-q4 --id yes --elected-by Null, throughexecute, the recursive clone andbootstrap.py, each leg on2026-q4withorigin/2026-q4as its upstream (needs_filter_repo).executewrites the pins' list from, quoted and written bare, and agrees with the files the materializer'srenderwrites whole on every candidate, for the tracking branch, the elector, the reference, the spec leg path and a pin.null,~and nothing; the doctor's placement plan quotes a path the same way; each test that runsexecuteruns it on a copy of the source of its own (c87c45f).The composition fuzz, every value class (7d6e13c against main 5ed3e5d). 286 values (rv181f's 244 candidates and 42 more) in eleven classes: the id given and derived, the tracking branch given and taken from the source's default branch, the elector, the election date, the reference, the visibility, both leg paths and a pin's product. Each runs through
plan, given by its flag, and throughcheckof a hand-edited plan carrying it as the head'sywrites it: 3146 rows, 2933 run. The 213 skipped are 210 tracking-branch values git orchecked_valuewill not take, and three a source could not be made for.plan, 0 atcheck. No row has this gate's finding beside another (0 double), so no clause of it can be false (0), and no row the tool compares hasplanandcheckdisagreeing (0; it leaves the visibility and derived-id classes out of that comparison). The stubghwas never called.--pinparses it, found every clause true). adopt-project.py:executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514 and scaffold-project.py:--pin 007@<commit>exits 0 and scaffolds a root whose ownvalidate-pins.pyrefusesentry 7, becauseneutral_product_pins:entries (and the pin file'sproduct:) are written bare #516 quote those lines now.Other files: main's
tests/test_adopt_manifest_values.pykeeps its own tests, withmisreads_elsewhere(gone since f38d19a) asked as_misread_findings(plan) == [];test_a_value_the_reader_reads_as_another_kind_is_not_this_questionsis main's again, less main's visibility row.tests/test_adopt_leg_paths.py: the not-text sentence has no "quote" and covers None. Main'stests/test_template_text_placeholders.py: a comment that saidplanwritestrueandnullbare, which it does not here; #516's pin-file rows beside it are main's.Revert table, round K5, measured on 9efe95a, and not again at 7d6e13c: the code its rows revert is unchanged since, except where main's merges changed the same functions (
_checked_plan_inputs,_entry_findings,yand, at #511's,cmd_plan; under Merges). Each change was reverted alone in a scratch clone of the head; the quoting file and main'stests/test_adopt_manifest_values.pywere run whole (601 tests), and the seven files touched were restored (sha256-checked). A row marked "with" runs withgit-filter-repoon PATH, so itsexecutetests run; the others run without it, which skips those 44 tests alone. Unreverted, with it: 601 passed. The rows from K1 on are rv181g's, text for text.y()back to main's test (76a6153's change), withREAD_BACK's rows whose value the plan reads as a keyword, atplan/checkand atexecute(30), the bare branchestrueandnulland the bare pinsnullandTrueatcheckand atexecute(8), #479's three shipped tests and theirexecute(4), the three bare-line #479 cases, the accepted end-to-end run, the keyword-learning test, the pin writer test, the keyword-named source file and the doctor's placement plan_template_tablewrites the pins' list bare again, withREAD_BACK's three pins atplan/checkand atexecute, and the keyword-learning, template-line and table testsbare_pinsleaves #514's quoting in placeplanand atcheck, and the G2 test_template_table(180902f)READ_BACK's three pins atplan/check, and the keyword-learning, template-line, table and pin agreement testscheckandexecuteask about no pin, withcheckand atexecuteplanasks about no pinplanneutral_product_pins:and its item as one)plan, atcheckand inREAD_BACK, the keyword-learning, template-line, table and pin agreement tests, the three ordinary-pin writer cases, and main'stest_plan_asks_what_check_asks_of_the_same_keysplanbuilds its plan a second time for the pinstest_plan_asks_what_check_asks_of_the_same_keystest_a_pin_beside_a_273_finding_is_not_named_misread_read_pinsrecordstest_a_pin_ending_in_whitespace_is_273s_finding_aloneplannames a pin's product--pinplanchecknames a pin's producta pin in pinschecktracking_branch:written bare again, withbare_templateerror, because its fixture asserts #259's quotes are in place. The 12:READ_BACK's four branch rows atplan/checkand atexecute(8),checkpassing a branch read as not text (3), and the template-line testid:written bare again, withREAD_BACK's--idand--projectrows twice (8), the four derived idscheckpasses,checkpassingid: 007, and six of main'stests/test_adopt_manifest_values.pycasespath:s and bothsubmodule_path:s), withREAD_BACK's nine leg paths atplan/checkand atexecutebare_templateleaves #259's quotes in placegit-filter-repo: the three bare branches atplanand atcheck(6), the two derived-id tests (5), the not-text rule (4), git's and the source's questions and the default branch's name (3),planasking the validator first, the sevenUNESCAPEDvalues, the keyword-learning test and #266's compositionstrof it againtest_the_misread_rule_names_a_plan_value_that_is_not_text_by_what_was_read, every row--tracking-branchtest_plan_names_a_tracking_branch_it_took_from_the_source_execute_gets_to_writesays yes to every value'"CORP\tom"',"~") and the test of what git is asked. Its visibility rows still pass: the validator, asked first, judges eachtest_a_leg_path_written_bare_is_266s_finding_alonetest_the_validator_says_which_values_it_judges[a-finding-about-no-one-value]test_a_manifest_its_reader_refuses_is_the_validators_aloneplanasks git only about a misread branch, not the sourcetest_a_branch_execute_does_not_push_in_every_mode_is_never_misread, each casecheckandexecutepass nounwrittento this gate_pushed_in_every_modewithout #479's questionorigin-lacks-itof that test and oftest_the_source_says_whether_execute_pushes_the_legs_to_a_branch_pushed_in_every_modewithout the source's own branchesonly-origin-has-itof the same two teststest_the_source_is_asked_only_about_a_branch_the_loader_misreads, andtest_the_misread_rule_names_an_id_plan_derived_by_where_it_came_from, which givesplanno sourceplan, this gate's question asked first, the plan built oncetest_plan_asks_the_validator_before_the_misread_rulecheck, a value the validator judges not left to it_execute_gets_to_write's visibility clause offNEVER_WRITTEN's fifteen visibility rows overbare_template. Each of the two guards keeps those values from this gate on its owntest_no_value_passes_a_validator_that_is_not_askedFLAG_DEFAULTSwithout the idtest_the_misread_rule_names_an_id_plan_derived_by_where_it_came_fromidtest_the_misread_rule_names_an_id_it_derived_from_the_assembly, every rowEarlier revert tables, each measured on the head named, are kept whole in this description's edit history: round K4 (180902f, 580 tests), the fix round (edf9163, with rows again on ed9968f, f911d81 and 94242a2), round K3 (94242a2, 491 tests) and the NIT round (edf9163, 543 tests). A row about a value #259 or #514 quotes describes that head, not this one. Each row failed at least one test except these, named there: rv181g's K1, K2, K3, K3b and V2b at 180902f (B1, fixed in 9efe95a); a boolean spelled with its own literals (S1192 only); and a branch
checked_valuerefuses, asked about (4 with H1's guard reverted too).Counts at 7d6e13c, this head (git 2.43.0, Python 3.12.3, pytest 9.1.1), measured before #511 landed on a scratch merge of 42362de with #511's predicted squash, whose tree, 88c22f0a, is this head's. Clean worktrees, a stub
ghfirst on PATH in both modes, and six sets, each run withgit-filter-repoon PATH and without it, each with exit 0, that commit named in its log and the worktree clean before and after it:test_adopt_plan_yaml_quoting test_adopt_plan test_adopt_leg_paths test_adopt_e2e test_adopt_execute_refusals test_adopt_submodules test_shape_doctor test_cli_reference test_windows_paths test_adopt_leg_path_collisions test_adopt_mount_ignored test_scaffold_leg_paths test_repo_hygiene test_adopt_verify. With it: 1928 passed, 4 skipped (the MedxEHR clone, and three rows oftest_shape_doctorthat print a PowerShell line only on a Windows host). Without it: 1833 passed, 99 skipped (95needs_filter_repo, plus those four). tests.yml and adopt-project.py: the Windows job installs git-filter-repo 2.47.0, so the adoption test files that skip without it run there, and a source that keeps its own root.gitattributesgets one marked block of/<copy> text eol=lflines appended to it in the split, so a Windows clone writes the pinned copies at the root with the bytes the pin records; the verification counts that file as the source's bytes followed by exactly that block, andupdate-shape.py apply --addextends it (#471) #511 changed four of these files and none of their counts.Icon\rfile), socheckrefuses the whole plan asyaml-unparsablewith no entry to edit #199's and scripts/repo_shape.py_split_flowignores a backslash inside a double-quoted flow item, sopins: ["x\",y"]reads back as two itemsx\andy"where the same text as a block sequence item reads as the one itemx",y#213's escapes and flow quotes, the scaffold's collisions and ignored mounts,test_adopt_manifest_valuesandtest_scaffold_manifest_values(scaffold-project.py and adopt-project.py ask the project's YAML reader and validate-manifest.py about the manifest's values before anything exists, so--id null,--tracking-branch trueand a blank--elected-byare refused (#258, #261) #314),test_manifest_validator(repo_shape.py, validate-manifest.py: a bracket in a plain flow scalar is refused and a name holding a lone surrogate is a finding, sopins: [a, b]]is not the pinb]andname: "a\ud83db"no longer killsmake validate(#301, #280) #390),test_adopt_pins(adopt-project.py--pin <name>is accepted byplan,checkandexecute("adoption verified"), but writes only the name intoneutral_product_pins:and nocontracts/<name>-pin.yaml, so the adopted root's ownvalidate-pins.py(and itsvalidate.yml) refuses withneutral-pin-missingfrom the first run #273, adopt-project.py:executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514),test_adopt_tracking_branch(adopt-project.py: a source whose default branch is notmainis adopted with both legs pushed, then its pull request is refused (--base main), and the printed way out repeats--base main#479),test_ruleset_hint_tracking_branch, theupdate-shapefiles and the rest. With it: 3869 passed, 4 skipped (one test about Windows' own default, three about NTFS junctions). Without it: 3686 passed, 187 skipped (183needs_filter_repo). Since 42362de, tests.yml and adopt-project.py: the Windows job installs git-filter-repo 2.47.0, so the adoption test files that skip without it run there, and a source that keeps its own root.gitattributesgets one marked block of/<copy> text eol=lflines appended to it in the split, so a Windows clone writes the pinned copies at the root with the bytes the pin records; the verification counts that file as the source's bytes followed by exactly that block, andupdate-shape.py apply --addextends it (#471) #511 added six tests totest_adopt_ignored_shape_files, each needinggit-filter-repo.id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259, scaffold-project.py: a leg path the project's own YAML reader would read back as another type (null,~,true,False,2026,1.5) is refused before anything is created, under--dry-runalike, through the one definition adopt-project.py asks, so--spec-path 2026no longer creates three repositories whose first bootstrap failsmanifest-leg-path, andadopt-project.py executeno longer saysadoption verifiedbefore that bootstrap fails (#225) #266 and update-shape.py on a holder cut before siblings.py existed: taking the new Makefile without--add scripts/siblings.py(AGENTS.md's default) breaksmake siblings,parkandresume#489, in neither set:test_template_text_placeholders(templates/assembly-root/project.yaml and the two leg pins write eight text placeholders bare (id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259, scaffold-project.py:--pin 007@<commit>exits 0 and scaffolds a root whose ownvalidate-pins.pyrefusesentry 7, becauseneutral_product_pins:entries (and the pin file'sproduct:) are written bare #516),test_leg_path_yaml_scalars(scaffold-project.py: a leg path the project's own YAML reader would read back as another type (null,~,true,False,2026,1.5) is refused before anything is created, under--dry-runalike, through the one definition adopt-project.py asks, so--spec-path 2026no longer creates three repositories whose first bootstrap failsmanifest-leg-path, andadopt-project.py executeno longer saysadoption verifiedbefore that bootstrap fails (#225) #266),test_bootstrap_after_bump,test_bootstrap_spelling,test_family,test_family_add_member_id,test_family_makefile_siblings,test_pin_validator,test_scaffold_e2e,test_setup_project_pyandtest_setup_sh. With it: 963 passed, 8 skipped. Without it: 953 passed, 18 skipped (tenneeds_filter_repo). The eight are scaffold-project.py: a leg path the project's own YAML reader would read back as another type (null,~,true,False,2026,1.5) is refused before anything is created, under--dry-runalike, through the one definition adopt-project.py asks, so--spec-path 2026no longer creates three repositories whose first bootstrap failsmanifest-leg-path, andadopt-project.py executeno longer saysadoption verifiedbefore that bootstrap fails (#225) #266's rows, which skip because "the templates read" those values back since templates/assembly-root/project.yaml and the two leg pins write eight text placeholders bare (id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259, or have no value left to refuse.test_doctor_legs_members_next,test_doctor_next_is_a_command,test_doctor_validator_rows,test_family_init_landing,test_family_printed_quoting,test_family_tracking_branch,test_scaffold_pin_and_reuse(scaffold-project.py:--pin 007@<commit>exits 0 and scaffolds a root whose ownvalidate-pins.pyrefusesentry 7, becauseneutral_product_pins:entries (and the pin file'sproduct:) are written bare #516),test_scaffold_reuse_seeded_legs,test_update_shape_addandtest_update_shape_rewrite_symlink. 364 passed, with it and without it; tests.yml and adopt-project.py: the Windows job installs git-filter-repo 2.47.0, so the adoption test files that skip without it run there, and a source that keeps its own root.gitattributesgets one marked block of/<copy> text eol=lflines appended to it in the split, so a Windows clone writes the pinned copies at the root with the bytes the pin records; the verification counts that file as the source's bytes followed by exactly that block, andupdate-shape.py apply --addextends it (#471) #511 added three tests totest_update_shape_add. Main's other test files changed since 191105a are in the sets above (test_adopt_execute_refusals, adopt-project.py:checknames, andexecuterefuses before the work directory is made, a plan whosesource:orlegs:is not a mapping or whoseallow_empty_legs:orpaths:is not a list of whatplanwrites, and asks the same first of apins:that is not a list, so a number or a boolean in any of the four no longer ends both in a traceback andallow_empty_legs: codeis no longer read as four legs (#386) #435's, whole in the first), and every run loads main'stests/conftest.py.test_adopt_root_symlinks(adopt-project.py:checkfollows each symlink the plan keeps where the shape writes its files, andexecuterefuses one the shape cannot write through, or one it writes beneath that git checks out as a file, before any leg exists, so such a link no longer breaks the run after both legs are pushed or has the shape's files written outside the assembly (#183, #178) #504),test_adopt_project_is_the_source(adopt-project.py:--organd--projectmust name the repository the source names, compared without case, soplanrefuses--project BillingServiceforacme/billing-serviceasadopt-not-the-sourceinstead of writing a plan whoseexecuteacts onacme/BillingService,checkfinds andexecuterefuses a plan edited to name another asplan-not-the-source, and a source whose own name the naming policy refuses is told to rename it first, never to retype--project(#499) #513),test_openreposhape_command(openRepoShape --doctor: the installed form keeps the clone of the standard it ran from and names it on the report's last line, so a NEXT line pasted after the command has exited runs instead of dying withcan't open file, and it hands--jsonand--placement-plan <file>on toshape-doctor.pyinstead of refusing them (#500) #510),test_update_shape_merged_away(update-shape.py and shape-doctor.py: no NEXT line pre-fills--accept-localfor a locally edited shape copy, so theapplya row names, pasted, meetsupdate-local-driftand its question to the human instead of re-pinning the edit as the standard, and ashape/collision copy merged into its original and removed ismerged-awaytocheck, whoseapplydrops its pin row behind the same yes, so an adopted root that did its follow-up validates green again (#501, #502) #512) andtest_refusal_default_remediation(repo_shape.py, validate-pins.py, family.py: a refusal built without a remedy prints itsREFUSEDline alone, the assembly root'svalidate-pins.pystill adds the lockstep remedy to its own, andaddrefuses a.gitmodulesgit cannot rea (#370) #494). With it: 248 passed. Without it: 223 passed, 25 skipped, eachneeds_filter_repo; tests.yml and adopt-project.py: the Windows job installs git-filter-repo 2.47.0, so the adoption test files that skip without it run there, and a source that keeps its own root.gitattributesgets one marked block of/<copy> text eol=lflines appended to it in the split, so a Windows clone writes the pinned copies at the root with the bytes the pin records; the verification counts that file as the source's bytes followed by exactly that block, andupdate-shape.py apply --addextends it (#471) #511 added one, totest_update_shape_merged_away. Main's other test files changed since 89c6a6c are in the sets above:test_adopt_finding_spellingandtest_adopt_ignored_shape_files(adopt-project.py:checkfollows each symlink the plan keeps where the shape writes its files, andexecuterefuses one the shape cannot write through, or one it writes beneath that git checks out as a file, before any leg exists, so such a link no longer breaks the run after both legs are pushed or has the shape's files written outside the assembly (#183, #178) #504);test_adopt_git_reads,test_adopt_late_failure,test_adopt_relative_submodule_url,test_adopt_split_branchandtest_adopt_tracking_branch(adopt-project.py:--organd--projectmust name the repository the source names, compared without case, soplanrefuses--project BillingServiceforacme/billing-serviceasadopt-not-the-sourceinstead of writing a plan whoseexecuteacts onacme/BillingService,checkfinds andexecuterefuses a plan edited to name another asplan-not-the-source, and a source whose own name the naming policy refuses is told to rename it first, never to retype--project(#499) #513);test_shape_doctor(openRepoShape --doctor: the installed form keeps the clone of the standard it ran from and names it on the report's last line, so a NEXT line pasted after the command has exited runs instead of dying withcan't open file, and it hands--jsonand--placement-plan <file>on toshape-doctor.pyinstead of refusing them (#500) #510, update-shape.py and shape-doctor.py: no NEXT line pre-fills--accept-localfor a locally edited shape copy, so theapplya row names, pasted, meetsupdate-local-driftand its question to the human instead of re-pinning the edit as the standard, and ashape/collision copy merged into its original and removed ismerged-awaytocheck, whoseapplydrops its pin row behind the same yes, so an adopted root that did its follow-up validates green again (#501, #502) #512); andtest_doctor_next_is_a_command,test_update_shapeandtest_update_shape_next_quoting(update-shape.py and shape-doctor.py: no NEXT line pre-fills--accept-localfor a locally edited shape copy, so theapplya row names, pasted, meetsupdate-local-driftand its question to the human instead of re-pinning the edit as the standard, and ashape/collision copy merged into its original and removed ismerged-awaytocheck, whoseapplydrops its pin row behind the same yes, so an adopted root that did its follow-up validates green again (#501, #502) #512)..gitattributesgets one marked block of/<copy> text eol=lflines appended to it in the split, so a Windows clone writes the pinned copies at the root with the bytes the pin records; the verification counts that file as the source's bytes followed by exactly that block, andupdate-shape.py apply --addextends it (#471) #511's new test file,test_adopt_attributes_eol_block: 37 passed, with it and without it. tests.yml and adopt-project.py: the Windows job installs git-filter-repo 2.47.0, so the adoption test files that skip without it run there, and a source that keeps its own root.gitattributesgets one marked block of/<copy> text eol=lflines appended to it in the split, so a Windows clone writes the pinned copies at the root with the bytes the pin records; the verification counts that file as the source's bytes followed by exactly that block, andupdate-shape.py apply --addextends it (#471) #511's other eleven changed test files are in the sets above.render-cli-reference.py --checkis ok. The full suite was not run here: the three-OS CI gate decides it.Windows. No Windows machine is reachable from the lane. A pytest plugin kept outside the repository makes text-mode writes in the test process write
\r\n, as on Windows:openandio.openwith nonewline, soPath.write_texttoo, and a text-modeinput=to a process. At #504's 9e8c672, run withoutgit-filter-repo, it fails exactly the 17 tests oftests/test_adopt_root_symlinks.pythe Windows job failed, with its message ("the plan was written with no pin"), and nothing else in that file; withgit-filter-repoit also failstest_without_a_pin_that_source_adopts, which needs disk symlinks and so never runs on Windows. At 7d6e13c, over this branch's four changed test files,tests/test_adopt_root_symlinks.pyand #511'stests/test_adopt_attributes_eol_block.py, it fails nothing: 1280 passed withgit-filter-repo, and 1207 passed, 73 skipped without it. Main 5ed3e5d, over the five of those files it has, gives 755 passed with it, and 713 passed, 42 skipped without it. Since #511 the Windows job installsgit-filter-repo, so the quoting file's 31 tests that need it run on Windows for the first time, and the run with it is the realistic one. Git's own line endings and file names on Windows are not emulated. This branch's added test lines hold noos.nameorsys.platformbranch, nochmod, no text-mode stdin, no path split and noread_bytes. Its twowrite_textcalls (answer,bare_template) are read back only as text, never as bytes, which is the pattern #504's Windows failure had. So nothing here changes for Windows; the three-OS CI is the gate.Reviews
Lane 2 at 76a6153: CHANGES NEEDED, one P2 (#181 (comment)). It confirmed the plan-file fix: 339,485 strings round-trip through
y()and the loader,y()differs from main on exactly 63 of them, and ordinary plans are byte-identical.project.yamland the leg pins; the gate (b26841b) refused it, and since templates/assembly-root/project.yaml and the two leg pins write eight text placeholders bare (id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259 the template quotes those lines and the value reads back (under What). The end-to-end test clones recursively and bootstraps what is accepted.Icon\rfile), socheckrefuses the whole plan asyaml-unparsablewith no entry to edit #199's, landed (4835fb4, merged): a name holding a line break is written on one line. P3-2: FIXED, the docstring that misstated main. The question: TAKEN: an explicitspec_path: nullisplan-bad-leg-path, as adopt-project.py accepts a non-canonical leg path such as--spec-path spec/, so the mount and its.gitmodulesregistration disagree andmainstill saysadoption verified#169 rules an explicit empty value.Lane 1's adversarial re-verification at a0ff15b (review181): PR-READY, 7 NITs. It re-ran lane 2's 26 scenarios, fuzzed the gate (2,440 flag rows and 3,872 hand-edited plan lines: 0 false refusals, 0 crashes), and found ordinary adoptions byte-identical to main.
007orTRUEis named by what was read; "not set in the plan" forid: null; the import comment and module docstring.adopt-project.pyand both as at 94242a2:'utf-8'(ten; main has nine) and"project.yaml"(three: this branch'sMANIFEST_YAML, 93e3bf0'sSHAPE_MANIFESTand An adopted assembly records nothing about which leg a human chose for a source submodule, so shape-doctor.py cannot verify a kept gitlink's placement and can only note it; adopt should write the plan's submodule answers into the assembly #189'sappend["project.yaml"]; main has two). None inscripts/shape_materialize.pyor the four test files then. At 7d6e13c against main 5ed3e5d'utf-8'is eleven against ten and"project.yaml"two against two (6c9e585 removedMANIFEST_YAML), andscripts/shape_materialize.pystill has none, but the quoting test file's K5 tests make a third use or more of five:"check"(seven),"product"(four), and"Remediation: ","assembly-root"and"scripts"(three each). Main'stests/test_adopt_pins.pyalready repeats"check"eight times and"product"nine, so they are named here, not folded._checked_plan_inputs, at 15 for cognitive complexity on main since adopt-project.py: a source whose default branch is notmainis adopted with both legs pushed, then its pull request is refused (--base main), and the printed way out repeats--base main#479, is at 15 here (by hand).id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259:--tracking-branch 2026adopts as typed, here and on main.rendersubstituting in turn, have since landed on main as scaffold-project.py and adopt-project.py ask the project's YAML reader and validate-manifest.py about the manifest's values before anything exists, so--id null,--tracking-branch trueand a blank--elected-byare refused (#258, #261) #314 and adopt-project.py and scaffold-project.py substitute the template placeholders one after another, so a free-text value holding another placeholder's token is expanded (--elected-by '{{SPEC_PATH}}'is recorded aselected_by: "spec") or, after the legs are pushed, refused astemplate-unsubstitutedwith "a defect in the substitution table of the tool you ran" #260, both merged here.Lane 1's re-verifications (rv181f).
tracking_branchwriteexecutenever makes, becausechecked_valuerefuses it first. F2b FIXED (e391b18): a visibility outside the three choices, whichexecutehands togh repo create, is no longer asked about. The merge of adopt-project.py: since #247 a byte that is not UTF-8 in--elected-by,--referenceor the--sourcedirectory passesplanandcheck, andexecutepushes both legs before it dies withUnicodeEncodeErrorinwrite_lf#255 DONE (e8e0026, dae165d).checkrows, 108planrows); the validator is now asked first. G2, SHOULD-FIX, FIXED (f38d19a): 44 values whose manifest the validator's reader refuses got two findings; now one. G3: eleven visibility texts the loader read as no value got no finding, a residual shared with main; since templates/assembly-root/project.yaml and the two leg pins write eight text placeholders bare (id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259 each is the validator'splan-manifest-invalid(under "Not in this PR").~got the misread finding, and git will not create it; the gate leaves out a misread branch git will not create. Since adopt-project.py: a source whose default branch is notmainis adopted with both legs pushed, then its pull request is refused (--base main), and the printed way out repeats--base main#479,~isplan-tracking-branch-missingandexecuterefuses it before any leg exists.~was the one branch the loader misreads thatchecked_valuepasses and git will not create. Its fuzz against main 5a25387 (5695checkrows, 3560planrows) found no row with both this gate's and the validator's finding, and every row with this gate's finding endedadoption verifiedon main's realexecute.Lane 1's re-verification at 180902f (rv181g, round K4): CHANGES NEEDED. Its own fuzz judge found 0 false clauses and 0 double findings, against main 191105a and on a scratch merge with main 8cd7dbe, and ordinary adoption byte-identical.
id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259 quotedtracking_branch:, no test held adopt-project.py: a source whose default branch is notmainis adopted with both legs pushed, then its pull request is refused (--base main), and the printed way out repeats--base main#479's composition or the validator-first order atplan: its rows K1, K2, K3, K3b and V2b failed 0 tests at 180902f. Three of the four tests new in round K5 hold them: K1 and K2 fail 3 each, K3 and K3b 2 each, and V2b 1 (the revert table above).executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514's docstring on_neutral_product_pinsbecame false once merged with this branch'sy(); it is made true (under "Composes with adopt-project.py:executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514").product:is answered by scaffold-project.py:--pin 007@<commit>exits 0 and scaffolds a root whose ownvalidate-pins.pyrefusesentry 7, becauseneutral_product_pins:entries (and the pin file'sproduct:) are written bare #516, which landed and quotes it (under "Not in this PR").Lane 1's re-verification at 9efe95a (rv181g, round K5): PR-READY. Its own fuzz, byte identity, pin files and targeted runs agreed with this branch's.
--organd--projectmust name the repository the source names, compared without case, soplanrefuses--project BillingServiceforacme/billing-serviceasadopt-not-the-sourceinstead of writing a plan whoseexecuteacts onacme/BillingService,checkfinds andexecuterefuses a plan edited to name another asplan-not-the-source, and a source whose own name the naming policy refuses is told to rename it first, never to retype--project(#499) #513 thatgit merge-treedoes not show: FIXED (6c9e585, under Tests).adopt-project.py, all this branch's and none printed, still said a pin's product is written bare, or that a pin? xmakes a manifest its reader refuses. N-7 sits in the adopt-project.py:--organd--projectmust name the repository the source names, compared without case, soplanrefuses--project BillingServiceforacme/billing-serviceasadopt-not-the-sourceinstead of writing a plan whoseexecuteacts onacme/BillingService,checkfinds andexecuterefuses a plan edited to name another asplan-not-the-source, and a source whose own name the naming policy refuses is told to rename it first, never to retype--project(#499) #513 conflict and is fixed in a22b7f9's resolution, the rest in 6c9e585. With docstrings blanked the module's AST is unchanged.Codex at 9efe95a: "Didn't find any major issues". SonarCloud at 9efe95a: the gate passed, with no S8705 and nine new smells: seven S9073 (a composite
assert) and one S5778 (two calls insidepytest.raises) in the quoting test file, and one S1192, a third"project.yaml"inadopt-project.py, which was this branch'sMANIFEST_YAML. 6c9e585 fixes all nine (under Tests and Merges).Merges
The branch was brought up to date by merging, never by rebasing:
mainhas been merged in 31 times. Round K6's four are 8beb3d9 in a22b7f9 (one hunk, resolved), b0826e0 in b602c16 (#504, three hunks, resolved), 3121945 in 42362de (#494, clean) and 5ed3e5d in 7d6e13c (#511, clean).7d6e13c merges main 5ed3e5d, by sha: tests.yml and adopt-project.py: the Windows job installs git-filter-repo 2.47.0, so the adoption test files that skip without it run there, and a source that keeps its own root
.gitattributesgets one marked block of/<copy> text eol=lflines appended to it in the split, so a Windows clone writes the pinned copies at the root with the bytes the pin records; the verification counts that file as the source's bytes followed by exactly that block, andupdate-shape.py apply --addextends it (#471) #511 for tests-windows never executes an adoption: git-filter-repo is not installed there, so the eight adopt suites skip and v1.0.0's "adopt works on Windows" is unverified #471. No conflict: the tree isgit merge-tree's, 88c22f0a, which a scratch merge with tests.yml and adopt-project.py: the Windows job installs git-filter-repo 2.47.0, so the adoption test files that skip without it run there, and a source that keeps its own root.gitattributesgets one marked block of/<copy> text eol=lflines appended to it in the split, so a Windows clone writes the pinned copies at the root with the bytes the pin records; the verification counts that file as the source's bytes followed by exactly that block, andupdate-shape.py apply --addextends it (#471) #511's predicted squash had before tests.yml and adopt-project.py: the Windows job installs git-filter-repo 2.47.0, so the adoption test files that skip without it run there, and a source that keeps its own root.gitattributesgets one marked block of/<copy> text eol=lflines appended to it in the split, so a Windows clone writes the pinned copies at the root with the bytes the pin records; the verification counts that file as the source's bytes followed by exactly that block, andupdate-shape.py apply --addextends it (#471) #511 landed; every count and probe at 7d6e13c here was measured on it.cmd_planis the one unit both sides changed, and keeps both: this branch's comment and_checked_plan_inputsasking the validator first, and tests.yml and adopt-project.py: the Windows job installs git-filter-repo 2.47.0, so the adoption test files that skip without it run there, and a source that keeps its own root.gitattributesgets one marked block of/<copy> text eol=lflines appended to it in the split, so a Windows clone writes the pinned copies at the root with the bytes the pin records; the verification counts that file as the source's bytes followed by exactly that block, andupdate-shape.py apply --addextends it (#471) #511'sattributes_link. Every other unit is the one side's that changed it, and no name inadopt-project.pyis bound twice. tests.yml and adopt-project.py: the Windows job installs git-filter-repo 2.47.0, so the adoption test files that skip without it run there, and a source that keeps its own root.gitattributesgets one marked block of/<copy> text eol=lflines appended to it in the split, so a Windows clone writes the pinned copies at the root with the bytes the pin records; the verification counts that file as the source's bytes followed by exactly that block, andupdate-shape.py apply --addextends it (#471) #511 changes no template, norenderand no lineexecutewrites into the project's YAML (under "Composes with tests.yml and adopt-project.py: the Windows job installs git-filter-repo 2.47.0, so the adoption test files that skip without it run there, and a source that keeps its own root.gitattributesgets one marked block of/<copy> text eol=lflines appended to it in the split, so a Windows clone writes the pinned copies at the root with the bytes the pin records; the verification counts that file as the source's bytes followed by exactly that block, andupdate-shape.py apply --addextends it (#471) #511").42362de merges main 3121945, by sha (fetched at 13:35Z): repo_shape.py, validate-pins.py, family.py: a refusal built without a remedy prints its
REFUSEDline alone, the assembly root'svalidate-pins.pystill adds the lockstep remedy to its own, andaddrefuses a.gitmodulesgit cannot rea (#370) #494 for repo_shape.py'sRefusalgives every refusal built without a remediation the assembly root's own (run git submodule update --init --recursive, then python3 scripts/validate-pins.py ... contracts/<leg>-pin.yaml), so in a family holderfamily.py addover an unparsable.gitmodules(git-failed),bumpon afamily.yamlof the wrong kind, and the holder's ownvalidate-family.pyon a shortpin.commiteach tell the operator to run a file no holder carries and to move a leg pin a holder does not have #370, which landed at 13:21:57Z. No conflict: the tree isgit merge-tree --write-tree b602c16 31219459's (4a502cdd), and no top-level unit ofadopt-project.py,scripts/repo_shape.py,scripts/family.pyorshape-doctor.pychanged on both sides. repo_shape.py, validate-pins.py, family.py: a refusal built without a remedy prints itsREFUSEDline alone, the assembly root'svalidate-pins.pystill adds the lockstep remedy to its own, andaddrefuses a.gitmodulesgit cannot rea (#370) #494 makesRefusal's default remediation none, so a refusal built without one prints itsREFUSEDline alone, and changesadopt-project.pyin_failed_loudlyalone. This gate's refusals each name their own remediation and none falls back to the empty default:plan's throughrefuse_manifest_valuesandMANIFEST_MISREAD_REMEDIATION,execute's through_refuse_what_check_finds' own list, and every otherRefusalin a unit this branch changed passes one too. The probes under "Composes with adopt-project.py:--organd--projectmust name the repository the source names, compared without case, soplanrefuses--project BillingServiceforacme/billing-serviceasadopt-not-the-sourceinstead of writing a plan whoseexecuteacts onacme/BillingService,checkfinds andexecuterefuses a plan edited to name another asplan-not-the-source, and a source whose own name the naming policy refuses is told to rename it first, never to retype--project(#499) #513" and "Composes with adopt-project.py:checkfollows each symlink the plan keeps where the shape writes its files, andexecuterefuses one the shape cannot write through, or one it writes beneath that git checks out as a file, before any leg exists, so such a link no longer breaks the run after both legs are pushed or has the shape's files written outside the assembly (#183, #178) #504" print each refusal's remediation at 42362de.b602c16 merges main b0826e0, by sha (fetched at 12:45Z): adopt-project.py:
checkfollows each symlink the plan keeps where the shape writes its files, andexecuterefuses one the shape cannot write through, or one it writes beneath that git checks out as a file, before any leg exists, so such a link no longer breaks the run after both legs are pushed or has the shape's files written outside the assembly (#183, #178) #504 for materialize_assembly_root writes the shape files through a root-kept symlink to anywhere on disk, and crashes with a traceback on a link loop, after both legs are pushed #183 and adopt-project.pycheckrefuses no root-kept symlink at or above a path the shape writes, so a link that dangles or leaves the tree makesexecutetraceback, or write the shape's files OUTSIDE the assembly, after both leg remotes are pushed #178, which landed at 12:40:37Z.git merge-treefinds three conflict hunks, all inadopt-project.py, and each keeps both sides:from shape_materialize import (...)list: this branch'sPLACEHOLDER_RE,TEMPLATEDandrender, and adopt-project.py:checkfollows each symlink the plan keeps where the shape writes its files, andexecuterefuses one the shape cannot write through, or one it writes beneath that git checks out as a file, before any leg exists, so such a link no longer breaks the run after both legs are pushed or has the shape's files written outside the assembly (#183, #178) #504'sSYMLINK_HOPS(moved there from this module),TOO_MANY_SYMLINKSandtemplate_files, each imported once and each read in the module;_entry_findings' docstring: this gate's finding after the mount paths, then the source's own submodules, then adopt-project.py:checkfollows each symlink the plan keeps where the shape writes its files, andexecuterefuses one the shape cannot write through, or one it writes beneath that git checks out as a file, before any leg exists, so such a link no longer breaks the run after both legs are pushed or has the shape's files written outside the assembly (#183, #178) #504's symlinks kept in the root, then the rest as both sides have it;_entry_findings' return:findings + misread + split + _root_symlink_findings(plan, source, tree) + ..., with this branch's remediation list unchanged, since adopt-project.py:checkfollows each symlink the plan keeps where the shape writes its files, andexecuterefuses one the shape cannot write through, or one it writes beneath that git checks out as a file, before any leg exists, so such a link no longer breaks the run after both legs are pushed or has the shape's files written outside the assembly (#183, #178) #504 adds a finding and no remediation to it.Every other path is
git merge-tree's, andadopt-project.pyis its blob byte for byte outside the three conflict blocks. git merged both sides cleanly where both touched one unit: the module docstring, andy(this branch's_reads_back_as_text, adopt-project.py:checkfollows each symlink the plan keeps where the shape writes its files, andexecuterefuses one the shape cannot write through, or one it writes beneath that git checks out as a file, before any leg exists, so such a link no longer breaks the run after both legs are pushed or has the shape's files written outside the assembly (#183, #178) #504'sFALSE_WORD). adopt-project.py:checkfollows each symlink the plan keeps where the shape writes its files, andexecuterefuses one the shape cannot write through, or one it writes beneath that git checks out as a file, before any leg exists, so such a link no longer breaks the run after both legs are pushed or has the shape's files written outside the assembly (#183, #178) #504 putswork_rootbeforeclonedinSource.__init__; its ownSource.openpasses both, and every other call passes four arguments by position, so none shifts.6c9e585 (round K6a): rv181g's M1 (under Tests), N-1 to N-6 and N-8 (under Reviews), and SonarCloud's nine smells.
MANIFEST_YAML, which nothing had read since 180902f took the pin's line from_template_table, is removed, so"project.yaml"appears twice inadopt-project.py, as on main. The Windows audit (under Counts) found nothing to change.a22b7f9 merges main 8beb3d9, by sha (fetched at 03:08Z): 173c00e (openRepoShape --doctor: the installed form keeps the clone of the standard it ran from and names it on the report's last line, so a NEXT line pasted after the command has exited runs instead of dying with
can't open file, and it hands--jsonand--placement-plan <file>on toshape-doctor.pyinstead of refusing them (#500) #510 for openRepoShape --doctor, the form AGENTS.md names first and the one--installgives a person, prints every NEXT command as a path into its temporary clone of the standard, which its EXIT trap deletes before the person can paste the command, so each one fails withcan't open file; and it refuses--jsonand--placement-plan, which AGENTS.md's doctor steps 7 and 4 use #500:openRepoShape --doctor), cf118ff (adopt-project.py:--organd--projectmust name the repository the source names, compared without case, soplanrefuses--project BillingServiceforacme/billing-serviceasadopt-not-the-sourceinstead of writing a plan whoseexecuteacts onacme/BillingService,checkfinds andexecuterefuses a plan edited to name another asplan-not-the-source, and a source whose own name the naming policy refuses is told to rename it first, never to retype--project(#499) #513 for adopt-project.pyplannever compares--project(or--org) with the repository it adopts in place, so a source namedbilling-serviceis refused with a remediation that steers to--project BillingService, which plans and checksplan okagainstacme/billing-service, andexecutethen acts onacme/BillingService: a rawgit ls-remotefailure when no such repository exists, and, when a different repository of that name does, both legs, the split branch, the pull request and the topic on that repository #499:--organd--projectmust name the repository the source names) and 8beb3d9 (update-shape.py and shape-doctor.py: no NEXT line pre-fills--accept-localfor a locally edited shape copy, so theapplya row names, pasted, meetsupdate-local-driftand its question to the human instead of re-pinning the edit as the standard, and ashape/collision copy merged into its original and removed ismerged-awaytocheck, whoseapplydrops its pin row behind the same yes, so an adopted root that did its follow-up validates green again (#501, #502) #512 for update-shape.pycheck's NEXT, and the doctor'sshape currencyrow that sends the reader to it, already carry--accept-local <path>for every locally edited shape copy, so pasting the command AGENTS.md says to run as named re-pins the project's edit as the standard: no refusal, no question to the human, a greenvalidate-pins.pyand a COMPLIANT doctor, though AGENTS.md says never to pass--accept-localand that keeping the edit is the human's decision #501 and update-shape.py, validate-pins.py and shape-doctor.py: once an adopted root does its own follow-up "merge shape/.gitignore into .gitignore" and removes the merged copy, the copy's pin row iscopy-missing, so the project'sbootstrap.pyand itsvalidatecheck go red,applyrefuses with a remedy (--accept-local) that is refused in the same words, and no tool removes the row, which leaves only undoing the merge or the hand edit of the pin that AGENTS.md forbids #502:update-shape.py,shape-doctor.pyandcollision_follow_upinscripts/shape_materialize.py). One conflict hunk, in_checked_plan_inputs, the only unit both sides changed: main passessourceto_check_names, and this branch adds its comment and its two refusals after that call. Resolved as main's call, then this branch's comment and calls in order: rv181g's proposed resolution (d55b975), with N-7 bringing the comment up to date. Main's other change to the function,_refuse_another_repositorybefore any pin's digest is read, merged cleanly above it. Every other path isgit merge-tree's;scripts/shape_materialize.pymerges cleanly, and no test of this branch asserts a collision follow-up. With this merge alone five tests of the quoting file failed (M1), which 6c9e585 fixes.Rounds K4 and K5, each by sha, are described in full in this description's edit history (the body at 9efe95a): 5c04ca2 merges main 631f087 (templates/assembly-root/project.yaml and the two leg pins write eight text placeholders bare (
id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259, README.md and AGENTS.md: 28 statements the v1.0.0 accuracy pass found false are corrected to what the tools do on main (#483) #505 and update-shape.py on a holder cut before siblings.py existed: taking the new Makefile without--add scripts/siblings.py(AGENTS.md's default) breaksmake siblings,parkandresume#489; one conflict,tests/test_adopt_manifest_values.py, six hunks, both sides kept), e3fe7cf merges 191105a (scaffold-project.py: a leg path the project's own YAML reader would read back as another type (null,~,true,False,2026,1.5) is refused before anything is created, under--dry-runalike, through the one definition adopt-project.py asks, so--spec-path 2026no longer creates three repositories whose first bootstrap failsmanifest-leg-path, andadopt-project.py executeno longer saysadoption verifiedbefore that bootstrap fails (#225) #266, clean), f54dce7 merges e6fbd74 (seven commits, adopt-project.py:checknames, andexecuterefuses before the work directory is made, a plan whosesource:orlegs:is not a mapping or whoseallow_empty_legs:orpaths:is not a list of whatplanwrites, and asks the same first of apins:that is not a list, so a number or a boolean in any of the four no longer ends both in a traceback andallow_empty_legs: codeis no longer read as four legs (#386) #435 and adopt-project.py:executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514 among them, clean;_template_tablekeeps both sides; at that bare merge the 14 tests round K4 named failed) and 8ce572b merges 89c6a6c (family.py:initrefuses to land in the standard's checkout and names a runnablescripts/family.py, printed commands quote paths, andadd,bumpandremoverefuse the tracking branch and print their branch's push (#488, #486, #487) #507 and scaffold-project.py:--pin 007@<commit>exits 0 and scaffolds a root whose ownvalidate-pins.pyrefusesentry 7, becauseneutral_product_pins:entries (and the pin file'sproduct:) are written bare #516, clean). 180902f and 9efe95a made the merges' sentences and those 14 rows true (under Tests and "Composes with adopt-project.py:executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514"). Before adopt-project.py:executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514 landed, rv181g's scratch merge of 180902f with 8cd7dbe failed three of adopt-project.py:checknames, andexecuterefuses before the work directory is made, a plan whosesource:orlegs:is not a mapping or whoseallow_empty_legs:orpaths:is not a list of whatplanwrites, and asks the same first of apins:that is not a list, so a number or a boolean in any of the four no longer ends both in a traceback andallow_empty_legs: codeis no longer read as four legs (#386) #435's pin tests, which pass with adopt-project.py:executewrites eachneutral_product_pins:entry bare, soplan --pin 007@<commit>saysadoption verifiedover a root whose ownvalidate-pins.pyrefusesentry 7#514 merged.Earlier merges, each by sha: d8ed5b0 (22c9fdd), c6d61d1 (e4decfb), a0ff15b (54aa69a), 2ad82cb (39ac59c), 44c8c17 (60d08a1), e4e5e44 (d94d155), e337f44 (ede15ce), 77218c8 (60588d5), 37d7a71 (92e29d9), bd695e5 (c3f4987), 2e225fe (a5b09c3), 2405670 (71b58a1), 4c6e856 (dd8d0ea, scaffold-project.py and adopt-project.py ask the project's YAML reader and validate-manifest.py about the manifest's values before anything exists, so
--id null,--tracking-branch trueand a blank--elected-byare refused (#258, #261) #314), 3662784 (75052cb), dc7b79d (437a360), edf9163 (7a8b981), ed9968f (5a25387), cf6da3f (965420b) and 43b0f58 (cc8f2da), and four named among the semantic conflicts below: 6b104cf (4835fb4), 3352be4 (be2790e), bf6d656 (a1b3a4b) and e8e0026 (1f90a0f). Each conflict was resolved keeping both sides. The semantic conflicts, each fixed in a commit of its own:_shownbeside adopt-project.py plan writes a plan its own loader cannot parse when a source name holds a line break (the macOSIcon\rfile), socheckrefuses the whole plan asyaml-unparsablewith no entry to edit #199's (6b104cf; 34d683f's_line_shown);k: [it's, a]reads two items,k: it's fine # notedrops its comment, and an unclosed"abcor a mid-key[is refused, not misread (#249, #250) #284'srenderspelling a quoted value withdouble_quoted(3352be4, 4bf2cc4);--elected-by '{{SPEC_PATH}}'is recorded aselected_by: "spec") or, after the legs are pushed, refused astemplate-unsubstitutedwith "a defect in the substitution table of the tool you ran" #260'srenderputting a value in once (bf6d656, d38e143);--elected-by,--referenceor the--sourcedirectory passesplanandcheck, andexecutepushes both legs before it dies withUnicodeEncodeErrorinwrite_lf#255 naming a value this gate named too (e8e0026, dae165d);--id null,--tracking-branch trueand a blank--elected-byare refused (#258, #261) #314's validator (f38d19a, the validator first);checknever reads a plan'selected_on:when the plan has noreference:, soplan okis followed by anexecutethat creates both leg repositories and only then refuseselection-date-malformed, naming--elected-on, a flag neither command takes #294'selection_date(4ffd460, f911d81);givenbound twice by adopt-project.py: a source whose default branch is notmainis adopted with both legs pushed, then its pull request is refused (--base main), and the printed way out repeats--base main#479's_checked_plan_inputs, so everyplanraisedTypeError(43b0f58 names this branch's setflags_given);mainis adopted with both legs pushed, then its pull request is refused (--base main), and the printed way out repeats--base main#479's branch questions and adopt-project.py--pin <name>is accepted byplan,checkandexecute("adoption verified"), but writes only the name intoneutral_product_pins:and nocontracts/<name>-pin.yaml, so the adopted root's ownvalidate-pins.py(and itsvalidate.yml) refuses withneutral-pin-missingfrom the first run #273's whole pins (69a743e, 94242a2).The gate followed scripts/repo_shape.py reads
? a: bin a flow mapping as the literal key? a, where the block reader refuses explicit keys, and refuses{a: }and{a:}asnot a flow collection: ''instead of reading{'a': None}#252's, scripts/repo_shape.py: the double-quoted reader that #247 adds (fix/adopt-plan-linebreak-names, not yet on main) resolves\x00inside a block-scalar KEY, and_parse_mappingsplits the NUL-joined sentinel record, so"a\x00b": |reads as the keyawith the bodyb<NUL>textwhere main reads the literal keya\x00b#248's and repo_shape.py, validate-manifest.py: a bracket in a plain flow scalar is refused and a name holding a lone surrogate is a finding, sopins: [a, b]]is not the pinb]andname: "a\ud83db"no longer killsmake validate(#301, #280) #390's reader changes with no edit.Main is 5ed3e5d at 16:09:26Z (
git ls-remote), merged here in 7d6e13c.After the merges, no top-level name is bound twice in
adopt-project.py,scaffold-project.py,scripts/repo_shape.py,update-shape.py,shape-doctor.pyorsetup-project.py(an AST scan, imports included), none is read and never bound, and no nameadopt-project.pyimports goes unread. Inscripts/shape_materialize.pyandscripts/family.py,YamlErrorandparse_yamlare each bound twice by a second import of them, exactly as on main. The branch's diff against main 5ed3e5d is six files:adopt-project.py,scripts/shape_materialize.py,tests/test_adopt_leg_paths.py,tests/test_adopt_manifest_values.py,tests/test_adopt_plan_yaml_quoting.pyandtests/test_template_text_placeholders.py.Composes with #266 (landed on main as 191105a, merged here)
Lane 2's #266 (#225) asks the same question in the shared leg-path rule,
leg_path_problem, of every template line that holds a leg path, rendered byrender.plan,checkandexecuteask that rule before this gate and before the validator. Since #259 those lines are quoted, so on the shipped templates neither rule refuses a leg path: #266's owntests/test_leg_path_yaml_scalars.pyrefuses only the values a real template line misreads, and skips a test whose values "the templates read ... back (#259 quotes the leg-path lines)", andREAD_BACKhere plans, checks and writes each of the nine leg paths as given. On a bare copy of the templates, given to both rules,test_a_leg_path_written_bare_is_266s_finding_aloneasks the composition:leg_path_problemrefusesnull,_misread_findingsnames nothing (_plan_fieldsasks this gate about a leg path only where that rule has no answer), andcheckprints one finding, #266'splan-bad-leg-path. The 28 rows round K3 said whichever landed second would adjust are gone with #259: the leg paths areREAD_BACKrows, and the leg-pathJUDGED_ONCErow is deleted (under Tests). #266's tests run in the third set under Counts.Composes with #514 (landed on main as e6fbd74, merged here)
Lane 2's #514 quotes an item of the manifest's
neutral_product_pins:list that the project's own reader would misread (_neutral_product_pins,_neutral_pin_entry). It landed as e6fbd74 (#515, 21:42:03Z, from its head cd18a1b) and is merged here in f54dce7. Its one line on this gate's path is_template_table'sNEUTRAL_PRODUCT_PINSentry. This gate asks that very table (_pin_field, through_manifest_values), so it asks the quoted line with no edit to the rule. At the bare merge exactly the 14 tests round K4 named failed: the threeREFUSEDpins atplan, atcheckand atexecute(9),JUDGED_ONCE'spin-text-misreadandpin-the-reader-refuses,JUDGED_BY_THE_VALIDATOR'sits-reader-refuses-the-file, the template-line test and the table test. #181 is the second to land, so 9efe95a makes them true (under Tests): the pins areREAD_BACKrows; the refusals are asked where the list is written bare (bare_pins), and so is G2's pin? x; and the two line tests expect- "Null"and- "null".9efe95a's one
adopt-project.pychange is to #514's own docstring on_neutral_product_pins. It said that a keywordyleaves bare reaches the list "only from a plan edited by hand, becausecheckrefuses the bareproduct: truethatplanwrites". With this branch'sy,planwritesproduct: "true"and"null",checkpasses them, andexecutewrites- "true"; the docstring says so now.With #514 and #516 landed, this gate finds nothing on main's templates, and stays as the backstop #314's scaffold twin is: a line a later template or table writes bare is refused again, with no edit to the rule (the tests over
bare_templateandbare_pins; writing the list bare again in_template_tablefails 9 tests).Composes with #513 (landed on main as cf118ff, merged here in a22b7f9)
#513 makes
--organd--projectname the repository the source names:planrefuses another asadopt-not-the-source,checkfinds a plan edited to name another asplan-not-the-source, andexecuterefuses it. Its_refuse_another_repositoryruns in_checked_plan_inputsbefore this gate's two refusals;checkaddsplan-not-the-sourceafter_entry_findings, which holds this gate's finding; andexecuterefuses #513's cause before it askscheck's findings. Probed at 7d6e13c in this process, as at 42362de, with the pins' list written bare (bare_pins) so that this gate names pinnull, on a source whoseoriginis undertestorg/Northwind:checkof a plan whoseorg:is edited tootherorg: exit 1,plan-manifest-misreadthenplan-not-the-source, one each, and this gate's remediation once;execute:REFUSED plan-not-the-sourcealone, with no remote made and no split branch;plan --org otherorg --pin null@<commit>:REFUSED adopt-not-the-sourcealone, and no plan written.So each cause is one finding at
check, andplanandexecuterefuse #513's first. M1 (under Tests) was the one test change the merge needed.Composes with #504 (landed on main as b0826e0, merged here in b602c16)
#504 (#183, #178) has
checkfollow each symlink the plan keeps where the shape writes its files, andexecuterefuse one the shape cannot write through before any leg exists. Itsplan-root-symlinkfindings are part of_entry_findings, the one listcheckprints andexecuterefuses, after the source's own submodules, so after this gate's finding. Probed at 7d6e13c in this process, as at 42362de, with a source that keepsscripts -> .in the root (the shape's scripts would land in the root itself, #504's R6 case) and, underbare_pins, a plan carrying pinnull:checkexits 1 withplan-root-symlinkalone;plan-manifest-misreadalone;checkexits 1 withplan-manifest-misreadthenplan-root-symlink, one each, and this gate's remediation once.executeexits 2 withREFUSED plan-manifest-misread: 2 finding(s), both listed incheck's order, no remote made and no split branch in the source;plan --pin null@<commit>from that source:REFUSED adopt-manifest-misread, and no plan written; adopt-project.py:checkfollows each symlink the plan keeps where the shape writes its files, andexecuterefuses one the shape cannot write through, or one it writes beneath that git checks out as a file, before any leg exists, so such a link no longer breaks the run after both legs are pushed or has the shape's files written outside the assembly (#183, #178) #504 asks nothing atplan.With #513's cause added too,
checkgives the three in that order, one each, andexecuterefuses #513's first. #504's test files run in the Counts above, and the Windows emulation runstests/test_adopt_root_symlinks.pytoo.Composes with #511 (landed on main as 5ed3e5d, merged here in 7d6e13c)
#511 (#471) appends one marked block of
/<copy> text eol=lflines to a source's own root.gitattributes, and verifies that file as the source's bytes followed by exactly that block. It writes nothing into the project's YAML, and adds no finding and no refusal before any leg exists (itsmaterialize-attributes-pathis for a pinned copy whose name git would read as a pattern, and the standard has none). Probed at 7d6e13c on a source that keeps its own.gitattributes:--spec-path null --tracking-branch true, through the shipped code as a process:checkpasses, andexecute --local-remote-dirappends the block and endsadoption verified; the manifest reads the spec leg's path back asnulland the tracking branch astrue, and both legs are ontrue;bare_pins, pinnull:checkgives this gate's finding alone, with its remediation once, andexecuterefuses it before any leg exists, the source's.gitattributesuntouched;.gitattributesthat is a symlink:checkfinds nothing without the pin, and this gate's finding alone with it.With the default flags, that source adopts byte for byte as on
main(33 split files).Not in this PR
executewrites asstrof it: a quiet residual shared with main. Probed at 7d6e13c, as at 42362de and 9efe95a:checkpassesid: 007, andexecute --local-remote-diradopts withid: "7";tracking_branch: 007, the source holding7, adopts on branch7(a source holding only007gets adopt-project.py: a source whose default branch is notmainis adopted with both legs pushed, then its pull request is refused (--base main), and the printed way out repeats--base main#479'splan-tracking-branch-missing, naming7);tracking_branch: true, the source holdingTrue, adopts onTrue; andtracking_branch: 1.50, the source holding1.5, adopts on1.5. Main 5ed3e5d does each the same, becausechecked_valuefalls back tostr(). The lineexecutewrites reads back as what it wrote, so this gate has nothing to name (READ_AS_NOT_TEXT:checkpasses each, as main does).plannever writes such a plan, because its writer quotes what would not read back. Read as not text, a leg path isplan-bad-leg-path, an election date adopt-project.py:checknever reads a plan'selected_on:when the plan has noreference:, soplan okis followed by anexecutethat creates both leg repositories and only then refuseselection-date-malformed, naming--elected-on, a flag neither command takes #294'selection-date-malformed, and a visibility the validator'smanifest-visibility, named as read.--pin 007@<commit>exits 0 and scaffolds a root whose ownvalidate-pins.pyrefusesentry 7, becauseneutral_product_pins:entries (and the pin file'sproduct:) are written bare #516's, which landed (89c6a6c, merged here) and quotes itsproduct:: at this head--pin null@<commit>,True@,007@and2026@adopt and bootstrap with- "<product>"in the manifest andproduct: "<product>"in the pin file, each read back as typed, and both of the root's own validators exit 0 (probed).--id null,--tracking-branch trueand a blank--elected-byare refused (#258, #261) #314 (for scaffold-project.py writes--idand--tracking-branchinto project.yaml unquoted, so--id nullor--id truescaffolds a project whose first bootstrap failsmanifest-id, and--tracking-branch true,007ornullputs the clone's legs onTrue,7ormainwith no upstream: #181's defect on the scaffold side, beside #225 #258), merged here. Its rule asks the reader inscripts/shape_materialize.py(manifest_misreads(values, what)), beside this branch's inadopt-project.py; both hand each template line torenderand askparse_yaml. Since templates/assembly-root/project.yaml and the two leg pins write eight text placeholders bare (id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259 neither refuses a value the template quotes. Making them one function is a follow-up.id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259: rv181f's G3 residual (a visibility the loader read as no value) and H1's (~). Probed at 9efe95a:visibilityset to"","null","~","#"," ",|-or"'private'"is oneplan-manifest-invalideach, the validator'smanifest-visibility, andexecuterefuses it before any leg exists, with noghcall.--tracking-branch ~, and a hand-editedtracking_branch: "~", are adopt-project.py: a source whose default branch is notmainis adopted with both legs pushed, then its pull request is refused (--base main), and the printed way out repeats--base main#479's:planrecords it with adopt-project.py: a source whose default branch is notmainis adopted with both legs pushed, then its pull request is refused (--base main), and the printed way out repeats--base main#479's note,checkfindsplan-tracking-branch-missing, andexecuterefuses it before any leg exists.id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259: round K3's two adopt-project.py: a source whose default branch is notmainis adopted with both legs pushed, then its pull request is refused (--base main), and the printed way out repeats--base main#479 limits. A misread branch only the source'soriginhas (--tracking-branch true):checksaysplan ok, andexecute --local-remote-dirrefuses it asplan-tracking-branch-missing(git ls-remote) before any remote is made. One the clone remembers afterorigindropped it:plan ok, andexecute --local-remote-diradopts ontrue. Neither is misread any more: the plan says"true", and the manifest's line is quoted.2026,1.5) adopt and bootstrap as typed, here and on main since templates/assembly-root/project.yaml and the two leg pins write eight text placeholders bare (id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259: each leg's HEAD is2026withorigin/2026as its upstream.Closes #181
🤖 Generated with Claude Code