Repository navigation
bump-leg.py: a tag a leg with no origin holds without its commit, or through a tag it lacks, is told so, a tag of a tree or a blob it lacks is refused by name, and rows hold YAML_NOT_TEXT_RE's exponents of two digits (#1103) - #1131
Conversation
… told it is held, `peeled_target` loses the `else` nothing reaches, and rows hold shape_materialize.py's `YAML_NOT_TEXT_RE` exponents of two digits (#1103) rv1090's review of #1090 (#912) left one SHOULD and two NITs, measured on main 6da757c: - No row held an exponent of two or more digits. With `[0-9]+` cut to `[0-9]` in both exponents of `YAML_NOT_TEXT_RE` (rv1090's R2), every spelling and entry row still passed, and `.5e-10`, `.05E-12` and `.5_e-10` would be written bare, which PyYAML reads as numbers. `YAML_FLOATS` gains `.5e-10`, `.5E+10`, `.5_e-10`, `.5e10` and `.5.5e-10`, the last because only the pattern as written takes it, so each exponent is held on its own. - In a leg with no `origin`, a tag id the leg holds without its commit (which `peeled_target` gives the verdict as typed since #912) was told it "is not in the leg's own object store". The tag is: it is now told the leg holds the tag and not the commit it names, under the same code, `bump-leg-commit-not-on-remote`. - `peeled_target`'s `else: "is an annotated tag that names no commit"` was reached by nothing: a tag that names a commit, or whose peel cannot be read, has returned, and `^{}` peels through every tag. It is gone. `shape_materialize.not_a_commit` keeps the words, for `commit_on_forge`. No refusal code changes, and none is added. Closes #1103 Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideImplementationTrace tag headers when peeling is unavailable so refusals distinguish missing commits, missing intermediate tags, and tags of trees or blobs, while simplifying the unreachable
TestsAdd regression coverage for the corrected no-origin tag diagnostics, missing links and non-commit tag targets, and exercise the YAML exponent patterns with multi-digit exponents.
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Independent review of #1131 (#1103) at The PR does what #1103 asks. It changes no refusal code, no output moved that should not have, every number I re-ran matches, and every merge is clean against current main FindingsNIT 1: the new no-
NIT 2: a regex mutant of my own survives. It is the same kind of gap the issue's SHOULD closes.
NIT (body, for the coordinator): main moved after the body was written.
Checked and found trueHead and files
NIT 1 (brief item 1)
NIT 2 (brief item 2)
The SHOULD (brief item 3)
All runs went through Runs (brief item 4)
Merges
Sonar-style checks
Title and body (brief item 5)
Not verified
Checklist candidates
Scratch left
Lane: openreposhape-5 (openRepoShape-5) |
…that tag, and a tag of a tree or a blob the leg lacks is refused `bump-leg-target-not-a-commit`, read from the tags' headers; `.5.5E-10` holds the first exponent's `E` (#1103) Fix round 1 of #1131, after rv1131's review (READY, two NITs). NIT 1. The no-`origin` words for a held tag the leg cannot peel keyed on `object_kind(id) == "tag"` alone, so two rarer cases got "without the commit it names" too, and it was untrue of both: - (a) a tag of a tag whose inner tag the leg lacks, with the commit held or not. It is now told which tag it leads through that the leg lacks, and that what that tag names is not known there, under the same `bump-leg-commit-not-on-remote`. - (b) a tag, or a tag of a tag, of a tree or a blob the leg lacks. It names no commit, so `peeled_target` now refuses it `bump-leg-target-not-a-commit`, in the words a tag of a held tree or blob already had (byte for byte, measured), with or without an `origin`, since no fetch makes a tree a commit. `tag_chain_end` reads the chain from each held tag's `type` and `object` header lines, where the peel cannot be read. The issue's own case, and every other id, keeps its words: a probe of main, 9d14c29 and this head over 15 legs and 10 ids found head-vs-new differences only in (a) and (b). NIT 2. Mutant M5 (`[eE]` to `[e]` in YAML_NOT_TEXT_RE's first exponent group) survived: no row held a capital-E exponent only that group takes. `.5.5E-10` is added to YAML_FLOATS. Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Re-check of #1131 (#1103) for lane openRepoShape-5 READY at Both of rv1131's NITs are met, and I measured each:
I found four NITs. None blocks. Two are worth a small commit: the new SonarCloud issue, and a mutant of mine that survives because no row holds a chain of three tags. FindingsNIT 1: the round adds one open SonarCloud issue, python:S7494, at
NIT 2: no row holds a chain of three tags, so a walk cut to two headers survives.
NIT 3:
NIT 4 (for the coordinator): the PR's title on GitHub is still the first commit's.
Checked and found true
Head and main
rv1131's NIT 1: (a) and (b), by name
Rows
Mutants. Each was applied alone in my scratch worktree. The file was then restored, and checked by sha256 (
The three body mutants killed the same rows, in the same counts, as the body says. Gates at
Merges
The body as posted (17:25:48Z)
Not verified
Scratch left
Lane: openreposhape-5 (openRepoShape-5) |
origin holds without its commit is told it is held, peeled_target loses the else nothing reaches, and rows hold shape_materialize.py's YAML_NOT_TEXT_RE exponents of two digits (#1103)origin holds without its commit, or through a tag it lacks, is told so, a tag of a tree or a blob it lacks is refused by name, and rows hold YAML_NOT_TEXT_RE's exponents of two digits (#1103)
…n and its docstring covers a malformed tag; test_bump_leg_reachability_words.py: three held tags to a tree the leg lacks are refused by name, which a two-header walk fails (#1103) #1131's re-check (rc1131, issuecomment-6347256815: READY, 4 NITs) found three things to mend in the fix round, and this commit mends them. NIT 1, SonarCloud python:S7494 at bump-leg.py:999: the dict constructor over a generator is a dict comprehension. It gives the same dict as the line it replaces, on every list of up to three lines made of two letters, a space and a tab, and on a real tag header with a continuation line. NIT 2: no row held a chain of three tags, so a walk cut to two headers (`while True:` read as `for _ in range(2):`) survived. `TAGS_OF_NO_COMMIT` has a fourth kind, a tag of a tag of a tag of a tree, all three tags held and the tree pushed nowhere, in both runs, with and without an `origin`. NIT 3: the docstring of `tag_chain_end` says it returns `("tag", <id>)` also for a tag whose header misstates what it names, which a malformed tag has. Nothing the tool does changes. Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Re-check of #1131 (#1103) NIT round for lane openRepoShape-5 READY at FindingsNIT 1: the body's "(2486027 lists)" counts comparisons, not lists.
NIT 2: the body's "Not yet measured: SonarCloud's own analysis" can now be replaced.
Checked and trueNIT 1 (the comprehension)
The commit message's overclaim
NIT 3 (the docstring)
NIT 2 (the three-tag row)
Runs (
Added lines and merges
Body and commit
Not measured
Lane: openreposhape-5 (openRepoShape-5) |
… leaves out a pattern like `refs/heads*` that puts a branch under another name, its clauses move into `_left_out`, and the docstrings name the clashes left and the way back (#1100) (#1157) #1100 holds five follow-ups from RV1087's review of #1087 (issuecomment-6314349196). This PR does all five: 1. Take the clause-4 fix for a pattern such as `refs/heads*`, and name the clash that is left, `+refs/pull/7/head:refs/remotes/origin/main`, in the three places that list clashes. 2. Add the `WHICH_CLAUSE` row that kills R16. 3. In `bump-leg.py`, name the unforced exception and the way back, `git fetch --force origin`. 4. Write down the accepted cost where the cost is described. 5. Move `origin_fetch_refspecs`' four clauses into a helper, `_left_out`. Fix round 1 (`baa19972a`) answers the independent review rv1157/rv1157b (issuecomment-6347113460: NOT READY, 2 SHOULD, 4 NIT, no BLOCKER). It does all six. See "Fix round 1" below. The NIT round (`368b87e9b`) answers rc1157's re-check of fix round 1 (issuecomment-6352215076: READY, 3 NITs, all in this body). It adds one test row, which kills F2, the survivor of fix round 1, and corrects three statements in this body: the reason F2 was left, the longest line's length and the #1131 merge-tree line. The scripts are unchanged. See "NIT round, after the re-check of fix round 1" below. Measured by a probe on git 2.43 and git 2.47.1, with local bare remotes only. The two gits gave identical tables. For each case the probe ran: - the repository's own `git fetch origin`; - the tools' fetch, `git fetch -q --prune --no-prune-tags --refmap= <origin|path> <refspecs>`, with the refspecs from main's `origin_fetch_refspecs`, once through `origin` and once through a path; - the repository's own fetch again ("own-after"). Where own-after failed, it then ran `git fetch --force origin`, a plain fetch, and the tools' fetch followed by the repository's own fetch ("again"). Branches are `main`, `x` and `release/x` at three unrelated commits unless the case says otherwise. ``` case own main o/p own-after force, plain, again C25 +refs/heads*:refs/remotes/origin/h*, branch h/x 0 128/128 0/0 - C18b +refs/pull/7/head:refs/remotes/origin/main alone 0 128/128 0/0 - C02 release/* onto origin/*, unforced 0 0/0 1/1 0 (ref back), 0, 1 C02c same, x descends from release/x 0 0/0 1/1 0 (ref back), 0, 1 C05u release/x onto origin/x, unforced 0 0/0 1/1 0 (ref back), 0, 1 C20u tags onto origin/*, unforced, branch and tag x 0 0/0 1/1 0 (ref back), 0, 1 C32 heads/* onto origin/*-a, branches q and q-a 0 0/0 0/0 - C33 feat-* onto origin/feat-*-old, feat-1, feat-1-old 0 0/0 0/0 - ``` These match the issue's numbers. C32 and C33 exit 0 because #1087 leaves them out. The own-after failure is `! [rejected] release/x -> origin/x (non-fast-forward)`. C02b, where `release/x` descends from `x`, gives 0. So the condition is that the kept commit does not descend from the branch's, not that the two commits are unrelated. The old paragraph's words "not in line" were wrong for C02c, and the new text corrects them. Main has since moved to `961640c6` (#1155, which touched only `docs/release-notes/v1.2.0.md`). The four files this PR changes are byte-identical there, so nothing above changes. **`scripts/shape_materialize.py`** - **Clause 4** (`gives_a_branch_another_name`) now reads a source that is not under `refs/heads/` as naming branches when it is a pattern whose part before the `*` is a start of `refs/heads/`. This is how `can_name_a_tag` reads `refs/*` for tags: ``` fixed, star, _ = source.partition("*") if not source.startswith(HEADS_PREFIX): return bool(star) and HEADS_PREFIX.startswith(fixed) ``` Such a pattern's `*` also takes what the pattern leaves of `refs/heads/`. So no destination under `refs/remotes/origin/` gives a branch its own name, and every such refspec is left out. The only pattern that keeps the names, `refs/heads*:refs/remotes/origin*`, writes outside `origin/`, and clause 1 leaves it out first. The shorter starts (`refs/`, `*`) can name a tag, and clause 2 leaves them out first. - **The fix was taken only after measuring it.** A pure-function differential of main against the branch ran over 39,385 configs: 43 sources x 31 destinations, forced and unforced, negatives, pairs, doubles, and each one beside the first refspec. - The branch carries nothing that main does not. - It newly leaves out 207 distinct refspecs. Every one has a pattern source whose part before the `*` starts `refs/heads/` but neither `refs/heads/` nor `refs/tags/` (`refs/h*` to `refs/heads*`). - The helper alone (main plus `_left_out`) gives main's output on all 39,385 configs. - The fix alone and the fix plus the helper give the same output as each other on all 39,385. - **The probe of all 53 cases**, on both gits, changed no outcome outside that class: exits, own-after, force, plain and again are identical for the 46 cases outside the class, C03u, C17u and C32o included. The seven cases in the class changed: ``` case own main o/p branch o/p own-after (branch) C25 refs/heads* onto origin/h*, branch h/x 0 128/128 0/0 0/0 C25u same, unforced 0 128/128 0/0 1/1; --force 0, plain 0 C25n refs/heads* onto origin/h*, no clash 0 0/0 0/0 0/0 (pruned, written back) C25g default glob + refs/heads* onto origin/h* 0 0/0 0/0 0/0 (pruned, written back) C25b default glob + refs/he* onto origin/he*, heads/x 128 128/128 0/0 128/128 C25c refs/he* onto origin/he*, branch heads/x 0 128/128 0/0 0/0 C25h glob + refs/h* onto origin/hh/*, ref refs/hidden/1 0 0/0 0/0 0/0 (pruned, written back) ``` Wherever a tools' fetch exited 0, every remote branch's `refs/remotes/origin/<name>` held that branch's commit (all cases, both rules, both ways, both gits). A fetch that exited 128 changed no ref. - **`_left_out(source, destination)`** holds the four clauses, in their order and with their comments. `origin_fetch_refspecs` now asks it once. The SonarQube analyzer (`python:S3776`, `analyze_code_snippet`) raises nothing on an excerpt of the four functions as committed, with their docstrings dropped. With four more `if ...: continue` in the loop it reports "from 17 to the 15 allowed", so `origin_fetch_refspecs` measures 9 (it was 15). By hand, `_left_out` is 3 and `gives_a_branch_another_name` is 2. - **The docstrings:** - `gives_a_branch_another_name` no longer ends "A source not under `refs/heads/` names no branch". It names the pattern and points to WHAT CAN STILL CLASH. - The A BRANCH UNDER ANOTHER NAME paragraph names the pattern. - A new THE COST, ACCEPTED paragraph says that, after either tool runs, an unforced refspec's own fetch exits 1 on every run until `git fetch --force origin`. Where the refspec was carried, the tools' fetch exited 128 in those same states and left the ref alone. No commit is lost, and main already behaves this way for unforced tags onto the glob. - WHAT CAN STILL CLASH now names any carried refspec whose source names no branch, onto a name a branch has: the pull requests in `origin/pr/` and C18b's single name. - Fix round 1 changes three of these paragraphs and adds one (below). **`scripts/bump-leg.py`**, docstrings only. An AST comparison with the docstrings blanked shows the code is identical to main's, at both heads. - The #1066 paragraph of `_leg_fetch_refspecs` names the `refs/heads*` pattern. It adds NOT WHERE THE LEG'S REFSPEC WAS WRITTEN WITHOUT ITS `+`, with the way back, `git fetch --force origin`. - "Two ways remain" now names the second way as a source that names no branch, onto a name a branch has, in a folder or onto a single name (C18b). **`tests/test_family_bump_fetch_refspecs.py`** - New `WHICH_CLAUSE` rows (both tools' tests ask them): - `another-name-suffix`, `+refs/heads/*:refs/remotes/origin/*-old`, left out (item 2); - `another-name-heads-pattern`, `+refs/heads*:refs/remotes/origin/h*`, left out; - `another-name-heads-pattern-shorter`, `refs/he*:refs/remotes/origin/he*`, left out; - `carried-not-refs-heads`, `+refs/headsx/*:refs/remotes/origin/headsx/*`, carried; - fix round 1: `another-name-heads-pattern-refs-h` and `another-name-heads-pattern-suffix` (below). - New `BRANCH_UNDER_ANOTHER_NAME` row `heads-pattern-into-a-folder`, `+refs/heads*:refs/remotes/origin/h*`, with `x-w1066` and `h/x-w1066`. It runs end to end through `family.py bump` and through `bump-leg.py` via `origin` and via `--local-remote-dir`, with the clash premise first. - Fix round 1: `NO_BRANCH_A_PATTERN_TAKES` and its end-to-end rows (below). - NIT round: `carried-refs-h-not-a-pattern`, `+refs/h:refs/remotes/origin/hy`, carried (below). **`tests/test_bump_leg_fetch_refspecs.py`**: one docstring sentence, and in fix round 1 the leg's `NO_BRANCH_A_PATTERN_TAKES` rows. `scripts/family.py` is unchanged. Its `_origin_fetch_refspecs` paragraph "A mount whose refspec git cannot satisfy" gives examples and claims no complete list, and it stays true. One new commit, `baa19972a`, on `a35a97ea5`. No merge of main was needed: main's one new commit touches none of these files. The scripts' code is unchanged: `astsame.py` (AST with docstrings blanked) gives SAME CODE for `shape_materialize.py` against `a35a97ea5` and for `bump-leg.py` against main. **SHOULD 1 (finding 1): two mutants of the fix survived the rows.** Two `WHICH_CLAUSE` rows are added. Both tools' tests ask them (`test_family_bump_fetch_refspecs.py` holds the table, and `test_bump_leg_fetch_refspecs.py` imports it): - `another-name-heads-pattern-refs-h`, `+refs/h*:refs/remotes/origin/hh/*`, left out. This is the class's lower edge, the shortest start of `refs/heads/` that no earlier clause meets. - `another-name-heads-pattern-suffix`, `+refs/heads*x:refs/remotes/origin/h*x`, left out. Git accepts it and maps branch `x` onto `origin/h/x` and `ax` onto `origin/h/ax` (probe section B, both gits). On the pure function (`tools/rows.py`, `logs/rows.log`): ``` row main head B1 B9 F2 +refs/h*:refs/remotes/origin/hh/* carried left out carried left out left out +refs/heads*x:refs/remotes/origin/h*x carried left out left out carried left out ``` So each new row fails on main. B1 is killed by `-refs-h` and B9 by `-suffix`. F2 was the one survivor of fix round 1. The NIT round's row kills it (see "NIT round" and Mutants). **SHOULD 2 (finding 2): leaving the class out changes the answer from pin to refuse** for a commit that only a non-branch ref reaches, where the pattern takes that ref too. The three places that said nothing is lost now say so: - `shape_materialize.py`, `origin_fetch_refspecs`: LEFT OUT BY EITHER OF THE LAST TWO, NO BRANCH IS LOST TO THE ANSWER, followed by a new paragraph, BUT A COMMIT ONLY A REF THAT IS NO BRANCH REACHES IS REFUSED. It covers the pull requests onto the branches' names (left out since #946, refused on main too) and, since #1100, a ref of another namespace that a pattern from a start of `refs/heads/` takes. It says that every run of either tool refuses such a commit alike. It also says that a refspec whose source is that namespace itself is carried, and the commit is pinned through it. This is finding 2(b): one remote ref, two answers by refspec. - `shape_materialize.py`, `_left_out`'s comment: a ref that is no branch, which a pattern takes too, no longer answers for one, so a commit only it reaches is refused, as one only a tag reaches is. - `bump-leg.py`, the #1066 paragraph: "so no branch is lost to the answer", and a new paragraph, A COMMIT ONLY A REF OF ANOTHER NAMESPACE THE PATTERN TAKES REACHES IS REFUSED NOW (#1100), with the review's `heads-x-in` case and its `heads-x-out` control. New end-to-end rows pin the refusal, `NO_BRANCH_A_PATTERN_TAKES` (`+refs/heads*:refs/remotes/origin/h*` with `refs/heads-archive/old-w1100`, and `+refs/h*:refs/remotes/origin/hh/*` with `refs/hidden/old-w1100`): - `test_a_ref_a_heads_pattern_takes_answers_for_no_branch` runs `family.py bump --dry-run` and `bump`. Both refuse `member-to-local-only` in the plain words, with nothing in the holder moved and no ref of the mount moved. - `test_a_ref_a_heads_pattern_takes_answers_for_no_branch_of_the_leg` runs `bump-leg.py` via `origin` and via `--local-remote-dir`. Both runs refuse `bump-leg-commit-local-only` in the plain words, with nothing in the root moved and no ref of the leg moved. - Each first asserts its premises: the ref reaches the commit, no branch of the remote does, and no remote-tracking ref does. All 6 rows fail on main, where both runs pin (Rows failing on main). The docstrings' claims, measured by `tools/claims.py` (the tools' own code from an exported tree: `fetch_leg` end to end, then `family.py`'s reader and `_on_a_remote_branch`). It ran on main `961640c6` and on `baa19972a`, on git 2.43 and 2.47.1. The two gits gave identical output on each tree (`logs/claims-{main,head}-{243,247}.log`): ``` case refspec beside the glob ref holding C main head C25h-in +refs/h*:refs/remotes/origin/hh/* refs/hidden/1 PIN REFUSE C25h-unforced refs/h*:refs/remotes/origin/hh/* refs/hidden/1 PIN REFUSE heads-x-in +refs/heads*:refs/remotes/origin/h* refs/heads-archive/old PIN REFUSE heads-x-unforced refs/heads*:refs/remotes/origin/h* refs/heads-archive/old PIN REFUSE hidden-out +refs/hidden/*:refs/remotes/origin/hidden/* refs/hidden/1 PIN PIN heads-x-out +refs/heads-archive/*:refs/remotes/origin/h-archive/* refs/heads-archive/old PIN PIN pulls-out +refs/pull/*/head:refs/remotes/origin/pr/* refs/pull/7/head PIN PIN pulls-onto-glob +refs/pull/*/head:refs/remotes/origin/* refs/pull/7/head REFUSE REFUSE ``` - `family.py`'s answer equals `bump-leg.py`'s in every row, and so do the two readers' lists. - On the head, the tools' fetch exits 0 and prunes the class ref (`origin/hh/idden/1`, `origin/h-archive/old`). The repository's next own `git fetch origin` exits 0 and writes it back. **NIT 1 (finding 3), `bump-leg.py`'s tag paragraph.** It now says that such a ref is pruned or holds the branch of that name, and that the leg's own next fetch writes it back. It adds NOT WHERE THE LEG WROTE THAT REFSPEC WITHOUT ITS `+` and the tag's commit does not descend from the branch's: then the leg's own fetch exits 1 on that run and every one after it, until `git fetch --force origin`. Measured (probe section C, both gits, main and head alike): ``` case refspec own tool own-after --force (ref back) plain C20 +refs/tags/*:refs/remotes/origin/* 0 0 0,0 - - (origin/x back at the tag) C20u refs/tags/*:refs/remotes/origin/* 0 0 1,1 0 (back) 0 ! [rejected] x -> origin/x (non-fast-forward) C20u-desc same, the tag's commit a child of x 0 0 0,0 - - ``` **NIT 2 (finding 4).** The 109-character line is reflowed. The longest line this PR adds, over `961640c6..368b87e` in the four files, is 78 characters (`test_bump_leg_fetch_refspecs.py`'s `assert contains(bare_of(remotes), made, ref), f"premise: {ref} reaches it"`). In the scripts the longest added lines are 75 (`bump-leg.py`) and 76 (`shape_materialize.py`). `bump-leg.py`'s longest line, 81, is main's. This body said 79 until the re-check measured 78. **NIT 3 (finding 5), the overclaim in `gives_a_branch_another_name`.** It no longer says each such refspec puts a branch under another name. Where the part before the `*` is all of `refs/heads` and the destination's `*` comes right after a `/`, git refuses the name it gives each branch, and the fetch writes no branch. The docstring now says this, and says that the pattern still takes the refs of another namespace, pointing to the new paragraph. Probe section B, both gits: `+refs/heads*:refs/remotes/origin/h/*` with branches `main`, `x` and `ax` exits 0, says `error: * Ignoring funny ref 'refs/remotes/origin/h//x' locally` (and the same for `h//main` and `h//ax`), and writes only `origin/h/foo/x` for `refs/headsfoo/x`. **NIT 4 (finding 6), the stale merge-tree list.** Merge-trees were re-run at `baa19972a` against main and every open PR touching these four files (Merge-trees). One new commit, `368b87e9b`, on `baa19972a`, in `tests/test_family_bump_fetch_refspecs.py` only: one `WHICH_CLAUSE` row, a sentence in the table's comment and one in its test's docstring. The scripts are byte for byte `baa19972a`'s (`git diff --name-only baa1997 368b87e` names that one file). The row is `pytest.param("+refs/h:refs/remotes/origin/hy", True, id="carried-refs-h-not-a-pattern")`. `WHICH_CLAUSE` is one table, which `tests/test_bump_leg_fetch_refspecs.py` imports and asks of `_leg_fetch_refspecs`, so the one row is two test ids, one in each file, and both tools' tests ask it. **NIT 1 (finding 1): the reason given for leaving F2 was wrong, and the row it said could not be added can be.** The first body said F2 differs from this head only on a source that is no pattern and is a start of `refs/heads/` (`refs/`, `refs/h`, ... `refs/heads`), that no such full name can be a ref beside the remote's branches, and that a row for it would pin an arbitrary answer. Measured by `tools/claims.py` (the tools' own code from an exported tree: `fetch_leg` end to end, then `family.py`'s reader and `_on_a_remote_branch`), on main `961640c6`, on this head and on this head with F2 applied, on git 2.43 and 2.47.1 (`logs/claims-{main,head,f2}-{243,247}.log`; the two gits gave identical output on each tree): ``` case refspec beside the glob ref holding C main head F2 refs-h-no-pattern +refs/h:refs/remotes/origin/hy refs/h PIN PIN REFUSE refs-head-no-pattern +refs/head:refs/remotes/origin/hz refs/head PIN PIN REFUSE refs-heads-no-pattern +refs/heads:refs/remotes/origin/hx refs/heads the remote cannot hold refs/heads and refs/heads/main hidden-out +refs/hidden/*:refs/remotes/origin/hidden/* refs/hidden/1 PIN PIN PIN C25h-in +refs/h*:refs/remotes/origin/hh/* refs/hidden/1 PIN REFUSE REFUSE ``` - Only `refs/heads` itself cannot be such a ref: `git update-ref` refuses it with "cannot process 'refs/heads/main' and 'refs/heads' at the same time" (exit 128, no ref made). `refs/h` and `refs/head` are made beside `refs/heads/main` with exit 0. - With `+refs/h:refs/remotes/origin/hy` and a commit C that only `refs/h` reaches, main's code and this head's carry the refspec and PIN C. Under F2 both tools leave it out and REFUSE C (`bump-leg-commit-local-only`, `family_answer=False`): the tools' fetch prunes `origin/hy`, which the repository's own fetch had written, and the next own fetch writes it back. - So this head's answer is not arbitrary. It is the rule ANY OTHER SOURCE NAMES NO BRANCH, the same as `hidden-out`'s: a source that is no pattern takes the ref of its name. Where the remote has no ref of that name, git completes the name instead: with a branch named `refs/h` (the ref `refs/heads/refs/h`) beside `hy`, the tools' fetch line exits 128 with `Cannot fetch both refs/heads/hy and refs/heads/refs/h to refs/remotes/origin/hy`, on git 2.43.0 and 2.47.1, on main as here. That is the WHAT CAN STILL CLASH shape (lane 5's re-check, issuecomment-6371455922, NIT 1). - On the pure function (`tools/rows.py`, `logs/rows.log`, `origin_fetch_refspecs` alone): ``` row main head B1 B9 F2 +refs/h:refs/remotes/origin/hy carried carried carried carried left out +refs/head:refs/remotes/origin/hz carried carried carried carried left out +refs/h*:refs/remotes/origin/hh/* carried left out carried left out left out +refs/heads*x:refs/remotes/origin/h*x carried left out left out carried left out ``` - **The new row passes on main by design,** as `another-name-suffix` does, because main's clause 4 (`gives_a_branch_another_name`) answers `False` for every source not under `refs/heads/` (`961640c6`, `scripts/shape_materialize.py` line 1704), and `refs/h` meets no earlier clause. It passes on this head. It kills F2: with F2 applied to `scripts/shape_materialize.py`, `-k carried-refs-h-not-a-pattern` over both test files gives 2 failed (`test_which_clause_leaves_each_refspec_out[carried-refs-h-not-a-pattern]` and `test_which_clause_leaves_each_refspec_out_of_the_legs_fetch[carried-refs-h-not-a-pattern]`), and over the 90 rows of Mutants, 2 failed and 88 passed. The runs are in `logs/mut-summary.log`: the row alone, 2 passed on main's code (`logs/mut-ROW-main-code.log`), 2 passed on this head (`logs/mut-ROW-head.log`), 2 failed under F2 (`logs/mut-ROW-F2.log`). - The Mutants section below replaces the F2 sentence with this result. **NIT 2 (finding 2): the line length.** Fix round 1's NIT 2 sentence now says 78, and names the scripts' 75 and 76 (above). The new commit adds lines of at most 76 characters, all ASCII, and no assert that joins conditions with `and` or `or`. **NIT 3 (finding 3): the #1131 merge-tree line.** #1131's head is `55c30f20` now (its NIT round), not `a4beec9e`. `git merge-tree --write-tree 55c30f2 368b87e` exits 0 with tree `e036c02d6a12`; at `baa19972a` it gave `bd6ebc5cd3af`, as the re-check measured. See Merge-trees. Main's production code (`961640c6`: `scripts/shape_materialize.py` and `scripts/bump-leg.py`) was put in the worktree under this head's two changed test files. Then the committed files were put back, with sha256 and dirty=0 checked (`logs/mut-MAIN-code-head-tests.log`). The result is **17 failed, 106 passed** at `baa19972a`, and **17 failed, 108 passed** at `368b87e9b` (`logs/mut-MAIN-code-head-tests.log`, 125 rows): the NIT round's 2 rows pass on main, by design. The failures are exactly the rows the fix decides: - `WHICH_CLAUSE` `[another-name-heads-pattern]`, `[-shorter]`, `[-refs-h]` and `[-suffix]`, in both files: 8. Each is carried on main ("Left contains one more item"). - `test_a_branch_put_on_another_branchs_name_fails_no_fetch[heads-pattern-into-a-folder]` (member), and `..._of_the_leg[origin-heads-pattern-into-a-folder]` and `[local-remote-dir-heads-pattern-into-a-folder]` (leg): 3. The fetch exits 128 on main, so the runs give the failed-fetch note. - Fix round 1's `NO_BRANCH_A_PATTERN_TAKES` rows: `test_a_ref_a_heads_pattern_takes_answers_for_no_branch[heads-pattern]` and `[refs-h-pattern]` (member), and `..._of_the_leg[origin-heads-pattern]`, `[origin-refs-h-pattern]`, `[local-remote-dir-heads-pattern]` and `[local-remote-dir-refs-h-pattern]` (leg): 6. On main the pattern is carried, so the real run pins the commit instead of refusing it. The first round's 7 are among these. Fix round 1 adds 10: `-refs-h` and `-suffix` in both files, and the 6 end-to-end rows. `another-name-suffix` and `carried-not-refs-heads` pass on main by design. They kill mutants (below). NIT round, on `368b87e9b` with git 2.43, through `tools/mut.sh` (`logs/mut-summary.log`). The scripts are `baa19972a`'s, so these are fix round 1's mutants over the NIT round's tests. Each mutant was applied alone to the committed `scripts/shape_materialize.py`. The runs covered both changed test files with `-k "which_clause or another_branchs_name or heads_pattern_takes"` (90 rows, 35 deselected). After each run the committed files were put back, and their sha256 and dirty=0 were checked: 9 of 9 restores in the script matched (`shape_materialize.py` `16c6ec378c36...`, `bump-leg.py` `8f4af9c5e78c...`). ``` mutant failed result main's code (961640c) under these two files, all 125 rows 17 the rows above (108 passed) F2 the fix's `bool(star) and` dropped 2 killed (carried-refs-h-not-a-pattern x2) B1 the class drawn from `refs/he` up (`refs/h*` carried) 5 killed (-refs-h x2, refs-h-pattern x3) B9 the fix only where the `*` ends the source 2 killed (-suffix x2) B4 the fix undone (`return False` in its place) 17 killed (every row the fix decides) ``` F2 is killed now. At `baa19972a`, with 88 rows, it survived (88 passed); B1 (5 failed), B9 (2) and B4 (17) gave the same counts as here. The earlier sentence said F2 differs from this head only on a source that is no pattern and is a start of `refs/heads/`, and that no such full name can be a ref beside the branches. The first half holds: over the 39,385-config corpus F2 differs from this head only there (`refs/`, `refs/h`, ... `refs/heads`). The second half held for `refs/heads` alone. `refs/h` and `refs/head` are refs a remote can hold beside its branches, and the head carries `+refs/h:refs/remotes/origin/hy` and pins a commit only that ref reaches, where F2 refuses it (NIT round, NIT 1). The row `carried-refs-h-not-a-pattern` holds that answer. At `a35a97ea5`, whose code this head keeps, the first round measured these over `WHICH_CLAUSE` in both files (66 rows then): ``` mutant failed result R16 clause 4 compares only the part before the `*` 2 killed (another-name-suffix x2); on main's own rows it survives (102 passed) R17 clause 4 compares only the part after the `*` 8 killed (the four another-name rows x2) F3 only `refs/heads*` itself, not shorter starts 2 killed (another-name-heads-pattern-shorter x2) F4 any pattern starting `refs/h` (too broad) 2 killed (carried-not-refs-heads x2) F5 every pattern not under refs/heads/ left out 6 killed (carried pulls, not-refs-tags, not-refs-heads x2) H1 _left_out's OUTSIDE clause removed 2 killed H2 _left_out's tag clause removed 14 killed H3 _left_out's branches'-glob clause removed 2 killed (branches-glob-pull-requests x2) H4 _left_out's last clause removed 14 killed H5 `if _left_out(...)` negated 60 killed ``` Every run went through `run9.sh` on the commit `368b87e9b` (dirty=0 in each START line): gh stub first on PATH, a bogus token, `GIT_SSH_COMMAND=false`, `GIT_CONFIG_GLOBAL=/dev/null`, and `EMAIL` set. - git 2.43, the three files that name the functions (`test_family_bump_fetch_refspecs.py`, `test_bump_leg_fetch_refspecs.py`, `test_bump_leg_reachability_words.py`): **159 passed** (`logs/g243.log`; 157 at `baa19972a`, and the NIT round's 2 rows). - git 2.47.1 (a git 2.47.1 build first on PATH; `git --version` printed `git version 2.47.1` in the log): the same three files: **159 passed** (`logs/g247.log`). - git 2.43, `test_path_guard_hygiene.py`, `test_pinned_copy_text.py` and the trio (`test_repo_hygiene.py`, `test_agents_md_codes.py`, `test_cli_reference.py`): **306 passed** (`logs/hyg243.log`, the same count as at `baa19972a`). `git merge-tree --write-tree <target> 368b87e`, run at 2026-10-23T12:06:38Z against main and the head of every open PR that touches one of the four files (55 open PRs listed at 12:06:22Z; `logs/merge-trees.log`). Each exits 0 with no conflict: - `origin/main` `961640c6`: tree `19c8442d96b0`; - #1104 `de89aa81` (`shape_materialize.py`): tree `b76c36be588a`; - #1105 `b28f40de` (`shape_materialize.py`): tree `5f2a8b3706e4`; - #1118 `2307036a` (`shape_materialize.py`): tree `0c21c6b39607`; - #1131 `55c30f20` (`bump-leg.py`; its head moved from `a4beec9e` in its NIT round): tree `e036c02d6a12`; - #1160 `0068f5af` (`shape_materialize.py`): tree `bce90eb65c6c`. The head's own tree is `351a9b73cda8`. Main is still `961640c6`: its one commit past `038dafba`, #1155, touches only `docs/release-notes/v1.2.0.md`. - C18b, a source that names no branch onto one branch's name, still clashes: own 0, tools 128. Which names the remote's branches have is not known from the refspecs. Leaving out every such refspec would also leave out the pull requests, which #946 carries on purpose. The docstrings now say so. The coordinator adds it and C25 to #1027's list. - Leaving out the `refs/h*` class prunes what such a refspec kept until the repository's next own fetch, as for #1066's cases (C25n, C25g, C25h). An unforced one held under a branch's name costs the non-fast-forward of THE COST, ACCEPTED (C25u). - Leaving it out also changes the answer for a commit that only a non-branch ref the pattern takes reaches, from pin to refuse (fix round 1, SHOULD 2). This is the rule #613 and #1037 keep for a commit only a tag reaches: a bump pins only what a branch reaches. Every run gives the same answer, so a dry run and the real run after it never differ. Pinning such a commit instead would be a design question, not part of this fix. Review: lane 5's independent review (issuecomment-6347113460) and its re-check, READY at `baa19972` (issuecomment-6352215076); then rc1157b, lane 5's independent sonnet re-checker, which wrote none of #1157: READY at `368b87e9`, the head that lands (issuecomment-6371455922), with no MUST or SHOULD; its wording NIT is corrected in the body. CI (Brett Heap's ruling 27): H0 is the head 368b87e itself, which passed tests, tests-git-bash and SonarCloud; main has touched none of its files since its base 038dafb; and the local gate passed on its predicted squash tree 44b3b5c over main b055bb7: the bump_leg_fetch_refspecs, family_bump_fetch_refspecs, repo_hygiene, agents_md_codes, cli_reference sets, 296 passed. Landed on Brett Heap's word of 2026-10-23T13:10:26Z, relayed to every lane by lane 3 (#473 issuecomment-6365697123): "merge v1.2.1 PRs once reviewed and green". Closes #1100 Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
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 6 days and 6 hours by commenting |
…ject.py`'s argv `--id` the reason that holds (`match`, then validate-manifest.py refuses a line feed), a row proves it, and the guard says a new reader needs a row, `fullmatch` too (#1243) `tests/test_value_pattern_line_break.py` says only what is true of the readers of `PROJECT_ID_RE`: no row of `ID_READERS` gives a reason that does not hold for the tool it names, and the guard `test_every_reader_of_the_id_pattern_is_in_the_table` tells the author of a new reader, in its docstrings and in its failure message, that the one way to pass is a row in `ID_READERS`, whichever method the reader calls. This is #1229, the follow-up of #1166 (#1139): rc1166b's NIT 1, NIT 2 and NIT 4 (#1166 issuecomment-6371408915). Wording, one assert message and one row, in the test file only. No production file changes. **Restacked onto main after #1166 landed** as `105b5da` (2026-11-08T14:49Z). This branch was built on #1166's head `5102b429` (branch `fix/1139-anchored-patterns`), with one commit, `f92338ef6`. The restack merges, never rebases: 1. #1166's final head, `5c8afefd` (its update-branch refresh). 2. Its squash `105b5da` on main, keeping THIS branch's side (`ours`) for `tests/test_value_pattern_line_break.py`. That file is the one add/add conflict, and the squash's side would drop every #1229 edit without a marker. 3. Main, which had not moved past the squash, so this merge was a no-op. - **The head:** `b80214b7f`. Its diff against main is the one file, 56 lines added and 16 removed. - **Checked by lane 5's `restack3.sh`:** its changed lines equal `f92338ef6`'s against that commit's base, and `git merge-tree --write-tree` with main is clean. 1. **A false reason (rc1166b NIT 2).** The module docstring's "LEFT AS THEY ARE" bullet and the `ID_READERS` rows said every argv `--id` of `scaffold-project.py`, `family.py init` and `adopt-project.py` "holds the id to `checked_value` after the pattern". That holds for the first two, and `tests/test_derived_id_refusals.py`'s `TOOLS` pins it for those two only. `adopt-project.py` never calls `checked_value` on `--id`: `_refuse_bad_id` asks `PROJECT_ID_RE.match(project_id)` of the id `plan` takes, passed or derived, and `args.id` reaches no `checked_value` call (`git grep -n 'args\.id\b' -- adopt-project.py`; its calls are for `--project`, the tracking branch, `--pin-owner`, the leg paths and `--org`). A line feed is still refused, by another door, the project's own `validate-manifest.py`. 2. **"`fullmatch` is enough" (rc1166b NIT 1).** Both docstrings said a new reader "asks with `fullmatch`, or it is listed in `ID_READERS`". The guard is `assert id_pattern_calls() == expected`, a dict equality over every (module, method), exact both ways, so a new `fullmatch` reader fails it too, and so does a listed reader taken away. The assert had no message, so the author of a sibling PR saw only a dict diff. 3. **The guard reads spellings (rc1166b NIT 4, optional).** `_ID = PROJECT_ID_RE` then `_ID.match(value)` passes it, and so does `from repo_shape import PROJECT_ID_RE as X`. Nothing in #1166 says that is false, and nothing says it. All in `tests/test_value_pattern_line_break.py`. - **Item 1.** The module docstring's bullet names `scaffold-project.py` and `family.py init` only, with `TOOLS` as the proof, and a new bullet says `adopt-project.py`'s argv `--id` is NOT held to `checked_value`: `plan` asks it with `match` and calls `checked_value` on `--project` and never on the id, so `northwind\n` passes the pattern, and the project's own `validate-manifest.py` refuses it as `adopt-manifest-invalid` naming `--id`, in `manifest-id`'s words, exit 2, no plan written (the row below, "measured for `plan` only"). The `adopt-project.py` row of `ID_READERS` begins with that reason in place of "argv `--id`, as above". The `scripts/family.py` and `scaffold-project.py` rows are as they were. - **Item 1's optional row.** `test_adopt_plan_names_a_passed_id_that_ends_in_a_line_break_as_an_id` runs `adopt-project.py plan --id` with a trailing line feed (conftest's `write_plan` over its `source_repo`, one `plan` run of about a second) and asserts exit 2, the first stderr line `REFUSED adopt-manifest-invalid: --id is 'northwind\n', which the project's own validate-manifest.py refuses: manifest-id: id is 'northwind\n'; it must match ^[a-z0-9][a-z0-9-]*$.`, and that no plan file was written. It also imports `write_plan` from `conftest`. `--id` and `--id=...` gave the same lines when measured. - **Item 2.** Both docstrings drop "it asks with `fullmatch`, or": a new reader, `fullmatch` included, is listed in `ID_READERS` with the reason a line feed cannot reach it, or the test fails; the table is exact both ways, so a reader taken away loses its row. The comment above `ID_READERS` says a reader added later needs its row whichever method it calls. - **Item 3.** The assert carries a message: "`ID_READERS` lists every call of a method of `PROJECT_ID_RE` outside `tests/`, `fullmatch` included, by module and method. Add, change or drop the row of the module and method the diff below shows, and give a `match` or `search` reader the reason a line feed cannot reach it." Pytest prints it above the dict diff. - **Item 4 (aliases).** Left as it is, and said: both docstrings say the guard reads spellings (a call whose receiver is the name `PROJECT_ID_RE` or an attribute of that name) and stops there, with the two aliases named as outside it. S1 to S3 below measure exactly that. `git grep -n 'checked_value' -- tests/test_value_pattern_line_break.py` now shows the `checked_value` reason only against `scaffold-project.py` and `scripts/family.py` (lines 43, 171, 173); the module docstring's `adopt-project.py` bullet and the `adopt-project.py` row name `validate-manifest.py` and `adopt-manifest-invalid` for it, and say `adopt-project.py` is not held to `checked_value`. **The new row fails on main (`50869f384`).** The test file is #1166's, so it was copied from this head onto a detached worktree at main, and only the new row was run (git 2.43.0): it fails, because on main the manifest validator's `id` pattern still takes `northwind` and a line feed (`$` matches before the line feed), and the line feed is refused only through the topic: ``` E AssertionError: REFUSED adopt-manifest-invalid: --id is 'northwind\n', which the project's own validate-manifest.py refuses: manifest-topic: topic is 'xf-project-northwind', expected 'xf-project-northwind\n' derived from id 'northwind\n'. E - manifest-id: id is 'northwind\n'; it must match ^[a-z0-9][a-z0-9-]*$. E + manifest-topic: topic is 'xf-project-northwind', expected 'xf-project-northwind\n' derived from id 'northwind\n'. ``` So the row holds the claim that `manifest-id` is the refusal, which is #1166's own change to `validate-manifest.py`, and it passes at #1166's head plus this commit (31 passed below). That is why this PR was stacked on #1166 until #1166 landed. Each applied alone to a scratch worktree (detached at `f92338ef6`), run, restored by writing the original bytes back, and the sha256 compared (`restored=True` each time, `git status` clean after each). Production files only; the test file is not touched by a plant. Node ids: the guard is `test_every_reader_of_the_id_pattern_is_in_the_table`, the row is the new one above. Baseline before any plant: both pass (2 passed). | plant | file | what | result | | --- | --- | --- | --- | | G1 | `update-shape.py` | `def _rc_probe(value): return PROJECT_ID_RE.fullmatch(value)` appended | guard fails, its message names `ID_READERS`; `Left contains 1 more item: {('update-shape.py', 'fullmatch'): 1}` | | G2 | `update-shape.py` | the same with `.match` | guard fails, message names `ID_READERS` | | G3 | `update-shape.py` | the same with `.search` | guard fails, message names `ID_READERS` | | G4 | `adopt-project.py` | `if False:` in place of the plan-`id:` test at line 6791 | guard fails, message names `ID_READERS`; `{('adopt-project.py', 'match'): 2} != {('adopt-project.py', 'match'): 3}` | | R1 | `adopt-project.py` | `fullmatch` in place of `match` at line 5140 (`_refuse_bad_id`) | guard fails (names `ID_READERS`) and the row fails: `REFUSED adopt-bad-id: --id 'northwind\n' must match ^[a-z0-9][a-z0-9-]*$` | | R2 | `adopt-project.py` | `checked_value('--id', project_id)` added before line 5140 | row fails: `REFUSED unsafe-value: --id is 'northwind\n', which is not a value this tool will put on a git or gh command line` | | R3 | `adopt-project.py` | `if False:` in place of `if values is not None:` in `_refuse_unholdable_values`, so no manifest check | row fails: the plan is written, exit 0 | | S1 | `update-shape.py` | `_ID = PROJECT_ID_RE` then `_ID.match(value)` | guard PASSES (1 passed): an alias is outside it, as the docstrings now say | | S2 | `update-shape.py` | `from repo_shape import PROJECT_ID_RE as X` then `X.match(value)` | guard PASSES (1 passed) | | S3 | `update-shape.py` | `PROJECT_ID_RE.match(value)` in a comment and in a string | guard PASSES (1 passed): not counted, as intended | No mutant survives: G1 to G4 and R1 to R3 each fail where they should. R2 shows the new row also fails if `adopt-project.py` ever does hold the id to `checked_value`, which makes the module docstring's sentence false, so whoever adds that call is sent to the wording. All through the lane's runner `run9.sh` (stub `gh` first on PATH, `GIT_SSH_COMMAND=false`, `GIT_CONFIG_GLOBAL=/dev/null`, `EMAIL` exported), on the committed head `f92338ef6` with `dirty=0`, Python 3.12.3, `git --version` printed into each log. - `tests/test_value_pattern_line_break.py`: 31 passed on git 2.43.0 (25.78 s) and 31 passed on git 2.47.1 (31.21 s), the 30 of #1166's head plus the new row. - The file plus the hygiene five (`test_pinned_copy_text.py`, `test_path_guard_hygiene.py`, `test_repo_hygiene.py`, `test_agents_md_codes.py`, `test_cli_reference.py`): 337 passed on git 2.43.0 (111.39 s) and 337 passed on git 2.47.1 (264.87 s), that is 31 and the five's 306. - `git diff --check` is clean. The diff touches only the test file, adds no line over 79 columns (the file's own width) and no non-ASCII line, and the file has no `assert` that joins conditions with `and` or `or` (an `ast` scan for a `BoolOp` in any assert: none). **At the restack (2026-11-08):** main is `105b5daf9`, #1166's squash, and the head is `b80214b7f`. `git merge-tree --write-tree origin/main HEAD` is clean, and the diff against main is the test file alone. The rest of this section is from the first push. Main `50869f384` (re-fetched just before the push), this head `f92338ef6`: - `git merge-tree --write-tree origin/main HEAD`: clean (exit 0). - `git merge-tree --write-tree 5102b42 HEAD` (#1166's head): clean (exit 0). - For the stack as a whole, `git merge-tree --write-tree origin/main 5102b42` (#1166 alone with main): clean (exit 0). The merge base of main and #1166's head is `038dafba9`. Of the 51 open PRs (`gh pr list --json number,headRefName,headRefOid,files`), only #1166 touches `tests/test_value_pattern_line_break.py`. Not checked: whether a PR that touches `adopt-project.py` (26 do) adds or removes a call of a method of `PROJECT_ID_RE`; that is the guard's own job once #1166 lands, and #1166's body found none among the open PRs. A PR that makes `plan` hold `--id` to `checked_value` would fail the new row (R2), which is the row doing its work. Review: rv1243, lane 5's independent sonnet reviewer, which wrote none of #1243: READY at `f92338ef`, H0 of this landing before its restack (issuecomment-6385593825), with no MUST; its one SHOULD (the body's restack sentence) is fixed in the body, and its four NITs are optional. The restack's changed lines equal the reviewed ones, and lane 3 placed it on v1.2.1 right after #1131 (#473 issuecomment-6723002632). CI (Brett Heap's ruling 27): H0 f92338e passed tests, tests-git-bash and SonarCloud; the head b80214b merges #1166's final head, then #1166's squash 105b5da (the branch's own side for #1166's files), then main, with tests, tests-git-bash and SonarCloud green; and the local gate passed on its predicted squash tree 372f767 over main 5926ccc: the value_pattern_line_break, repo_hygiene, agents_md_codes, cli_reference sets, 202 passed. Landed on Brett Heap's word of 2026-10-23T13:10:26Z, relayed to every lane by lane 3 (#473 issuecomment-6365697123): "merge v1.2.1 PRs once reviewed and green". Closes #1229 Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…says a full name that is no pattern takes the one ref git completes it to, which may be a branch, where it said any other source names no branch (#1238) (#1244) #1238 is a wording NIT that the review of #1235 (#1230) found in the code: NIT 4 of rv1235's first review, and NIT 2 of its re-check. The docstring of `gives_a_branch_another_name` in `scripts/shape_materialize.py` ended "ANY OTHER SOURCE NAMES NO BRANCH", but git completes a source that is a full name and no pattern, and completes `refs/h` to the branch `refs/heads/refs/h` where the remote has one. The sentence now says what git does with such a source, as an open list ("such as"), and where the clash is. A reader of the docstring is no longer told that a source outside `refs/heads/` is never a branch. Wording only, in one docstring of one file: 9 lines added, 3 removed. No code, no row, no other file. The file's syntax tree with every docstring dropped is the same as main's. The sentence is at `scripts/shape_materialize.py:1720-1723`, the end of the docstring of `gives_a_branch_another_name` (line 1671): ``` 1720: BRANCH REACHES). A shorter start, such as `refs/*` or `*`, can name a 1721: tag, and that clause has left it out first. ANY OTHER SOURCE NAMES NO 1722: BRANCH, though a refspec from one can still put a ref on a branch's 1723: name: `origin_fetch_refspecs` says where (WHAT CAN STILL CLASH). ``` It is true of a source in another namespace such as `refs/pull/7/head`. It is not true of every other source. A source that is a full name and no pattern takes the one ref git completes the name to on the remote: for `refs/h`, `refs/h` itself, the branch `refs/heads/refs/h`, the tag `refs/tags/refs/h`, `refs/refs/h` or `refs/remotes/refs/h`, and where several exist git takes the exact ref over a tag over a branch. Where the remote has the branch, the source is a branch. The code is right: `gives_a_branch_another_name("refs/h", ...)` is False, because the source has no `*` and does not start with `refs/heads/`, so the refspec is carried. What fails afterwards is the clash the docstring's own next clause names. Only the words were untrue. Measured on this branch's base, with git 2.43.0 (`/usr/bin/git`) and git 2.47.1, which gave the same answers on every row. Local bare remotes, `GIT_CONFIG_GLOBAL=/dev/null`, `GIT_CONFIG_NOSYSTEM=1`, `GIT_SSH_COMMAND=false`. rv1235's probe scripts were run again here (copied unchanged, new output folders), and their logs are identical to rv1235's. The fetch was `+refs/heads/*:refs/remotes/origin/*` plus the refspec of the row, and the remote had `main` and `hy`: ``` the remote has refspec result branch refs/heads/refs/h +refs/h:refs/remotes/origin/hy 128 Cannot fetch both refs/heads/hy and refs/heads/refs/h to refs/remotes/origin/hy tag refs/tags/refs/h +refs/h:refs/remotes/origin/hy 128 Cannot fetch both refs/heads/hy and refs/tags/refs/h to refs/remotes/origin/hy exact ref refs/h (update-ref) +refs/h:refs/remotes/origin/hy 128 Cannot fetch both refs/heads/hy and refs/h to refs/remotes/origin/hy refs/refs/h or refs/remotes/refs/h +refs/h:refs/remotes/origin/hy 128, the same message naming that ref each of those five refs, alone onto origin/not-a-branch 0, holds that ref's commit branch refs/heads/refs/h onto origin/refs/h 0 (the branch's own name: no clash) refs/pull/7/head onto origin/main 128 Cannot fetch both refs/heads/main and refs/pull/7/head to refs/remotes/origin/main branch refs/heads/main onto origin/main 0 ``` So a name with no `*` can be a branch that its spelling does not show, and the clash is a ref that is not that branch put on a branch's name, which is what the second half of the sentence has to say. "ANY OTHER" was wrong in a second way: a carried refspec from a source that IS the branch does not clash onto that branch's own name. The docstring's last sentence, now lines 1721-1729: ``` tag, and that clause has left it out first. ANY OTHER SOURCE TAKES NO BRANCH BY A PATTERN, and a full name that is no pattern, such as `refs/h`, takes the one ref git completes it to on the remote: that ref itself, or one such as the branch `refs/heads/refs/h` or the tag `refs/tags/refs/h` (measured, git 2.43 and 2.47.1). Such a name can stand for a branch its spelling does not show, so it is not left out here. A refspec from any such source can still put a ref on a branch's name, if the ref is not that branch itself: `origin_fetch_refspecs` says where (WHAT CAN STILL CLASH). ``` - **"Takes no branch by a pattern"** holds for every source this sentence covers. Git matches a pattern against each ref's whole name, and a ref under `refs/heads/` starts with the part before the `*` only where that part is a start of `refs/heads/` or is under it, the two cases the function reads before this sentence. - **The list is open** ("one such as"): it names the exact ref, the branch and the tag, and the rows above show two more completions (`refs/refs/h`, `refs/remotes/refs/h`), so no reader takes the three as all. - **"So it is not left out here"** is what the code does: `gives_a_branch_another_name` is False for `refs/h` onto `origin/hy`, onto `origin/refs/h`, for `refs/pull/7/head` onto `origin/main`, and for the pattern `refs/pull/*/head`, and `origin_fetch_refspecs(["+refs/h:refs/remotes/origin/hy"])` returns it carried. It is True for `refs/heads/main` onto `origin/hy`, as before. - **"If the ref is not that branch itself"** is the condition the rows show: the clash is two different refs on one name, a branch's. The checks the issue's Verify lists: - **The diff is only that docstring.** The diff has one hunk, inside the docstring, which spans lines 1672-1724 on main and 1672-1730 here. The file's tree with every docstring dropped (module, class and function: 168 each side) is equal on main (`50869f384`) and at this head under `python3 -I`, `ast.dump`: 380120 characters of dump on both sides. A control that renames `gives_a_branch_another_name` on the head side gives 380121 and DIFFERENT, and the same trees with the docstrings kept differ (520956 against 521366), so the check can fail. Python 3.12.3. The check compares two dumps made by one interpreter and asserts no text that depends on the version. - **`git grep -n 'ANY OTHER SOURCE NAMES NO$' -- scripts/`** finds nothing at this head. On main it finds `shape_materialize.py:1721`. With any case and no line end, `git grep -n -i 'other source names no'` also finds nothing here, and nothing but that line on main. - **No added line is over 79 columns or holds a non-ASCII character.** 9 lines are added, the longest is 75 columns, and none has a tab or trailing space. The file's own width is unchanged: 20 lines over 79 columns (longest 118) and 90 lines with a non-ASCII character, as on main, none of them mine. - **No test, `README.md`, `docs/` page or `AGENTS.md` line pins the sentence.** The issue's grep holds at this head. The issue asks whether `shape_materialize.py:1874` (now 1880, in the docstring of `origin_fetch_refspecs`) and `bump-leg.py:1165` (in `_leg_fetch_refspecs`) want the same change. Both paragraphs were read, and both are left as they are: ``` shape_materialize.py:1880: WHAT CAN STILL CLASH is a refspec carried whose source names no branch, bump-leg.py:1165: whose source names no branch, onto a name a branch of the remote has ``` 1. **They define a shape by its examples and say nothing about what git completes a name to.** Each goes on at once to the pull requests (`origin/pr/` where a branch `pr/7` is, and `+refs/pull/7/head:refs/remotes/origin/main` beside a branch `main`), and neither names `refs/h`. They were not the sentence that told a reader a source is never a branch. 2. **"Names no branch" is spelling there, as it is in the sentence the function's docstring opens with.** "A SOURCE UNDER `refs/heads/` NAMES BRANCHES" is true by spelling too: `can_name_a_tag`'s docstring measures that `+refs/heads/zz` took the tag `refs/tags/refs/heads/zz` where the remote had no branch `zz`. The sentence changed here now says outright that the spelling of a name with no `*` does not show what it stands for, so the docstring that defines the class carries the caveat the two borrowers need. 3. **The mechanism they state is the one that fails `refs/h`:** "wherever a name it writes is a branch's". The `origin_fetch_refspecs` paragraph ends with why such a refspec is carried at all: "Which names the remote's branches have is not known from its refspecs, and left out, what such a refspec keeps would be pruned on every run". That holds for `refs/h` word for word. 4. **The cost of leaving them is one reading:** a reader who takes "names no branch" in git's sense meets the single case `refs/h` beside a remote branch `refs/heads/refs/h`. If the review wants a word there, it is a few words in each place, and no open PR touches either paragraph (merge-trees below), so it can be a fix round. None of either. No row is added or changed and no behaviour moved: the syntax tree outside the docstring is main's, so there is nothing for main to fail and nothing to mutate. `gives_a_branch_another_name` returns what it returned for every case in the table above (called in process at this head; the syntax tree outside the docstrings is main's). All through the lane's runner `run9.sh` (stub `gh` first on PATH, `GIT_SSH_COMMAND=false`, `GIT_CONFIG_GLOBAL=/dev/null`), on the committed head `36ec589a6` with the tree clean (`dirty=0`), as one run of seven files at a time: `tests/test_pinned_copy_text.py`, `tests/test_path_guard_hygiene.py`, `tests/test_repo_hygiene.py`, `tests/test_agents_md_codes.py`, `tests/test_cli_reference.py` (the hygiene five), `tests/test_family_bump_fetch_refspecs.py` and `tests/test_bump_leg_fetch_refspecs.py`. - **The seven files, git 2.43.0** (`/usr/bin/git`): 504 passed in 469.44s (47 `test_agents_md_codes.py`, 68 `test_bump_leg_fetch_refspecs.py`, 23 `test_cli_reference.py`, 57 `test_family_bump_fetch_refspecs.py`, 121 `test_path_guard_hygiene.py`, 87 `test_pinned_copy_text.py`, 101 `test_repo_hygiene.py`; the counts are from `--collect-only -q` on the same head and add up to 504). Log `v131/n1238/gate-g243.log`. - **The seven files, git 2.47.1** (first on PATH, `git --version` printed into the log): 504 passed in 714.00s. Log `v131/n1238/gate-g247.log`. - **`-k carried-refs-h-not-a-pattern`** over `tests/test_family_bump_fetch_refspecs.py` and `tests/test_bump_leg_fetch_refspecs.py`: 2 passed, 123 deselected, on 2.43.0 and on 2.47.1 (`run-k-g243.log`, `run-k-g247.log`). - Both runs start with `dirty=0`. Python 3.12.3 (CI runs 3.14); nothing asserts text that depends on the version. The measured git behaviour above comes from the probe scripts, not the test files. `-k carried-refs-h-not-a-pattern` over the two fetch-refspec files is part of those runs. `git merge-tree --write-tree origin/main HEAD` exits 0, with tree `9d874bcf2caf`, equal to this head's tree, against main `50869f384` (the merge base). Against the head of each open PR that touches `scripts/shape_materialize.py` or `scripts/bump-leg.py` (read from `gh pr list --state open --limit 100 --json number,headRefName,headRefOid,files`, 52 open PRs at the last read, 7 of them touch one of the two files), `git merge-tree --write-tree HEAD <head>` exits 0 and the merged tree still holds the new sentence and not the old one: ``` #1223 f50b472 exit 0 #1207 6fa1edd exit 0 #1160 0068f5a exit 0 #1131 55c30f2 exit 0 #1118 2307036 exit 0 #1105 b28f40d exit 0 #1104 de89aa8 exit 0 ``` Main moved once while this was gated, to `4829418f0` (`adopt-project.py`, `docs/cli.md` and a new test file: none of the files this change or its runs touch), and `git merge-tree --write-tree origin/main HEAD` against it also exits 0. None of their hunks is within 100 lines of the sentence changed here. Review: rv1244 and rv1244b, lane 5's independent sonnet reviewers, which wrote none of #1244: READY at `36ec589a6`, H0 of this landing (issuecomment-6723280477), with no MUST or SHOULD; its four NITs are optional (two wordings, a body note and a follow-up). CI (Brett Heap's ruling 27): H0 is the head 473959f itself, which passed tests, tests-git-bash and SonarCloud; main has touched none of its files since its base 9729315; and the local gate passed on its predicted squash tree fb798f3 over main 9252ef2: the repo_hygiene, agents_md_codes, cli_reference sets, 171 passed. Landed on Brett Heap's word of 2026-10-23T13:10:26Z, relayed to every lane by lane 3 (#473 issuecomment-6365697123): "merge v1.2.1 PRs once reviewed and green". Closes #1238 Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Goal
The three things rv1090's review of #1090 (#912) left (#1090 issuecomment-6316009648): a row that holds
YAML_NOT_TEXT_RE's exponents of two or more digits, a true refusal where a leg with nooriginholds a tag without its commit, and noelsethat nothing reaches. This is #1103. Brett Heap's ruling of 2026-10-21T10:32:04Z held it until the v1.2.0 tag, and it is now on the v1.2.1 milestone.Three commits:
9d14c29aon main6da757cb, a merge of main038dafba(clean), and fix round 1a4beec9e, which answers rv1131's review (#1131 issuecomment-6321276436, READY with two NITs): see## Fix round 1. Together they changescripts/bump-leg.py,tests/test_neutral_pin_entry_one_spelling.pyandtests/test_bump_leg_reachability_words.py. The sections down to## Runsare the first commit's, as rv1131 reviewed them.What held on main
Each claim was measured on main
6da757cbby name; the issue's lines are atd5389eb6. All three held, at the same names.YAML_NOT_TEXT_RE(scripts/shape_materialize.py:538-543) has two exponent groups,(?:[eE][-+][0-9]+)?and(?:[eE][-+]?[0-9]+)?, and every exponent inYAML_FLOATS(tests/test_neutral_pin_entry_one_spelling.py:92) has one digit.[0-9]+to[0-9]in both groups.test_scaffold_pin_and_reuse.pyandtest_adopt_pins.pythat-k "spell or entry or name or rule or stock or every"selects..5e-10,.5E+10,.5_e-10,.5e10and.05E-12correctly, so this is a missing row, not a defect._unreachable_refusal(scripts/bump-leg.py:1239) was called by the probe on a leg that holds the tagv1's object but not its commit.has_origin=Falseand eitherfetched, it saidbump-leg-commit-not-on-remote: <tag> is not in the leg's own object store, and the leg's mount, <leg>, has no remote namedoriginto fetch it from, .... That is untrue:cat-file -tsaystag.has_origin=Trueit saidopensoft/openGlass has no branch containing <tag>, which is true.peeled_target(scripts/bump-leg.py:979) ran over a commit, a tag, a tag of a tag, a tree, a blob, a tag of either, a tag and a tag of a tag held without their commit, and an id the leg lacks.names a {kind}was reached by the tree and the blob, andis an annotated tag of a {names}by the tag of a tree and the tag of a blob.else: what = "is an annotated tag that names no commit"was reached by nothing.What changed
YAML_FLOATSgains.5e-10,.5E+10,.5_e-10and.5e10, as the issue asks..5.5e-10. All four of those reach the second pattern (\.[0-9_]+), so a cut to the first group's exponent alone still passed them. Only the first pattern, YAML 1.1's as written, takes.5.5e-10. Each group is now held on its own (R2a and R2b below).--pinadmits every name in it:.e+5,.5e+3and.5E+10hold a+, whichPIN_ARG_RErefuses, so it says a plain scalar can spell them.YAML_FLOATS: double-quoted and read back, and double-quoted by the rule alone. That is 10 new rows._unreachable_refusal: where the leg has nooriginandobject_kind(id)istag, the refusal keeps its code,bump-leg-commit-not-on-remote. Its words are now:<tag> is an annotated tag the leg's own object store holds without the commit it names, and the leg's mount, <leg>, has no remote namedoriginto fetch that commit from, so whether a branch of <repository> reaches it is not knownRemediation: check the tag -- the commit it names belongs to the LEG, not to the assembly root -- and push that commit to a branch of the leg first if it is not pushed yet; give <leg> back a remote namedorigin, ...(an em dash in the tool, as in the words beside it).origin.grepoftests/,README.md,docs/, the handbook andAGENTS.mdfinds it only inbump-leg.py, andAGENTS.md's codes table is unchanged.peeled_target: the three-wayif/elif/elseis now one expression:is an annotated tag of a {names}for a tag,names a {kind}otherwise, with a comment saying why nothing else is left. The words for a tree, a blob and a tag of either are unchanged, by the same trace on the head.shape_materialize.not_a_commit, whichcommit_on_forgeuses, still says "an annotated tag that names no commit"; it is not touched.Rows, failing on main
tests/test_bump_leg_reachability_words.py,test_a_tag_a_leg_with_no_origin_holds_alone_is_told_it_is_held.git hash-object -t tag -w). The leg'soriginis renamed toupstream, then both runs go throughassert_one_refusal.told_no_origin, and that the old sentence is not in stderr.private_remote,without_origin,runnerandorigin_back.6da757cb, git 2.43): 1 failed, 159 passed. The one failure is that row, which got main'sREFUSED bump-leg-commit-not-on-remote: <tag> is not in the leg's own object store.andoror.Mutants
Each mutant was applied alone to a scratch clone holding this commit's files, then restored and compared with the worktree.
[0-9]+to[0-9]in both exponent groups.5.5e-10, both rows).5_e-10and.5e10, both rows)-k "no_origin or held"in the reachability fileRuns
All runs go through lane 5's
run9.sh: a stubghfirst on PATH, a bogus token, everyLANES_*variable cleared,EMAIL=service@opensoft.dev,GIT_SSH_COMMAND=falseandGIT_CONFIG_GLOBAL=/dev/null.git --versionprintedgit version 2.47.1): 160 passed.bump-leg.py. The commit changes production code inscripts/bump-leg.pyonly.scripts/shape_materialize.pyis unchanged: only its test rows were added, and those ran on both gits above. So the related-file runs take the test files that namebump-legorbump_leg, and the files that name onlyshape_materializeare not needed.grep -rl -E 'bump-leg|bump_leg' tests --include='*.py': 56 files, sorted and split into two halves. It holdstests/test_bump_leg_reachability_words.pyagain, andtests/test_repo_hygiene.pyandtests/test_agents_md_codes.py.tests/test_bump_leg.py, and one is Windows' own stdout default, intests/test_stdout_encoding.py.tests/test_repo_hygiene.py,tests/test_agents_md_codes.pyandtests/test_cli_reference.py: 170 passed.Fix round 1
Commit
a4beec9e, on the merge of main038dafbainto the branch (ae830e2b, clean, tree10c2372fe578). It answers rv1131's review (#1131 issuecomment-6321276436).What changed
originwords for a held tag were untrue in two rarer cases. The branch keyed onobject_kind(id) == "tag"alone, so every held tagpeeled_targetcannot peel was told the leg holds it "without the commit it names". Each case now reads the tag chain from the tags' own headers,git cat-file tag'stypeandobjectlines, one tag at a time while the leg holds the next (tag_chain_end, new). The peel (<id>^{}) says nothing where the leg lacks an object on the way.bump-leg-commit-not-on-remote, now in these words:<tag> is an annotated tag the leg's own object store holds without a tag it leads through, <inner tag>, and the leg's mount, <leg>, has no remote namedoriginto fetch that tag from, so what that tag names, and whether a branch of <repository> reaches it, are not knownRemediation: check the tag -- the tags it leads through, and what they name, belong to the LEG, not to the assembly root -- and push them to the leg first if they are not pushed yet, and any commit they name to a branch of it; give <leg> back a remote namedorigin, ...(an em dash in the tool, as in the words beside it).peeled_targetnow refuses itbump-leg-target-not-a-commit, in the words a tag of a held tree or blob already had:--to <tag> is an annotated tag of a tree in the leg (<repository>), not a commit. That is with or without anorigin, since no fetch makes a tree a commit.9d14c29aboth refuse itbump-leg-target-not-a-commitin those words. The new refusal is byte for byte the same as main's for the same tag id held with its tree or blob, in all 6 (leg, id) pairs of (b) below.origin, such a tag was refusedbump-leg-commit-not-on-remote, " has no branch containing ", with "check the commit". That is the one with-originanswer this round changes.README.md,docs/, the handbook orAGENTS.md;tests/test_agents_md_codes.pyandtests/test_cli_reference.pypass.peeled_targetand_unreachable_refusalkeep their docstrings' accounts true: the 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 paragraph now says a tag held without THE COMMIT it names, and each function says what (a) and (b) are now told.refs/replace/is never walked: git's own peel never leaves one (measured, git 2.43:rev-parse <tag>^{}was still running at a 10 s timeout), and the walk is reached only where the peel failed..5.5E-10is added toYAML_FLOATS: only the first exponent group takes it, and only with itsE. The list's comment says so.Words compared with main's and the head's tool
The probe called
peeled_targetand_unreachable_refusalfrom main038dafba, the head9d14c29aand this head on 15 legs x 10 ids x 4(has_origin, fetched), as rv1131's did, with legs for (a) and (b) added. Each id was asked as typed (raw) and, wherepeeled_targetreturned, as it returned (flow). That is 1578 answers from main and the head, and 1554 from this head, where 6 (leg, id) pairs are refused before the verdict.9d14c29avs this headrawaskshas_origin=Falseonly. Each holds the outer tag without the inner one: a tag of a tag of a commit, with the commit or without it, and a tag of a tag of a tree, with the tree or without it.peeled_targetand the four verdicts each: a tag of a tree and a tag of a blob, each alone and under a held tag of it, and those two tags of a tag, all held without the tree or blob.rawasks ask_unreachable_refusaldirectly about a held tag of a tree or blob, which the flow never does:peeled_targetrefuses it first, with the tree or blob held or not. They keep the head's words.originbut (b)'s, every missing id and every held commit keeps main's words.Rows, failing on
9d14c29aand on mainIn
tests/test_bump_leg_reachability_words.py, in the file's style, throughassert_one_refusal(both runs, exit 2, the same words, nothing written). They reuseprivate_remote,without_origin,runner,told_no_origin,refusalandorigin_back. Two helpers,pushed_nowhereandwritten_into_the_leg, are split out ofheld_without_its_commit, which keeps what it did.test_a_tag_through_a_tag_the_leg_lacks_is_told_which_tag_it_lacks[commit-held, commit-missing]: (a). The leg holds only the outer tag; the commit is the leg's own pin, or one pushed nowhere. The note and the two lines are asserted exactly, and "without the commit it names" is not in stderr.test_a_tag_of_a_tree_or_a_blob_the_leg_lacks_is_refused_by_name[...]: (b), 6 rows. The kinds are a tag of a tree, a tag of a blob, and a tag of a tag of a tree with both tags held. Each runs with the leg'soriginand without it. The tree and the blob are pushed nowhere, so no fetch brings them, and the row checks that afterwards. The two lines are asserted exactly, with the note where there is noorigin.9d14c29a'sbump-leg.py: 8 failed, 59 passed (git 2.43; the rows oftests/test_bump_leg_reachability_words.pyandtests/test_bump_leg_dry_run_truth.py). The 8 are exactly the 8 new rows: the 2 of (a) and the 6 of (b). The old file was put back by sha256 afterwards.038dafba'sbump-leg.py: 9 failed, 58 passed (the same two files, git 2.43). The 9 are those 8 and the issue's row,test_a_tag_a_leg_with_no_origin_holds_alone_is_told_it_is_held. The old file was put back by sha256 afterwards..5.5E-10adds 2 rows, one to each test that takesYAML_FLOATS. They pass on main and on the head, as they should: the rule is right, and they are there to kill M5.andoror. Added lines are at most 79 characters wide. The only non-ASCII character is the em dash in the tool's own words.Mutants
Each was applied alone in the worktree, at this commit. Its rows were run, then the file was restored and checked against the commit by sha256. For
bump-leg.pythe rows aretests/test_bump_leg_reachability_words.pyandtests/test_bump_leg_dry_run_truth.py; forshape_materialize.py, the spelling file.shape_materialize.py, the first exponent group's[eE]cut to[e].5.5E-10, intest_a_name_yaml_takes_for_no_text_is_double_quotedandtest_the_rule_quotes_it_whatever_the_projects_reader_readsnames = names or tag_chain_end(submodule, target)[0]cut tonames = names, so the header is never read where the peel says nothingtag = namedstep replaced by a secondreturn, so the chain is never followed past its first tagtag of a tag of a treerows of (b)if names == "tag":to"tags"("tree", "blob")to("tree",)tag of a blobrows of (b), andtag of a blobforlocal-remote-dirandoriginintest_a_tree_a_blob_or_a_tag_of_either_is_refused_by_name_in_both_runs{named}to{commit}origintoo,not has_origin andcuttest_a_tag_the_leg_holds_without_its_commit_is_not_on_the_remote, forlocal-remote-dirandorigintag, nottypeNo mutant survived. After each,
scripts/shape_materialize.pyandscripts/bump-leg.pywere restored from the commit and their sha256 checked:a37e3d83fbcda475...andd6822093b1aa4b0e...both matched, with the worktree clean.Runs
All go through lane 5's
run9.sh, as above, ata4beec9e.tests/test_neutral_pin_entry_one_spelling.pyandtests/test_bump_leg_reachability_words.py): 170 passed, which is the first commit's 160 and this round's 10 new rows.git --versionprintedgit version 2.47.1): 170 passed.tests/test_path_guard_hygiene.py,tests/test_repo_hygiene.py,tests/test_agents_md_codes.pyandtests/test_cli_reference.py: 219 passed.bump-leg.py, picked as above (grep -rl -E 'bump-leg|bump_leg' tests --include='*.py'): 57 files, which are the 56 files of the first commit's set andtests/test_path_guard_hygiene.py, which main gained since. Sorted and split in two:tests/test_bump_leg.py(a disk that ignores case) andtests/test_stdout_encoding.py(Windows' own stdout default).a4beec9eitself, committed, with the worktree clean (dirty=0in each log's START line).Merge-trees
Refreshed for fix round 1 (rv1131's body NIT).
git merge-tree --write-treeof the heada4beec9ewas run against origin/main, re-fetched (-c gc.auto=0 -c maintenance.auto=false), and against each open PR that touches these files:038dafba9dbef571322318b1, the head's own tree: the head holds main by its mergefix/1019-points-at-row(DRAFT)ee48b5fe96937383abd73770fix/1100-refspec-followups(DRAFT)a35a97ea52f4de32065b7371--tonames, so a tag above it reads as one that reaches it and a tag below it keeps the plain words, and--points-atfor--containsno longer survives (#1019) #1107 touchestests/test_bump_leg_reachability_words.py. It givesmade_in_the_legan optionalon=and adds rows aftertest_a_tag_of_a_tag_the_two_share_reaches_it.made_in_the_legnor anything tests: the bump-leg.py and family.py reachability rows tag a descendant of the commit--tonames, so a tag above it reads as one that reaches it and a tag below it keeps the plain words, and--points-atfor--containsno longer survives (#1019) #1107 changes. The merged file parses, and no name in it is defined twice.refs/heads*that puts a branch under another name, its clauses move into_left_out, and the docstrings name the clashes left and the way back (#1100) #1157 touchesscripts/bump-leg.py,scripts/shape_materialize.pyand two refspec test files, so it shares onlyscripts/bump-leg.pywith this PR._leg_fetch_refspecs, at lines 1109 to 1146 of main's file. This PR's hunks in that file are at main's lines 976 to 1043 (tag_chain_end, new, andpeeled_target) and 1250 to 1289 (_unreachable_refusal), so the two do not meet and the merge is clean.ae830e2bbrought in every file main gained since6da757cb(among them tests/test_path_guard_hygiene.py and shape-doctor.py: a bare is_file/is_dir/exists guard in a shipped tool fails by file and line unless its_is_aor a reasoned allowlist covers it, and aproject.yamllinked into an unsearchable folder isyaml-unreadable(#430) #1111'stests/test_path_guard_hygiene.py). None of them is one of these three files, and the runs above stand on the head's own tree.NIT round
rc1131 re-checked fix round 1 at
a4beec9eand found it READY with four NITs (#1131, issuecomment-6347256815). The commit55c30f20f005mends the first three, inscripts/bump-leg.pyandtests/test_bump_leg_reachability_words.py. NIT 4, the title, is not in a commit. Nothing the tool does changes: the production edit is a comprehension that gives the same dict and a longer docstring.What changed
bump-leg.py:999. Intag_chain_end,dict(line.split(" ", 1) for line in lines if " " in line)is now{key: value for key, sep, value in (line.partition(" ") for line in lines) if sep}, the form rc1131 gave.tools/equiv2.pycompares the two on every list of up to two lines of up to five characters, and every list of up to three lines of up to three characters, overa,b, a space and a tab (2478716 distinct lists: the script runs the two ranges one after the other, and the 7311 lists of up to two lines of up to three characters sit in both, so it makes 2486027 comparisons, as rc1131's count of the union shows), and on a real tag header with a continuation line, a line with no space and atypeline with a trailing space: equal in all. The commit message says "every list of up to three lines"; the coverage is the one in this sentence (equiv.py's first pass, 3864591 lists, drew its three-line lists from a prefix of the strings).opensoft_openRepoShape, python, MAIN) reports one issue, python:S7494 MINOR, at the line of the olddict(...)form, and none at the comprehension.scripts/bump-leg.py:999(MINOR, created 2026-10-22T13:31:04Z), and its status is CLOSED, so there are 0 open issues. The quality gate is OK on all five conditions (new reliability, security and maintainability ratings 1, new duplicated lines density 0.0, new security hotspots reviewed 100.0). The API gives no commit, so it does not say which analysis it read: it shows the issue closed, not that the analysis was of55c30f20.TAGS_OF_NO_COMMIThas a fourth kind,tag of a tag of a tag of a tree: three tags, all held, and the tree pushed nowhere, run with and without anoriginlike the other kinds. Its values are now(what, depth), and the row makesdepth - 1tags over the first one (outer1-w1131,outer2-w1131) where it made the oneouter-w1131for the second kind; no other row reads the dict.tag_chain_end's docstring. It now says("tag", <id>)is also what it returns "through a tag whose header misstates what it names (type tagover an<id>the leg holds as something else, which only a malformed tag has)".Rows, failing on main
Both rows of the new kind (
test_a_tag_of_a_tree_or_a_blob_the_leg_lacks_is_refused_by_name[tag of a tag of a tag of a tree-origin]and[...-no-origin]) fail on main'sbump-leg.py(961640c6) and pass on the head's before this commit (a4beec9e's). The two test files that hold rows forpeeled_target(tests/test_bump_leg_reachability_words.pyandtests/test_bump_leg_dry_run_truth.py) run 69 rows:bump-leg.pyfrom961640c6a4beec9e(this PR before the commit)55c30f20With main's file the new rows fail on the code of the refusal, as the other rows of the kind do: the row expects
REFUSED bump-leg-target-not-a-commit, and the run with anoriginprintsbump-leg-commit-not-on-remote: testorg/Atlas-spec has no branch containing <the tag>(main'speeled_targetreturns the tag as typed where its peel cannot be read, because the leg lacks the tree, and the run goes on to the reachability verdict), and the run without one prints thegit fetch origin ... has no remote named originnote and goes the same way.Mutants
Each was applied alone to
scripts/bump-leg.py, the 69 rows run on git 2.43.0, and the file restored fromHEAD; all 11 restores matched (bl=1a7a69d38f737dce,dirty=0). The mutants are intools/mutate.py.while True:asfor _ in range(2):tag, and the refusal isbump-leg-commit-not-on-remote, notbump-leg-target-not-a-committag = namedreplaced by a returntest_a_tag_through_a_tag_the_leg_lacks_is_told_which_tag_it_lackskey: valueasvalue: keyif not sep)if sepfilter droppedrpartitionforpartitiontypeandobjectlines each carry exactly one space and each appears once, and nothing reads another key. C1 keeps a line with no space as a key with an empty value, which.get("type", "")and.get("object", "")already say of a missing key. C2 splits at the last space, which for a line with one space is the first. Where they could differ is a hand-forged header that repeats a baretypeline aftertype tag; no row forges one.Runs
Every run went through
run9.shwithEMAIL=service@opensoft.dev, on55c30f20itself, committed, with the worktree clean (dirty=0in each log's START line). Logs are in$Q/v124/n1131/, where$Qis the lane's scratchpad.tests/test_bump_leg_reachability_words.py,tests/test_neutral_pin_entry_one_spelling.py,tests/test_bump_leg_dry_run_truth.py,tests/test_path_guard_hygiene.py,tests/test_agents_md_codes.py,tests/test_cli_reference.py,tests/test_pinned_copy_text.py(g243.log)/usr/bin/git)g247.log)git --versionprintedgit version 2.47.1)bump-leg.py, which fix round 1 ran in two shards (3419 passed), were not run again: the edit is insidetag_chain_end, and the rows that reach it are the two files above, which ran in full. CI'stestsjob on the pushed head is the full-suite run.tests/test_path_guard_hygiene.pypasses: the commit adds nois_file,is_dir,exists,is_symlinkoros.pathguard.andoror.Merge-trees at
55c30f20git merge-tree --write-treeof the head against origin/main, re-fetched (-c gc.auto=0 -c maintenance.auto=false), and against each open PR that touches one of this PR's three files. Of the 41 open PRs listed at 20:09Z (gh pr list --json number,headRefName,headRefOid,files), only #1157 and #1107, besides this one, touch them.961640c681b54d7968be5547: the head's tree anddocs/release-notes/v1.2.0.md, the one file main gained since038dafba(#1155)fix/1100-refspec-followups(DRAFT)368b87e9be87(re-measured at 2026-10-23T12:06:38Z, after #1157's NIT round, which changes onlytests/test_family_bump_fetch_refspecs.py;baa19972a271gave the same clean merge with treebd6ebc5cd3af, anda35a97ea52f4when this round began)e036c02d6a12fix/1019-points-at-row(DRAFT)ee48b5fe9693a72145c05633scripts/bump-leg.pyare at lines 979 to 1003, intag_chain_end; shape_materialize.py and bump-leg.py: the fetch for a branch's answer leaves out a pattern likerefs/heads*that puts a branch under another name, its clauses move into_left_out, and the docstrings name the clashes left and the way back (#1100) #1157's against main are in_leg_fetch_refspecs, from line 1105. They do not meet.After the second re-check
rc1131 found this round READY at
55c30f20f(#1131, issuecomment-6352247887), with two NITs of its own, both in this body and neither in the code. This body now answers both: the count of lists in NIT 1's equivalence check (2478716 distinct lists, 2486027 comparisons), and SonarCloud's own analysis (python:S7494 CLOSED, 0 open issues, gate OK, no commit named by the API). The one other change is the #1157 row of the merge-trees table, which moved when #1157's NIT round added a commit. The head55c30f20fis unchanged.Closes #1103
Claim: #1103 (comment)
Lane: openreposhape-5 (openRepoShape-5)
🤖 Generated with Claude Code
Summary by Sourcery
Improve bump-leg validation and diagnostics for incomplete annotated tags while expanding coverage for YAML float spellings with multi-digit exponents.
Bug Fixes:
Enhancements:
Tests: