Skip to content

tests/test_chain_remedy_callers.py: no row holds #980's unchanged --link-source reading at the old first-= split's corners (path =x, blank path, key in capitals), so four gate mutants survive #1054

Closed

Description

@brettheap

Goal: three test rows hold what #980 left unchanged in how --link-source reads a value (a keyed path that begins with =, a keyed blank path, a key typed in capitals), so each of rv980's four surviving mutants of scripts/validate-repository-naming.py fails a test.

State

#1044 (#980, landed as 82fc86752) made a --link-source key the longest chain link before an = that leaves a path after it (LinkSources._read_source, scripts/validate-repository-naming.py:401-444). Its body claims that a chain whose links hold no = is read exactly as before. That is true: rv980 compared 800 runs on the base and the head (stdout, stderr and exit code) and none differed. But the claim rests on that probe. tests/test_chain_remedy_callers.py holds the #980 rows for the new reading (:479-653) and none for the corners of the old first-= split.

What happens

Nothing is wrong in the code. rv980 wrote seven mutants of the new reading, and four survive the five closest test files (404 passed under each). Line numbers are on main:

  • R10, :439: at = typed.rfind("=", 0, at) becomes typed.rfind("=", 0, at - 1), so the scan skips an = that sits right before the current one. --link-source openXdox==x (chain openXdox,openYdox,openInk) prints link-source-unreadable: =x (--link-source openXdox=...), the key openXdox with the path =x. Under R10 it is a bare PATH, and the run adds a false line: "It was read as a PATH: openXdox is not a link of a referent chain here".
  • R1, :437: in if link is not None and typed[at + 1:]:, typed[at + 1:] becomes typed[at + 1:].strip(). --link-source 'openXdox= ' stops being keyed to the path (one blank) and becomes a bare PATH, with the same false line.
  • R14, :385: no_path tests self._read_source(typed)[1], and R14 tests [1].strip(). The same value 'openXdox= ' is then refused link-source-no-path, exit 2. On main it exits 0, as before validate-repository-naming.py: the --link-source <link>=<path> the gate offers for a chain link holding = is read as a bare PATH when typed, so it never verifies that link #980.
  • R5, :438: return link, typed[at + 1:] becomes return typed[:at], typed[at + 1:], so the link is spelled as typed and not as the chain spells it. With OPEN=Y=<tree>, the chain openXdox,open=y,openInk and a name that reads no declaration (brett-wip), the NOTE says no name classified here reads OPEN=Y's own declaration where main says open=y's.

Each one changes output. R1, R10 and R14 do it for a chain whose links hold no =, and rv980 counts those three against #1044's claim; R5 does too, with a key typed in capitals (OPENXDOX=<tree>). At drafting the gate printed the same bytes before #980 (5b41d44f, the base of #1044) and on main for openXdox==x, 'openXdox= ' and OPENXDOX=<tree>.

Direction

Add three rows to the #980 section of tests/test_chain_remedy_callers.py, which already has THREE (:200), EQUALS (:322), naming and tree. They are rv980's suggestion, and together they kill all four:

  1. A keyed path that begins with =. openXdox==x with THREE: the WARNING =x (--link-source openXdox=...) could not answer for openXdox: no such file or directory, and no "read as a PATH" line. (open=y== with EQUALS is the same row for a link holding =: = (--link-source open=y=...) could not answer for open=y.) Kills R10.
  2. A keyed blank path. 'openXdox= ' with THREE: exit 0, no REFUSED, the WARNING link-source-unreadable: (--link-source openXdox=...) (the path is one blank), and no "read as a PATH" line. Kills R1 and R14.
  3. A key typed in capitals, in a run that reads no declaration. OPEN=Y=<tree> with EQUALS and the name brett-wip: the NOTE ends reads open=y's own declaration and never names OPEN=Y. Kills R5. (OPENXDOX=<tree> with THREE kills it too, and prints the same bytes before validate-repository-naming.py: the --link-source <link>=<path> the gate offers for a chain link holding = is read as a bare PATH when typed, so it never verifies that link #980. OPEN=Y= is the row for the new reading.)

rv980's checklist line, if the reviewer checklist takes it: when a PR claims an unchanged reading, at least one test row covers each corner of the old split (a path beginning with =, a blank path, and a key typed in another case).

Optional, and the writer's call: the "It was read as a PATH" line names only the text before the first =. It is LinkSources.read_as_path (:616-626), and the text is typed.partition("=")[0] at :623. rv980's ruling is a NIT for a follow-up, not a regression: under the new reading the line is always true, since a value whose first-= text names a link is always keyed and never bare. It can still mislead, in a chain openXdox,open=y,openInk:

  • open=z=<p> says "open is not a link of a referent chain here", where the key the person typed was open=z.
  • --link-source open=y, with no path, says "open is not a link ... (links: openXdox, open=y, openInk)", where the value is a link's name and names no path.

Both print the same bytes before #980 (checked at drafting), and the list of links makes the mistake recoverable. If the line is changed, it could name each text before an = that _read_source tried, or say that a value that is a link's name names no path. Two rows hold the line's bytes where the first-= text is the only text and must keep passing: test_a_key_is_read_whole (tests/test_chain_remedy_callers.py:632-653) and test_a_path_typed_after_the_equals_sign_is_still_read (tests/test_link_source_chain_defaults.py:344-364). The twin line in validate-pins.py (PinSources.read_as_path, :1547-1561) has the same shape, and is #1053's.

Land the three rows alone: they touch no pinned copy. A change to the line touches scripts/validate-repository-naming.py, a pinned copy (COPIED_FROM_SHAPE, scripts/shape_materialize.py:439-443), so it takes one upgrade row.

Verify

  • tests/test_chain_remedy_callers.py passes alone on main with the three rows (rv980 ran it on git 2.43 and 2.47.1).
  • In a scratch worktree, apply each mutant by its one-line edit above and run that file alone. Each must fail: R10 on row 1, R1 and R14 on row 2, R5 on row 3, and each on the line it changes. At drafting the gate was run on main and on each mutant with these values and each row's output differs from main's in just that way; no test was run.
  • The three rows alone leave no new row in a root's update-shape.py check.

Found by lane 5's independent review of #1044 (#980), rv980 (#1044 issuecomment-6288391970).

Milestone: v1.2.0 (placement is lane 3's call).

Not claimed.

Lane: openreposhape-5 (openRepoShape-5)

Activity

  1. brettheap commented on Oct 20, 2026

    @brettheap
    ContributorAuthor

    CLAIMED — lane openRepoShape-5, session 50cede29-ab82-4ca3-9f67-d98e1a58c8d2@Eagle, 2026-10-20T10:51:06Z, for #1054

    Logged in lanes/log/openRepoShape-5.md at d67cf3dcad40bb4350f4b128b4cf66fea2004648.

    The three reads (lane-collision-protocol Rule 1):

    1. existing `CLAIMED —` comments on opensoft/openRepoShape#1054:
       none
    2. `gh pr list --repo opensoft/openRepoShape --state all --search "#1054"`, kept where the row or its body names `#1054`:
       none
    3. `git ls-remote --heads git@github.com:opensoft/openRepoShape` branches naming `1054` as a whole token:
       none
    
  2. added this to the v1.2.0 milestone on Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions