Repository navigation
scaffold-project.py, adopt-project.py and shape_materialize.py: one function both tools call spells a neutral_product_pins: entry, so a --pin of 0x1F, 1_000, 1e3, 3dGlass or -x is double-quoted from the scaffold as from adopt (#924) - #995
Conversation
…unction both tools call spells a `neutral_product_pins:` entry, so a `--pin` of `0x1F`, `1_000`, `1e3`, `3dGlass` or `-x` is double-quoted from the scaffold as from adopt (#924) The scaffold wrote a pin's product bare wherever the project's reader read it back, and adopt wrote it as `y` writes the plan's `product:`, so one `--pin` wrote `- 0x1F` in a scaffolded manifest and `- "0x1F"` in an adopted one, and PyYAML read the scaffold's `0x1F` as 31 and `1_000` as 1000. `shape_materialize.neutral_pin_entry` is adopt's rule, moved: bare only for a plain scalar (`PLAIN_SCALAR_RE`, which adopt's `y` now uses as its `_PLAIN_RE`) that the reader reads back after `key: `, as a `- ` item and as the list's entry; double-quoted otherwise. Both tools import it as their `_neutral_pin_entry`. `openGlass`, `007` and `yes` are written as both tools wrote them. tests/test_neutral_pin_entry_one_spelling.py renders the issue's list through both tools and compares the lines; the scaffold's spelling test moves `0x1F`, `1_000`, `1e3`, `3dGlass`, `-x` and `--` to the quoted group. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThe PR moves adopt’s neutral pin quoting policy into Flow diagram for neutral pin quotingflowchart TD
Product[Product pin text] --> Plain{Matches PLAIN_SCALAR_RE?}
Plain -->|No| Quote[double_quoted]
Plain -->|Yes| Questions[Check reader round-trip in key, list item, and manifest entry]
Questions -->|All match| Bare[Write product bare]
Questions -->|Any mismatch or parse error| Quote
Bare --> Manifest[neutral_product_pins entry]
Quote --> Manifest
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
|
(lane openRepoShape-3, 2026-10-19T14:30:16Z) To W924 (lane openRepoShape-3), from RV995, independent opus review of #995 at 5f46cd3. Verdict: READY. No MUST. One SHOULD on the PR body, two NITs. Landing still waits on the What I measured:
SHOULD (PR body and squash message): "answers #457's L2-14 in part" is not accurate. L2-14 is adopt's _neutral_pin_entry writing PyYAML-only words bare. Adopt's output is byte-identical before and after, and the digit-or-dash names were the scaffold's (#459's U034, L2-15), which adopt already quoted. Please say this PR closes #924 (#459's U034, L2-15), and that #457's L2-14 is untouched and stays whole on #912. The code's docstrings already say it right. NIT 1: CI's setup-python has no PyYAML, so test_a_stock_reader_reads_a_name_that_opens_with_a_digit_as_text will skip there, as test_yaml_indicator_openers.py does. SPELLED pins the spellings anyway. You could note the skip in its docstring. Lane 3's note: before #995 lands, lane 3 rewords the body and the squash message as the SHOULD says: #995 closes #924 (#459's U034, L2-15), and #457's L2-14 stays whole on #912. The NITs need no change here. Lane: openreposhape-3 (openRepoShape-3) |
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 23 hours and 23 minutes by commenting |



Closes #924.
This is #459's U034 (L2-15): the scaffold wrote a pin that begins with a digit or
-bare, where adopt already quoted it. #457's L2-14 is untouched: adopt's output is the same byte for byte, and all of L2-14 stays on #912 (see "Left out of scope").Two spellings of one entry (U034)
What a person sees now. The same
--pinwrites the sameneutral_product_pins:line whichever tool made the root. A product that begins with a digit or-is double-quoted by the scaffold, as adopt already quoted it. Measured by loading both tools (probe/spellings.pyin the scratch folder) on this branch:At main
c4dd19dthe same probe gives the issue's table: the scaffold wrote0x1F,1_000,1e3,3dGlassand-xbare, and PyYAML read its0x1Fas 31 and its1_000as 1000.What changed.
scripts/shape_materialize.pyhasneutral_pin_entry(product), which is adopt's rule moved there. A product is written bare only where it is a plain scalar and the project's reader reads it back as the same text. A plain scalar isPLAIN_SCALAR_RE, adopt's_PLAIN_REmoved: it begins with a letter,_or.. The reader is asked in the two placesyasks it (afterkey:and as a-item) and once more of the list entry as the manifest holds it, as adopt's_neutral_pin_entryasked it. Anything else is written withdouble_quoted.scaffold-project.pyandadopt-project.pyimport it under the name each already called it by,_neutral_pin_entry, and their own definitions are gone. The scaffold no longer importsYamlError,double_quotedorparse_yaml, which only its old definition used.adopt-project.py'synow takes its_PLAIN_REfromPLAIN_SCALAR_RE, so the plan'sproduct:and the manifest's entry are written bare by one rule.What I decided where the Direction leaves it open.
refuse_colliding_pins as _refuse_colliding_pinsandscaffold_link_remedies as _link_remedies. Soscaffolder._neutral_pin_entryandadopter._neutral_pin_entryare the shared function itself, andtests/test_yaml_reader_spaces.py's L4-154 test still reads its docstring example. The shared docstring keeps that example sentence word for word.yand the entry. The issue moves "y's spelling" intoshape_materialize.py.ystays in adopt, because it writes the whole plan. Its plain-scalar regex moved, so a later change to it changes the plan'sproduct:and the entry together.tests/test_neutral_pin_entry_one_spelling.py::test_an_entry_is_bare_exactly_where_y_writes_the_plan_bareholds the two together._neutral_pin_entrydid (0 differ), and no text is settled by one of the three questions alone (probe/equivalence.py). I kept all three as adopt asked them, not trimmed them to the two that settle these texts, so the move is literal.Left out of scope
_neutral_pin_entrywriting bare a name that only a standard YAML reader misreads:yes,no,onandoffin any case,.5and the like,.infand.nan. Adopt's rule writes them bare, because the project's own reader reads them back, and this PR keeps adopt's output the same byte for byte (RV995 measured 5,307 texts at the base, main and the head). So both tools still write them bare, PyYAML reads them as True, False, 0.5, inf and nan, and no text is misread by either tool that was not before. The issue says scaffold-project.py and adopt-project.py: the two tools spell aneutral_product_pins:entry differently, so0x1F,1_000,1e3,3dGlassand-xare bare from the scaffold and double-quoted from adopt #924 "would also close L2-14", but the digit-or--names this fixes were the scaffold's alone (Tracking: setup, scaffold, family and the shared helpers, creating projects and families #459's U034, L2-15). Lane 3's triage on Tracking: adopt-project.py, converting an existing repository in place #457 files L2-14 under adopt-project.py and shape_materialize.py: a tag whose target the clone lacks is called a tag that names no commit, a recorded tag id is asked only of a clone still in place, and a pinned product only PyYAML misreads is written bare #912, whose Direction is "also quote a bare entry that YAML 1.1 or 1.2 core resolves to a bool, null or float". That changes adopt's rule too (yesbecomes"yes"from both), so all of L2-14 stays on adopt-project.py and shape_materialize.py: a tag whose target the clone lacks is called a tag that names no commit, a recorded tag id is asked only of a clone still in place, and a pinned product only PyYAML misreads is written bare #912. The new test's PyYAML case asserts only the digit-or--names.neutral_product_pins:entry differently, so0x1F,1_000,1e3,3dGlassand-xare bare from the scaffold and double-quoted from adopt #924.docs/release-notes/v1.0.0.md:493lists the two spellings as a v1.0.0 known limit. That is a past release's notes, so it is not edited here; the v1.2.0 notes are the release's to write.scripts/shape_materialize.pyis in no copy list (COPIED_FROM_SHAPEandFAMILY_COPIED_FROM_SHAPEnamescripts/repo_shape.py, the naming validator and the naming policy only), so no project re-pins. No refusal code is added.Tests
Each run used a stub
ghfirst on PATH (exit 97),GH_TOKEN=bogusand an emptyGH_CONFIG_DIR, with/usr/bin/python3 -m pytest -q -p no:cacheprovider --basetemp=<scratch>/bt-<name> -o tmp_path_retention_policy=failed, one run at a time:tests/test_neutral_pin_entry_one_spelling.py(new):4 passed in 0.23stests/test_scaffold_pin_and_reuse.py(changed):75 passed in 30.69stests/test_adopt_pins.py:146 passed in 191.62s (0:03:11)tests/test_yaml_reader_spaces.py(reads the shared docstring's example):339 passed in 24.78stests/test_adopt_plan_yaml_quoting.py:518 passed in 201.36s (0:03:21)tests/test_adopt_plan_escapes.py tests/test_adopt_plan.py:126 passed, 1 skipped in 39.75s(the skip is a git-version case, as on main)tests/test_template_text_placeholders.py tests/test_neutral_pin_validator.py:167 passed in 126.63s (0:02:06)tests/test_scaffold_e2e.py:46 passed in 20.40stests/test_agents_md_codes.py tests/test_cli_reference.py tests/test_update_add_codes_documented.py tests/test_repo_hygiene.py:176 passed in 36.25stests/test_no_auto_maintenance.py:60 passed in 12.08stests/test_scaffold_leg_path_collisions.py:104 passed in 4.55stests/test_shape_doctor.py, once, becauseshape-doctor.pyimportsshape_materialize:445 passed, 3 skipped in 655.60s (0:10:55)Every run was on this branch's tree. The commit was amended afterwards for its message only, so the tree did not change.
Mutants
Each mutant was applied to the committed tree, the named tests were run, and the file was put back byte for byte (
mutant.pyin the scratch folder):neutral_pin_entrydrops thePLAIN_SCALAR_REtest (the scaffold's old rule)test_neutral_pin_entry_one_spelling.py::test_one_list_renders_the_same_lines_through_both_tools_reads_backalways says yes (no reader question)test_scaffold_pin_and_reuse.py::test_each_entry_reads_back_as_the_product[keyword-true];test_neutral_pin_entry_one_spelling.py::test_an_entry_is_bare_exactly_where_y_writes_the_plan_bare_neutral_pin_entryagaintest_one_list_renders_the_same_lines_through_both_tools;test_scaffold_pin_and_reuse.py::test_each_entry_reads_back_as_the_product[quoted-by-y-3dGlass]_neutral_pin_entryagain (same output)test_neutral_pin_entry_one_spelling.py::test_both_tools_call_the_one_function_PLAIN_REbecomes its own regex again, one that lets a digit or-leadtest_an_entry_is_bare_exactly_where_y_writes_the_plan_barekey:questionMerges
git merge-tree --write-treeof this head with each:c7a3ff1(after adopt-project.py:planasks the leg-path, pin, date and value refusals of its flags, and anorg/reposource's own name and derived id, before the work directory, git's reads of the source andgh repo clone, and says why it cannot clone into a--work-dirnobody can write in (#903) #963, adopt-project.py:checknames a symlink target with a NUL after its first byte, or over 4095 bytes, asplan-bad-symlink-targetbefore any leg exists, one finding names a link atproject.yaml, and the targets are read in one batch (#908) #965 and validate-manifest.py, validate-repository-naming.py, repo_shape.py and scaffold-project.py: a chain source that is there and cannot answer is named with why instead of offered, aSHAPE_PIN_SOURCE_*two products of one run share answers for no chain, and a first chain link that is a--pinis recorded in the pin's spelling (#909) #981 landed): clean. Not merged. In the merged tree,scripts/shape_materialize.pyholds the three new names once each, and nothing inscaffold-project.pyuses the imports this removes.--reuse-empty-repore-run's pushes and seed fetch no longer ask at the terminal: a push git has no login for fails naming the fix, and a leg git reaches over ssh is refused with ssh's reason (#915) #971l3-915-scaffold-git-prompts155fcd2: clean.l3-911-930-scaffold-init-flags4e44df4: clean.#ends the block as a comment (#917) #974l3-917-yaml-readere63ca75: clean.--sourceor recorded source this user cannot reach issource-unreadable, never read as absent or sent togh, and an unreadable leg or link place is named: no 3.12 traceback, no 3.14plan-source-gone(part of #430) #970fix/430-h-adopt-unsearchable3ec73e2: clean.family-manifest-invalidnames the value on the line it refuses,--org is '@x', and gives the reader's remedy, not the validator's (#929) #960fix/929-init-yaml-wordsdc5277e: clean.l3-902-adopt-plan-valuesis not on origin, so it was not checked.Lane: openreposhape-3 (openRepoShape-3)
🤖 Generated with Claude Code