Skip to content

adopt-project.py: _pin_row_problem says a pin field whose value is not text as _read_as says what the plan's reader read, "records commit: as the number 7", where it spelled it as Python does, commit: 7 and True, so check and execute name it in the words the plan's other refusals use under the same plan-pin-malformed (#1114) - #1198

Draft
brettheap wants to merge 1 commit into
mainfrom
fix/1114-pin-row-value-read-as

Conversation

@brettheap

@brettheap brettheap commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Closes #1114

This PR lands only after the v1.2.0 tag, on Brett's word.

What

_pin_row_problem holds each of a pin's five fields (product, repository, commit, tree_sha256, digest_source) to text. A value that is not text it spelled with Python's repr: a number as commit: 7, a boolean as True, a list as ['a', 'b'] and a mapping as {'k': 'v'}. Every other refusal of a plan's value says what the reader read, with _read_as: "the number 7", "the boolean true", "a list of 2 item(s)", "a mapping of 1 key(s)". W1020 found the gap while working on #1020.

It says it that way now, field by field, in adopt-project.py's _pin_row_problem, in the not_text branch only:

  • one field: records `commit:` as the number 7, which is not text
  • two or more: records `commit:` as the number 7, `tree_sha256:` as the boolean true, which are not text

What stays as it was:

  • the code, plan-pin-malformed, the remedy, and the sentence's ending, which is not text;
  • every text value's spelling, byte for byte (commit: '7', which is not 40 hex);
  • a null or a blank: it is "records no commit:" under the code it has always had, and no value is spelled there.

Every field takes the one site (PIN_FIELDS). The docstring and a comment say so. The change is 13 lines added and 4 removed in adopt-project.py.

Behaviour change

A pin field that is a number, a boolean, a list or a mapping is named by what it was read as, where it was named as Python spells it. A text value, a null and a blank print as they did.

Rows

tests/test_adopt_pin_row_values.py is new, ASCII only, with 45 rows. Each runs check and then execute --yes, with a stub gh first on PATH and --local-remote-dir, and asserts one finding, execute refusing with that same finding, an empty gh log, no remotes directory made and no Traceback.

Group Rows On main's code On this head
The matrix: 5 fields by number, boolean, list, mapping 20 20 failed 20 passed
The whole sentence: commit: 7, and a boolean digest_source 2 2 failed 2 passed
Edge forms: 0, false, 7.5, -3, 007, [], {}, [a] 8 8 failed 8 passed
Two fields at once, a non-text commit beside bad text, entry 2 3 3 failed 3 passed
Null and blank controls 6 6 passed 6 passed
Text controls (commit: "7" and "true", another product, no owner, short digest, source by hand) 6 6 passed 6 passed
All 45 33 failed, 12 passed 45 passed

Every one of the 33 failures is the finding text holding the repr spelling (records `commit: 7` , True, ['a', 'b'], {'k': 'v'}, {}, []). There is no crash or other cause. The 12 that pass on main are the controls, which pin what must not change.

Two existing rows pinned the old spelling, and no others did. Each now expects the new one, and both pass on this head (2 passed):

  • tests/test_adopt_pins.py, the a-number-commit row: `commit:` as the number 1234567;
  • tests/test_adopt_plan_value_order.py, the not-text row: `digest_source:` as the number 7.

Mutants

19 mutants of this head's adopt-project.py, all killed. Each was run against the new file (first failure with -x, or a whole run where the table says so), and each run was restored with its sha256 checked.

Mutant Result
M1: main's repr form put back killed, 33 failed and 12 passed
M2: !r inside the new "as" form killed, 33 failed and 12 passed
M3: str() in place of _read_as killed
F-repr-product, F-repr-repository, F-repr-commit, F-repr-tree_sha256, F-repr-digest_source: repr put back for one field only 5 of 5 killed, each by that field's own rows
M4: "is" in place of "as" killed
M5: singular and plural swapped killed
M6: only the first field named killed
M7: missing takes any falsy value (an existing guard; the zero, false and empty rows pin it) killed
M8: only a list and a mapping counted as not text killed
M9: the ending without "how it was read" killed
M10: the code plan-pin-unrecorded killed
T1 to T4: the text branches unquoted (commit, product and repository, digest, source) 4 of 4 killed

Gate

A scratch merge of this head d38e4e36d with main 961640c68: commit 1ef3f0783, tree b12197610, which is this head's own tree. Targeted files only, in the landing gate's environment (a stub gh first on PATH, GH_TOKEN=bogus, an empty GH_CONFIG_DIR, an empty global git config). The set is 47 test files: every one that names plan-pin-malformed, plan-pin-unrecorded, _pin_row_problem, _read_pins or pins:, the new file, and test_path_guard_hygiene.py, test_repo_hygiene.py and test_scaffold_leg_path_collisions.py; and test_shape_doctor.py (448 ids, each in exactly one shard, run from a script that reads each list into an array because 25 ids hold spaces). Every chunk exited 0, with 0 failed.

Chunk Files Result
f01 test_adopt_pins.py 150 passed
f02 test_adopt_plan_yaml_quoting.py, test_adopt_plan.py 606 passed, 1 skipped
f03 test_adopt_execute_refusals.py, test_adopt_pin_row_values.py, test_adopt_plan_escapes.py, test_adopt_plan_value_order.py, test_adopt_reencoded_files.py, test_adopt_root_symlinks.py, test_adopt_shape_pin_and_work_dir.py, test_adopt_template_values.py 661 passed, 3 skipped
f04 test_bump_remedies_family_root.py, test_chain_link_fold.py, test_chain_pin_sources.py, test_chain_remedy_callers.py, test_doctor_gitlink_repairs.py, test_doctor_recorded_answers.py, test_family_add_member_id.py, test_family_makefile_siblings.py 255 passed
f05 test_family_tracking_branch.py, test_legs_not_a_list.py, test_link_source_chain_defaults.py, test_manifest_validator.py, test_naming_policy.py, test_neutral_pin_entry_one_spelling.py, test_neutral_pin_validator.py, test_non_list_project_fields.py 757 passed
f06 test_path_guard_hygiene.py, test_pin_annotated_tag.py, test_pin_source_env_collisions.py, test_pinned_copy_text.py, test_reader_refusals_named.py, test_repo_hygiene.py, test_scaffold_e2e.py, test_scaffold_leg_path_collisions.py 599 passed
f07 test_scaffold_pin_and_reuse.py, test_scaffold_pin_collisions.py, test_scaffold_referent_chain_links.py, test_setup_project_py.py, test_template_text_placeholders.py, test_update_shape_add_ignored.py, test_update_shape_landing.py, test_validate_pins_named_and_spelled.py 1078 passed
f08 test_yaml_flow_brackets.py, test_yaml_flow_quotes.py, test_yaml_indicator_openers.py, test_yaml_reader_spaces.py 813 passed
d01 test_shape_doctor.py, 56 ids 56 passed
d02 the same, 56 ids 56 passed
d03 the same, 56 ids 56 passed
d04 the same, 56 ids 56 passed
d05 the same, 56 ids 56 passed
d06 the same, 56 ids 56 passed
d07 the same, 56 ids 56 passed
d08 the same, 56 ids 53 passed, 3 skipped

The file chunks hold 4919 passed and 4 skipped (f02: SHAPE_MEDXEHR_CLONE is unset; f03: the reftable backend needs git 2.45). The doctor shards hold 445 passed and 3 skipped (d08: the PowerShell line is printed only on a Windows host). In all, 5364 passed, 7 skipped, 0 failed.

Run on the merge with #1125's head. A scratch merge of this head with #1125's head 9549006fe: commit 8a291d8b2, tree 88530bb7. The new file (45), test_adopt_pins.py (150), test_adopt_plan_value_order.py (40), test_adopt_execute_refusals.py (181) and test_adopt_check_plan_name.py (45): 461 passed.

Merge-trees

git merge-tree --write-tree, this head against each, all clean (exit 0), at the heads of 2026-10-23T08:20Z:

This PR needs no #1125

#1125 (for #1020) makes _read_pins say an entry that is not a pin as _read_as says it. Its adopt-project.py hunks are at the _read_pins docstring and at the non-dict entry's {row!r} (about line 625 on main), and they do not touch _pin_row_problem. Its test hunks do not overlap the lines this change edits. The two are textually independent: this PR is based on main, not stacked, the merge-tree with #1125's head is clean, and the run above on the merge with its head passed.

An entry that is neither a name nor a mapping stays #1020's, in #1125.

Not in this PR

The other repr in the pin rows are on a value that is text by then: the later branches of _pin_row_problem (spelled as before), a text product in an entry's name, and an entry that is a name. They are not gaps.

docs/release-notes/v1.2.0.md is left alone. Its lines 297 to 299 mention #901's pins wording, and the release owner may want a line there.

Out of scope

Four other places in adopt-project.py put a plan value of any type into a refusal with !r, as the pin rows did: plan-wrong-kind, plan-unsupported-mode, plan-bad-leg and plan-bad-id. They are not pin rows, so this change leaves them. They were found by reading the code, not by running it. They are filed as #1199, with each site's line and what it would print.

🤖 Generated with Claude Code
Lane: openreposhape-4 (openRepoShape-4)

…not text as `_read_as` says what the plan's reader read, "records `commit:` as the number 7", where it spelled it as Python does, `commit: 7` and `True`, so `check` and `execute` name it in the words the plan's other refusals use under the same `plan-pin-malformed` (#1114)

`_pin_row_problem` held each of a pin's five fields (`product`, `repository`, `commit`, `tree_sha256`, `digest_source`) to text and spelled a value that is not text with `repr`: a number as `commit: 7`, a boolean as `True`, a list as `['a', 'b']` and a mapping as `{'k': 'v'}`. Every other refusal of a plan's value says what the reader read (`_read_as`): "the number 7", "the boolean true", "a list of 2 item(s)", "a mapping of 1 key(s)". It says it that way now, field by field: "records `commit:` as the number 7, which is not text", and for two or more "records `commit:` as the number 7, `tree_sha256:` as the boolean true, which are not text".

What stays as it was: the code (`plan-pin-malformed`), the sentence's ending, and every text value's spelling byte for byte (`commit: '7'`, which is not 40 hex). A null or a blank is "records no `commit:`" under the code it has always had, and spells no value.

The other `!r` in the pin rows are on a value that is text by then: the later branches of `_pin_row_problem`, a text product in the entry's name, and an entry that is a name. An entry that is neither a name nor a mapping is #1020's, in #1125.

tests/test_adopt_pin_row_values.py runs each of the five fields with a number, a boolean, a list and a mapping through `check` and `execute`, with the edges of `_read_as`, two fields at once, null and blank rows, and text controls. tests/test_adopt_pins.py and tests/test_adopt_plan_value_order.py had one row each that pinned the old spelling.

Lane: openreposhape-4 (openRepoShape-4)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Oct 23, 2026

Copy link
Copy Markdown

Reviewer's Guide

Updates malformed pin diagnostics so non-text values are described using _read_as (for example, “commit: as the number 7”) rather than Python repr, while preserving existing behavior for text, null, and blank values. Adds broad check/execute regression coverage and updates two existing expectations.

Flow diagram for malformed pin value diagnostics

flowchart LR
    PinField["Pin field value"] --> Missing{"Null or blank?"}
    Missing -->|Yes| NoRecord["records no field"]
    Missing -->|No| Text{"Text value?"}
    Text -->|Yes| Existing["Existing text validation and spelling"]
    Text -->|No| ReadAs["_read_as(value)"]
    ReadAs --> Diagnostic["records field as reader description"]
    Diagnostic --> Refusal["plan-pin-malformed"]
Loading

File-Level Changes

Change Details Files
Align malformed pin-field diagnostics with the reader’s human-readable type descriptions instead of Python repr output.
  • Use _read_as for non-text values across all five PIN_FIELDS, preserving singular/plural wording and the existing refusal code and remediation.
  • Document the diagnostic convention and unchanged handling of null, blank, and text values.
adopt-project.py
Add comprehensive regression coverage for non-text pin values and preserve existing diagnostic behavior.
  • Test all five fields across numbers, booleans, lists, mappings, edge values, and multiple simultaneous invalid fields in both check and execute.
  • Verify null, blank, and text controls retain their prior wording, codes, and quoting.
  • Update existing expectations for numeric pin-field diagnostics.
tests/test_adopt_pin_row_values.py
tests/test_adopt_pins.py
tests/test_adopt_plan_value_order.py

Assessment against linked issues

Issue Objective Addressed Explanation
#1114 Spell non-text values in malformed pin fields using _read_as rather than Python repr, so refusals describe what the plan reader read. ✅

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sonarqubecloud

Copy link
Copy Markdown

@brettheap

Copy link
Copy Markdown
Contributor Author

RV1198: independent review of #1198 at d38e4e36: READY

RV1198 started this review and stopped at an account swap with nothing posted. RV1198-cont finished it with RV1198's scripts, and did not redo the measurements RV1198 had already made. This is the one comment.

No MUST and no SHOULD. There are three NITs, all for follow-ups. Two predate this PR and are equally true on main: one in the plan's reader and one in _read_as. Each needs a follow-up issue. The third is about the PR body alone.

What was measured

  • Heads, read again at 2026-10-23T12:57Z: adopt-project.py: _pin_row_problem says a pin field whose value is not text as _read_as says what the plan's reader read, "records commit: as the number 7", where it spelled it as Python does, commit: 7 and True, so check and execute name it in the words the plan's other refusals use under the same plan-pin-malformed (#1114) #1198 at d38e4e36d, and origin/main at 961640c68, which is the v1.2.0 tag's commit.
  • Scope. The change is the not_text branch of _pin_row_problem, 13 lines added and 4 removed in adopt-project.py, as the body says. The rest is the new rows file (45 rows, ASCII only) and the two edited rows. The reader, scripts/repo_shape.py, is untouched (0 diff lines).
  • The PR's rows on main's code. Main's adopt-project.py (sha256 0c1c1f3d) was put in place and then restored, with the sha256 checked after.
    • tests/test_adopt_pin_row_values.py: 33 failed, 12 passed, as the body says. Every failure is the finding in its old repr spelling. The 12 are the controls.
    • The plan-pin-malformed-a-number-commit row of tests/test_adopt_pins.py: 1 failed.
    • The whole of tests/test_adopt_plan_value_order.py: 1 failed, 19 passed. The one failure is [not-text], on records `digest_source: 7` . On the merge's code the file gives 20 passed.
  • A direct probe of _pin_row_problem at this head: 5 fields by 47 YAML spellings, 235 rows.
    • Every number, float, boolean, list and mapping is said in _read_as's words. No value that is not text is spelled with repr, and nothing raised.
    • A null, ~, '' or "" still gives "records no <field>:" under the code it had.
    • This reader has no date type. 2026-10-23, 2026-10-23T10:00:00Z, 12:30, yes, on, 0x1F, 1e5 and .inf are all read as text, so they reach the text branches, which are unchanged.
  • The one caller, _read_pins. Its remedy, _pin_fix(row.get('repository') or product), puts <openProduct> in place of any value that is not text, so no repr reaches the remedy either.
  • CI runs these rows. All 45 are @needs_filter_repo, and the tests job installs git-filter-repo==2.47.0 (tests.yml), so they run there rather than skip.

Gate

The gate ran on a scratch merge, 1131ee5fc, of this head and 961640c68. Its tree, b12197610, is this head's own tree.

  • Environment: the landing gate's. A stub gh first on PATH, GH_TOKEN=bogus, an empty GH_CONFIG_DIR, an empty global git config, GIT_CONFIG_NOSYSTEM=1 and the test identities.
  • Files: targeted files only, the same 8 chunks as FIN1114's gate.
  • test_shape_doctor.py: run in 8 shards from a bash script, with the ids one per line read into an array. The shards hold each of the 448 collected ids exactly once.
  • Disk: every chunk and shard started at 17G or more free, as df -BG shows it (rounded up).
Chunk Files Result
f01 test_adopt_pins.py 150 passed
f02 test_adopt_plan_yaml_quoting.py, test_adopt_plan.py 606 passed, 1 skipped
f03 test_adopt_execute_refusals.py, test_adopt_pin_row_values.py, test_adopt_plan_escapes.py, test_adopt_plan_value_order.py, test_adopt_reencoded_files.py, test_adopt_root_symlinks.py, test_adopt_shape_pin_and_work_dir.py, test_adopt_template_values.py 661 passed, 3 skipped
f04 test_bump_remedies_family_root.py, test_chain_link_fold.py, test_chain_pin_sources.py, test_chain_remedy_callers.py, test_doctor_gitlink_repairs.py, test_doctor_recorded_answers.py, test_family_add_member_id.py, test_family_makefile_siblings.py 255 passed
f05 test_family_tracking_branch.py, test_legs_not_a_list.py, test_link_source_chain_defaults.py, test_manifest_validator.py, test_naming_policy.py, test_neutral_pin_entry_one_spelling.py, test_neutral_pin_validator.py, test_non_list_project_fields.py 757 passed
f06 test_path_guard_hygiene.py, test_pin_annotated_tag.py, test_pin_source_env_collisions.py, test_pinned_copy_text.py, test_reader_refusals_named.py, test_repo_hygiene.py, test_scaffold_e2e.py, test_scaffold_leg_path_collisions.py 599 passed
f07 test_scaffold_pin_and_reuse.py, test_scaffold_pin_collisions.py, test_scaffold_referent_chain_links.py, test_setup_project_py.py, test_template_text_placeholders.py, test_update_shape_add_ignored.py, test_update_shape_landing.py, test_validate_pins_named_and_spelled.py 1078 passed
f08 test_yaml_flow_brackets.py, test_yaml_flow_quotes.py, test_yaml_indicator_openers.py, test_yaml_reader_spaces.py 813 passed
d00 to d07 test_shape_doctor.py, 448 ids (62, 56, 54, 56, 60, 57, 58, 45) 445 passed, 3 skipped (all in d06)
  • File chunks: 4919 passed, 4 skipped. The skips are f02's SHAPE_MEDXEHR_CLONE, which is unset, and f03's three reftable rows, which need git 2.45.
  • Doctor: 445 passed, 3 skipped. The skips are the PowerShell rows, which run only on a Windows host.
  • In all: 5364 passed, 7 skipped, 0 failed. These are the same counts as FIN1114's gate in the body, chunk by chunk. Its doctor shards were sized differently, but the totals are equal.

Mutants

There were 32 mutants of this head's adopt-project.py, and all 32 were killed. Each one was run against the whole new rows file and then restored. Its sha256 was checked after each run (f6162d6f), and the mutant tree is clean.

Mutant Rows that failed, of 45
W1114 M1: main's repr form put back 33
W1114 M2: !r inside the new "as" form 33
W1114 M3: str() in place of _read_as 33
W1114 F-repr-product: repr for that field only 4
W1114 F-repr-repository 4
W1114 F-repr-commit 16
W1114 F-repr-tree_sha256 5
W1114 F-repr-digest_source 5
W1114 M4: "is" in place of "as" 33
W1114 M5: singular and plural swapped 33
W1114 M6: only the first field named 1
W1114 M7: missing takes any falsy value 4
W1114 M8: only a list and a mapping counted as not text 20
W1114 M9: the ending without "how it was read" 5
W1114 M10: the code plan-pin-unrecorded 33
W1114 T1: the commit text unquoted 2
W1114 T2: the product and repository text unquoted 2
W1114 T3: the digest text unquoted 1
W1114 T4: the source text unquoted 1
RV1198 B-int-repr: repr for an int only 12
RV1198 B-float-repr: repr for a float only 1
RV1198 B-bool-repr: repr for a boolean only 8
RV1198 B-list-repr: repr for a list only 7
RV1198 B-dict-repr: repr for a mapping only 6
RV1198 B-loader-made: _the_loader_made in place of _read_as 13, the list and mapping rows: the two helpers word a number and a boolean alike
RV1198 J1: " and " as the joiner 1
RV1198 J2: the fields in reverse order 1
RV1198 J3: no backticks round the key 33
RV1198 J4: the singular ending changed 32
RV1198 K1: missing takes [] and {} 2
RV1198 K2: missing takes a null only 1
RV1198 K3: not-text asked of the commit and the digest only 13

B-float-repr is killed by one row only, the float edge (7.5). That is enough, and it is the one guard on that branch.

Merge-trees

This head was merged with git merge-tree --write-tree against each of the following, and every merge was clean (exit 0). The heads were fetched at 12:05Z and listed again at 12:57Z.

Every merged tree keeps the new not_text line, the new rows file byte for byte, and both edited rows. None brings back the old repr form. No other open PR adds a line about a pin's "not text" finding. #1177's _not_text is main's helper for id:, and #1177 changes only one call of it.

#1125, looked at closely.

  • No shared code. Its adopt-project.py hunks are these, and none touches _pin_row_problem:
    • the _read_pins docstring;
    • the entry that is no mapping, where {row!r} becomes {_read_as(row)};
    • the order of org: and id: in cmd_check and cmd_execute;
    • docstrings.
  • The shared test table. Its tests/test_adopt_pins.py hunk is the a-number row of the same malformed table, about 55 lines above this PR's a-number-commit row. Merged, the two rows use one wording: "pins: entry 1 is the number 7, not a pin" and "records commit: as the number 1234567, which is not text".
  • The run. On a scratch merge, c4dfb492e (tree 88530bb7b, which differs from main only in the two PRs' six files), these files gave 461 passed, as the body says:
    • the new rows file: 45;
    • test_adopt_pins.py: 150;
    • test_adopt_plan_value_order.py: 40;
    • test_adopt_execute_refusals.py: 181;
    • test_adopt_check_plan_name.py: 45.

Findings

NIT-1 (predates this PR; in _read_as, shared by every caller): an overflowing float is said as a number this reader would never read. It needs a follow-up issue.

  • What it says. A commit: of four hundred 1s followed by .5 gives records `commit:` as the number inf, which is not text in both check and execute, measured through both on the merge. Three hundred 9s followed by .5 gives "the number 1e+300".
  • Why it is wrong. This reader reads inf and 1e+300 as text, and "the number inf" stands for every float that overflows. On main the same values read as commit: inf.
  • Remedy (a follow-up): have _scalar refuse a decimal whose float is not finite, as yaml-unparsable. Then have _read_as say a float in a form this reader reads back as that number, with no exponent and no inf, in place of repr.

NIT-2 (predates this PR; in the plan's reader): a plain integer of more than 4300 digits ends check and execute in a Python traceback. It needs a follow-up issue.

  • Where it comes from. _scalar's int(text) raises ValueError: Exceeds the limit (4300 digits) for integer string conversion, and load_yaml catches only YamlError.
  • What was measured on the merge (Python 3.12.3, limit 4300; CI's 3.14 has the same default):
    • A commit: of 4301 digits gives a traceback with exit 1 in both check and execute.
    • So does a 4301-digit value at a top-level key outside pins:. So this is the reader's, not the pin rows'.
    • Neither run asked gh anything or made the remotes directory.
    • 4300 digits is a finding as it should be: exit 1 from check and exit 2 from execute.
  • Remedy (a follow-up): catch the ValueError in _scalar and raise a YamlError naming the line, so that it is yaml-unparsable.

NIT-3 (the PR body only): the release-notes paragraph cites the wrong lines.

Not a finding. Some different plan spellings get the same words, because the reader reads them as the same value. 007 and 7 both say "the number 7", and the PR's padded row pins that. 7.50 and 7.5 both say "the number 7.5", and -0 and 0 both say "the number 0". Lists or mappings of the same size get the same words. Main's repr gave the same value in each case.

Lane: openreposhape-4 (openRepoShape-4)

This was referenced Nov 8, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant