Skip to content

tests: the bump-leg.py and family.py reachability rows tag a descendant of the commit --to names, so a tag above it reads as one that reaches it and a tag below it keeps the plain words, and --points-at for --contains no longer survives (#1019) - #1107

Merged
brettheap merged 2 commits into
mainfrom
fix/1019-points-at-row
Oct 23, 2026

Conversation

@brettheap

@brettheap brettheap commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Lane: openreposhape-5 (openRepoShape-5)
Claim: #1019 (comment)

Goal

No test held that a tag at a DESCENDANT of the refused commit counts as a tag that reaches it, so --points-at in place of --contains survived in bump-leg.py and family.py (mutant M9 of #988's review, filed as #1019). This adds the rows the issue asks for to the two reachability test files, and no production code changes. Opened as a draft with no milestone: Brett Heap's ruling of 2026-10-21T10:32:04Z ("leave the follow-ups out of v1.2.0") held it until the v1.2.0 tag, and it is now on the v1.2.1 milestone.

What still held on main

Measured on main 2a119f28 before any change, and checked again on 16f9a0e81 and 7e00834c (the tag questions did not change, only their line numbers):

  • scripts/bump-leg.py _remote_tag_reaches and scripts/family.py _origin_tag_reaches list the tags with git for-each-ref --contains <commit> --format=%(objectname) refs/tags/. No other copy of either exists (the --contains in on_a_remote_branch and _on_a_remote_branch asks about branches and is not this question).
  • The mutant (--contains -> --points-at on that one line in each script) SURVIVES every row on main: test_bump_leg_reachability_words.py 34 passed, test_family_bump_reachability_words.py 20 passed, 54 rows, as the issue says.
  • The production code is right: the new rows pass on unmutated main. So they do not "fail on main"; they fail on the mutant, which is the issue's own acceptance ("With --points-at, that row must fail"). No defect was found.

What changed (two test files, 197 insertions, 4 deletions, after round 2)

tests/test_bump_leg_reachability_words.py:

tests/test_family_bump_reachability_words.py:

  • made_on(holder, env, parent, name): a commit never pushed, made in the mount on top of parent, through commit_in_mount (which checks parent out first), so the holder stays clean.
  • test_a_tag_at_a_descendant_of_the_commit_reaches_it: the same three kinds (3 rows), both bump --dry-run and the real bump refuse member-to-local-only in the tag words, with nothing moved and the mount's own tag as it was.
  • test_a_tag_at_an_ancestor_of_the_commit_alone_keeps_the_plain_words: the control (1 row).

No assert joins conditions with and or or; each premise is its own assert. No README, docs, AGENTS.md or handbook text names these tests or their row counts (grep), so none changes.

The mutants (rebased tree, 7e00834c's line numbers: bump-leg.py 1205, family.py 8869; each mutant restored, sha256 checked equal to HEAD)

Mutant Rows before (main's) With these rows
--points-at in bump-leg.py survives: 34 of 34 pass killed: 6 failed, 36 passed (all six descendant rows; the two controls pass, as they should)
--points-at in family.py survives: 20 of 20 pass killed: 3 failed, 21 passed (all three descendant rows)
--merged in bump-leg.py (extra: a check read the other way round) survives: 34 of 34 pass killed: 8 failed (6 descendant rows and both controls)
--merged in family.py (extra) survives: 20 of 20 pass killed: 4 failed (3 descendant rows and the control)

The failure is the one the issue names: the plain words ("is reachable from no branch of ...") where the tag words ("and a tag of ... reaches it, but no branch ... does") are expected. Logs: r-mut-points-at-bump.log, r-mut-points-at-family.log, r-mut-merged-bump.log, r-mut-merged-family.log (and mut-main-*.log for the 34 and 20 on 2a119f28).

Runs (through run9.sh, EMAIL=service@opensoft.dev, GIT_SSH_COMMAND=false, GIT_CONFIG_GLOBAL=/dev/null, gh stubbed, at most two at once, never the full suite)

On the pushed head 01fcb259:

  • git 2.43.0 (/usr/bin/git): the two test files, 66 passed (42 + 24; 34 + 20 before).
  • git 2.43.0: the trio tests/test_repo_hygiene.py, tests/test_agents_md_codes.py, tests/test_cli_reference.py and tests/test_update_shape_kept_helper.py, 329 passed.

On 1201680f (the same two files on top of 16f9a0e8, before 7e00834c, whose three files no row of mine touches or runs):

  • git 2.43.0: the trio alone, 170 passed; the two test files that import helpers from mine (test_bump_leg_fetch_refspecs.py, test_family_bump_fetch_refspecs.py), 102 passed; the two test files, 42 and 24 passed.
  • git 2.47.1 ($Q/v115/w764/git247/bin/git first on PATH, git --version printed git version 2.47.1): the two test files, 66 passed.

No script changed, so no other test runs a changed script.

Merge-trees

Round 2 (rv1107, NIT 2; NIT 1, the title, was the coordinator's)

  • The NIT. The docstrings of the two descendant rows said "Every row before this one tagged the commit itself, so ... survived all 54 of them". 54 counts the two files together (34 + 20), several earlier rows tag nothing (the no-origin and prune rows), and a count goes stale with the next row.
  • The fix, docstrings only. Each now says only what holds: "No row before this one tags a descendant of the commit: the tags in them are at the commit itself or below it, so --points-at in place of --contains, the mutant M9 of bump-leg.py and family.py: the tag check compares the commits tags point to, so a lightweight or another annotated tag at the same commit reaches it (#982) #988's review, survived every row of this file." No row, assert or helper changed. The 54 in "What still held on main" above is the two files together, as it says.
  • One commit on top of 01fcb259, a fast-forward push, no rebase: head ee48b5fe9693ebda099d1c0aadaca7bdc16b91c1, read back with git ls-remote. Its tree is 615e571bb62fd1cf481e4152278b0e3628bab059.
  • Runs (git 2.43.0, through run9.sh, EMAIL=service@opensoft.dev): tests/test_bump_leg_reachability_words.py 42 passed, tests/test_family_bump_reachability_words.py 24 passed. They ran on 7186f3cf, the same tree as the pushed head; only the commit message was shortened after.
  • Merge-tree against a re-fetched origin/main (6da757cb, merge base 7e00834c): exit 0, tree 453566df33c886cf7fc2b1c39af02acbbff365dc. Main moved by nine files, none of mine; its scripts/bump-leg.py change (9 lines added) leaves _remote_tag_reaches and its --contains line alone (now line 1214).

Closes #1019

🤖 Generated with Claude Code

Summary by Sourcery

Strengthen reachability tests for bump-leg and family so descendant-tag handling is validated and incorrect points-at or reverse-reachability implementations are caught.

Enhancements:

  • Expand bump-leg and family reachability coverage to verify that tags on descendant commits are reported as reaching the target, while tags on ancestors do not change the plain refusal wording.

Tests:

  • Add parametrized reachability tests covering annotated and lightweight tags across local and remote repositories, including controls for ancestor tags, dry-run behavior, and preservation of repository state.

… names, so a tag above it is told as one that reaches it and a tag below it keeps the plain words, and `--points-at` in place of `--contains` no longer survives (#1019)

`_remote_tag_reaches` (bump-leg.py) and `_origin_tag_reaches` (family.py)
list the tags that reach a refused commit with `git for-each-ref --contains`:
a tag at the commit or at any commit made on top of it. Every row tagged the
commit itself, so swapping `--contains` for `--points-at`, which lists only a
tag AT the commit, survived all 54 rows of
tests/test_bump_leg_reachability_words.py and
tests/test_family_bump_reachability_words.py (mutant M9 of #988's review).

Each file now has a row where the leg (or mount) and the remote each tag a
DESCENDANT of the commit, made apart, and both runs give the tag words (bump-leg
through `origin` and through `--local-remote-dir`), and a control where they tag
only the commit's ANCESTOR and the plain words stay. No production code changes.

Closes #1019

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

sourcery-ai Bot commented Oct 21, 2026

Copy link
Copy Markdown

Reviewer's Guide

This test-only PR closes the reachability coverage gap in bump-leg and family by modeling tags on descendants and ancestors of a local-only commit, validating correct refusal wording and state preservation across execution paths, and killing the surviving --points-at mutation.

File-Level Changes

Change Details Files
Add descendant-tag reachability coverage to ensure tags on descendants of the refused commit are reported as reaching it.
  • Extend commit-fixture helpers to create commits on top of arbitrary parents.
  • Exercise annotated and lightweight tag combinations through both bump-leg execution paths.
  • Assert tag-based refusal wording, unchanged refs, and no repository movement.
  • Verify premises exclude exact-point tags, branches, and remote-tracking refs.
tests/test_bump_leg_reachability_words.py
tests/test_family_bump_reachability_words.py
Add ancestor-tag control cases to preserve the distinction between tags above and below the target commit.
  • Assert a tag on the parent does not reach a child commit absent from the remote.
  • Preserve the existing plain reachability wording for both dry-run and real bump flows.
  • Check offline state and repository contents remain unchanged.
tests/test_bump_leg_reachability_words.py
tests/test_family_bump_reachability_words.py
Strengthen mutation coverage for the production reachability query without changing production code.
  • Ensure replacing --contains with --points-at fails all descendant-tag cases.
  • Cover both bump-leg and family implementations and multiple tag object types.
  • Retain compatibility checks for existing helper-importing and hygiene test suites.
tests/test_bump_leg_reachability_words.py
tests/test_family_bump_reachability_words.py

Assessment against linked issues

Issue Objective Addressed Explanation
#1019 Add reachability-test rows in bump-leg.py’s two routes and family.py where tags point to a descendant of the pinned commit, and verify the tag-reaches wording (so replacing --contains with --points-at fails those rows). ✅
#1019 Add a control in each test area showing that a tag only at an ancestor does not reach the pinned commit and the plain wording remains. ✅

Possibly linked issues


Tips and commands

Interacting with Sourcery

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

Customizing Your Experience

Access your dashboard to:

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

Getting Help

@brettheap

Copy link
Copy Markdown
Contributor Author

Independent review of #1107 (#1019) at 01fcb259, for lane openRepoShape-5: READY

Findings: no MUST, no SHOULD.

  • NIT 1, the title. It begins "bump-leg.py and family.py:", but no line of either script changes.
  • NIT 2, tests/test_bump_leg_reachability_words.py:355 and tests/test_family_bump_reachability_words.py:476. The docstrings say "Every row before this one tagged the commit itself, so ... survived all 54 of them."
    • 54 is the two files together (34 + 20); each file held 34 or 20 on its own.
    • Several earlier rows (the no-origin and prune rows) tag nothing.
    • The count goes stale with the next row.
    • Scenario: a reader counting this file's rows finds 34 and doubts the docstring.
    • Suggest "all 34 of this file's (54 with family's)", or drop the count.

Checked and true

  1. The issue's mutant, --points-at in place of --contains, at bump-leg.py:1214 (line 1205 at 7e00834c) and family.py:8869.

    • It survives main's rows (6da757cb): bump-leg 34 passed, family 20 passed. That is the issue's 54.
    • The new rows kill it.
      • bump-leg, on the predicted merge: 6 failed, 36 passed. The six failures are the descendant rows, both routes. The controls pass.
      • family, at the head: 3 failed, 21 passed. The three failures are the descendant rows.
      • These are exactly the writer's numbers. The failure text gives the plain words where the tag words are expected.
    • I also re-ran the writer's --merged mutant in family.py: 4 failed (the 3 descendant rows and the control), 20 passed. Same as the body.
  2. The rows.

    • Descendant rows. Their setup checks:

      • above^ is made;
      • the two tags are two objects, and both peel to above;
      • for-each-ref --points-at made refs/tags/ is empty on both sides, which is what makes --points-at fail;
      • a tag of the remote contains made, but no branch and no remote-tracking ref does.

      They then expect the tag words, no "cannot fetch", the own tag unchanged and nothing moved. Each asserts what its name says.

    • Controls.

      • Setup: the commit named is the child and the tag is at its parent. The remote holds the parent but not the child, and no tag and no remote-tracking ref of the leg (or mount) contains the child.
      • They expect local_only(child).
      • They are true controls: they pass on the real code and under --points-at, and fail under --merged.
    • Helpers.

      • The rows reuse the files' own helpers: tag_as, pushed, only_a_tag_reaches, assert_one_refusal, tag_reaches, local_only, contains, commit_in_mount, both_runs, private_origin.
      • made_in_the_leg's default on="HEAD" keeps the old behaviour. Its one other importer, tests/test_bump_leg_fetch_refspecs.py, does not pass on.
    • AST scan.

      • The bump-leg file has 89 asserts and the family file 77. None joins conditions with and or or.
      • Both files are at most 79 wide, their existing width. The added lines are ASCII.
      • No top-level name is defined twice.
  3. Three mutants of my own. Each was applied alone and restored, with its sha256 checked equal to HEAD.

    mutant where result
    M1: compare the remote's tag list only with the commit (in held -> == commit.lower()) bump-leg.py:1227 6 failed, 36 passed: only the descendant rows, so main's 34 rows miss it too
    M2: the same family.py:8881 3 failed, 21 passed: only the descendant rows
    M4: drop the ^{} peel (set(tags) | set( -> set(tags) or set() bump-leg.py:1219 14 failed, 28 passed: 10 of the bump-leg.py and family.py: the tag check compares tag objects, not the commits they point to, so a lightweight tag against an annotated one of the same name at the same commit gets the plain words (follow-up to #937) #982 rows and 4 descendant rows; leg-lightweight-remote-annotated passes, as it should

    No mutant I ran survived. M1 and M2 are a second gap that only the new rows close.

  4. Runs, through run9.sh:

    tree git result
    head 2.43 66 passed in 191.57s
    head 2.47.1 (git --version read 2.47.1 at $Q/v115/w764/git247/bin/git; run9.sh's prepended folders hold only gh and git-filter-repo) 66 passed in 314.89s
    predicted merge aff80287 (01fcb259 + 6da757cb) 2.43 66 passed in 204.70s
  5. Merges:

    against result
    6da757cb clean, tree 0d1c5ad74e3cab67e019a7f69912782201973627
    7e00834c clean, tree 3a852e87 (the head's own tree)
    16f9a0e81 clean
    family.py: a failed remove's by-hand lines spell a leg mounted at a path that is not UTF-8 as a printf word of octal escapes ending in a slash, so git -C <member>/<leg> and submodule update -- <leg> typed in any POSIX shell name that leg (#1086) #1098 dad7961a (draft, touches family.py) clean, tree 46484a22
    shape_materialize.py: env_commit's docstring says the split commit's message spells a path byte that is not UTF-8 as _shown does, an escape, and no longer says it names it with a ? in its place, which #900 replaced (#1018) #1104 clean

    No other open PR touches the two test files.

  6. Title and body.

    • The title is 237 characters, ASCII.
    • closingIssuesReferences is [1019] alone, and the only closing keyword is Closes #1019. body-guard.py exits 0.
    • Body numbers I reproduced:
      • 195 and 4.
      • 34, 20 and 54.
      • 42 + 24 = 66.
      • 6 failed / 36 passed; 3 failed / 21 passed; 4 failed under family --merged.
      • Lines 1205 and 8869 at 7e00834c; tree 3a852e87.
  7. Sonar: gate OK, 0.0% new duplication, 0 open issues.

Could not verify

  • M3 (family --contains plus --merged on the same commit) was ready but not run, because of the checkpoint. So I made three mutants of my own, which meets the brief's "at least three" but not the common rules' four.
  • These body numbers were not reproduced: bump-leg --merged 8 failed; the 329 (the trio plus test_update_shape_kept_helper.py); the 102 importer rows; the 170 at 1201680f; the 8 open PRs at the time of writing.
  • tests/test_bump_leg_fetch_refspecs.py imports the changed made_in_the_leg, but I did not run it on the merge.
  • git 2.55 (CI).

Checklist candidates

  • A test-only PR's title names the test files or tests:.
  • A docstring states no row count that spans files.
  • When reviewing a reachability check, also mutate the comparison to "the commit only" (M1/M2).

(Posted by lane openRepoShape-5's coordinator. rv1104-1107 is lane 5's independent opus reviewer; it wrote none of #1107. The PR stays a DRAFT until the v1.2.0 tag; its NITs are taken before it leaves draft.)

Lane: openreposhape-5 (openRepoShape-5)

@brettheap brettheap changed the title bump-leg.py and family.py: rows tag a descendant of the commit --to names, so a tag above it is told as one that reaches it and a tag below it keeps the plain words, and --points-at in place of --contains no longer survives (#1019) tests: bump-leg.py's and family.py's reachability rows tag a descendant of the commit --to names, so a tag above it reads as one that reaches it and a tag below it keeps the plain words, and --points-at in place of --contains no longer survives (#1019) Oct 21, 2026
@brettheap brettheap changed the title tests: bump-leg.py's and family.py's reachability rows tag a descendant of the commit --to names, so a tag above it reads as one that reaches it and a tag below it keeps the plain words, and --points-at in place of --contains no longer survives (#1019) tests: the bump-leg.py and family.py reachability rows tag a descendant of the commit --to names, so a tag above it reads as one that reaches it and a tag below it keeps the plain words, and --points-at for --contains no longer survives (#1019) Oct 21, 2026
…_words.py: the descendant rows' docstrings say no earlier row of their file tags a descendant, and no longer count "all 54" rows of both files (#1019)

rv1107's NIT 2: "survived all 54 of them" counted the rows of the two files together
(34 + 20), several of those rows tag nothing (the no-`origin` and prune rows),
and the number goes stale with the next row. Each docstring now says only what
holds: no row before the descendant row tags a descendant of the commit, so
`--points-at` in place of `--contains` survived every row of its file.
Docstrings only.

Refs #1019

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

Copy link
Copy Markdown

@brettheap

Copy link
Copy Markdown
Contributor Author

Re-check of #1107 (#1019) at ee48b5fe, for lane openRepoShape-5: READY

Findings: one optional NIT. It does not need another round.

What changed and what I checked

  • The commit. One commit on top of 01fcb259 (a fast-forward). It changes only the two test files, 4 lines added and 3 removed in each. Its message says "Refs tests: no reachability row tags a descendant of the pinned commit, so --points-at in place of --contains survives in bump-leg.py and family.py #1019", which is not a closing keyword.
  • NIT 2 is fixed. "all 54" is gone from both files. The only other "all 54" in tests/ is an unrelated sentence in test_bootstrap_branch_bytes.py:528.
  • The new claims are true. I listed every tag each row makes in both files:
    • Every other tag is at the commit itself, or below it: the remote's v-same is at the pin, and the control's tag is at the parent.
    • The descendant rows are the only ones in either file that tag a descendant.
  • Nothing but docstrings changed.
    • With docstrings blanked, both files' ASTs are identical to round 1's (101224 and 80813). The only docstring that differs in each is test_a_tag_at_a_descendant_of_the_commit_reaches_it's.
    • The asserts are the same: 89 and 77, none with and or or.
    • The widths are still at most 79. The added lines are ASCII and at most 78 wide.
  • NIT 1 is fixed. The new title begins tests:. It is 250 characters, ASCII.
  • Merge. With main 6da757cb it is clean, tree 453566df (the body's tree). Of the 16 open PRs, only tests: the bump-leg.py and family.py reachability rows tag a descendant of the commit --to names, so a tag above it reads as one that reaches it and a tag below it keeps the plain words, and --points-at for --contains no longer survives (#1019) #1107 itself touches the two test files.
  • The refreshed body is true.
    • The stat against 7e00834c is 197 added and 4 removed.
    • The head's tree is 615e571b.
    • The commit the writer ran on, 7186f3cf, has the same tree 615e571b and the same parent 01fcb259, so its runs count for the pushed head.
    • Main moved by 9 files since 7e00834c, with bump-leg.py at 9 lines added and the --contains line now at 1214.
    • closingIssuesReferences is [1019], and the only keyword in the text is Closes #1019. body-guard.py exits 0.

Could not verify

  • The writer's 42 passed and 24 passed. I did not re-run them: the change is docstrings only and the AST is identical. Round 1 measured 66 at the head on 2.43 and on 2.47.1, and on the merge.
  • git 2.55 (CI).

(Posted by lane openRepoShape-5's coordinator. rv1104-1107 is lane 5's independent opus reviewer; it wrote none of #1107. The PR stays a DRAFT until the v1.2.0 tag.)

Lane: openreposhape-5 (openRepoShape-5)

@sourcery-ai

sourcery-ai Bot commented Oct 23, 2026

Copy link
Copy Markdown

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 4 hours and 42 minutes by commenting @sourcery-ai review. Upgrade to get a review now.

@brettheap
brettheap merged commit b055bb7 into main Oct 23, 2026
5 checks passed
@brettheap
brettheap deleted the fix/1019-points-at-row branch October 23, 2026 19:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tests: no reachability row tags a descendant of the pinned commit, so --points-at in place of --contains survives in bump-leg.py and family.py

1 participant