Repository navigation
family.py: the dry runs' member-duplicate-id remedies and init's family-holder-already-here say the branch their lines run on, and that add line is _cited, so each runs as printed from the tracking branch (#1228) - #1264
brettheap wants to merge 2 commits into
Conversation
…`family-holder-already-here` say the branch their lines run on, and that `add` line is `_cited`, so each runs as printed from the tracking branch (#1228) Three lines a remedy printed were reached with no branch read, named none, and were refused `family-on-tracking-branch` when typed where a fresh clone stands (lane 5's review of #1201, SHOULD-1): - `add --dry-run` skips `_checked_branch` and still asks `_checked_member_id`; `bump --dry-run` asks `_checked_bumped_id` before `_branch_said`. Both now take a keyword-only `dry_run` flag, false by default, so the real runs' asks (`_mount_and_pin`, `_checked_bump_target`, `_bump_and_pin`) and their words are unchanged. Set, the remedy puts `_off_the_tracking_branch` before its line or lines, naming the branch the `remove` line's own refusal names (`family/remove-<id>`), and ends with `BRANCH_LEFT`, as `member-already-present` does. - `init`'s `family-holder-already-here` printed `_add_command`'s line between plain backticks. It now comes after `init`'s own branch, `git -C <holder> switch -c family/add-<Project>`, in the same clause, and through `_cited`, so a holder under a folder named with a backtick prints it whole. The switch line is `_add_branch_switch`, shared with the NEXT STEPS block (same bytes there), and the clause's words are `_branch_clause`, which `_off_the_tracking_branch` now calls. The quoting test's `NAMED` moves one host-quoter call from `_print_next_steps` to `_add_branch_switch`; README's sentence already lists `switch -c`. Rows: each dry run's lines and `init`'s `add` are typed as printed from the tracking branch: alone they are refused there, after the `switch -c` they go through. The two `both_runs` rows of `member-duplicate-id` and `add`'s dry-run parity case now pin the real run's words as before and the dry run's with the clause. NIT-1: the row with a space in its name asserts the `switch -c` line, which kills the branch-from-name mutant. NIT-2: `typed()` says it asserts the interpreter and does not look it up. Closes #1228 Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideImplementationUpdate duplicate-ID refusal handling so dry runs include the required branch clause and trailing guidance while real-run wording remains unchanged.
Factor branch-clause and init branch-switch formatting, then use the shared switch in NEXT STEPS and the holder-already-here remedy so its add command is cited and executable as printed.
TestsAdd executable remedy checks for dry-run duplicate-ID and init refusals, including tracking-branch behavior, quoting, and branch names derived from row IDs.
Separate real-run and dry-run expectations for bump duplicate-ID refusals and verify their remedies differ only by the dry-run branch guidance.
Add a focused add dry-run duplicate-ID test that pins parity of refusal details and the additional branch clause in its remedy.
Update quoting-call accounting to reflect that the shared init branch-switch helper now owns the switch command.
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-5, 2026-11-08T17:36Z) Independent review of #1264 (#1228) by rv1264, lane 5's opus reviewer, which wrote none of it: READY at Independent review of #1264 (#1228) for lane openRepoShape-5, by rv1264 (opus), at b54520c: READY No MUST and no SHOULD. Two NITs and two observations, none of which needs the head to move. The head is still FindingsNIT-1: the commit adds a fourth bare
|
| id | result |
|---|---|
| M1, M2, M4a, M4b, M6, M7, M11, M14 | killed, 3 each |
| M3, M8, M9, M12 | killed, 2 each |
| M5 | killed, 1 ([ab]`) |
| M15 | killed, 11 |
| M10a, M10b | survived (NIT-2) |
rv1201's R1-R7: all killed, matching the body. R1: 15 of 17. R2: 6. R3: 1 (NAMED). R4: 4. R5: 1 (the NIT-1 row). R6: 4, 3 of them in test_family.py. R7: 1.
My own O1-O10: all killed.
| id | mutation | result |
|---|---|---|
| O1 | (b)'s branch built from the bumped row | killed, 3 |
| O2 | (b) names two verbs | killed, 3 |
| O3 | (c) names bump and remove |
killed, 2 |
| O4 | (a)'s clause said before "If ... renamed at the forge," | killed, 3 |
| O5 | _checked_member_id defaults dry_run=True |
killed, 2: _mount_and_pin's linked-manifest and moved-id rows |
| O6 | _checked_bumped_id defaults dry_run=True |
killed, 3 |
| O7 | family/add-<project> lowercased |
killed, 3, including test_init_next_steps_spell_the_branch_as_its_member |
| O8 | the real add gets the clause too, against family.py: the add, bump and remove lines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, and member-already-present prints its remove (#1127) #1201's own rows only |
killed, 4 in test_family_add_member_id.py |
| O9 | the real bump gets the clause too |
killed, 3 |
| O10 | _branch_clause always puts its switch between backticks |
killed, 2 |
So the real runs' words are pinned byte for byte by rows family.py: the add, bump and remove lines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, and member-already-present prints its remove (#1127) #1201 already had.
Reading and the body's other claims
- The diff is 5 files, +442/-57. All 442 added lines are ASCII and at most 79 columns.
- No added line uses
ast.dump,repr(,sys.versionor!r. - No new assert joins conditions with
andoror. The one such assert,test_family_dry_run.py:1431, iseac177af9's:1394. - Every
_family_lineand_add_commandsite either follows_checked_branchin its real run or carries the clause, so the Goal's "every" holds forfamily.py. - No changed remedy wording appears in README,
docs/, AGENTS.md or the templates. - The body has one closing keyword (
Closes #1228), andbody-guard.pyexits 0 on the live body. Sourcery appended its summary after the writer's footer.
Gates
- All runs used
run10.shon Python 3.12.3, with git-filter-repo fromL/tools/venv247(2.47.0 by the briefs; its--versionprintsa40bce548d2c). - Every head run's START line reads
dirty=0. - No run had a skip or a failure.
| run | git | result | log |
|---|---|---|---|
gate A, 13 files: the issue's 8, test_family_dry_run.py, and the 4 hygiene files that exist (test_path_guard_hygiene.py does not) |
2.43.0 | 663 passed | logs/gateA243.log |
| gate A | 2.47.1 | 663 passed | logs/gateA247.log |
| gate B, the body's 9 related files | 2.43.0 | 389 passed | logs/gateB243.log |
predicted restack C2 4091dda72 (tree 240aabff, main be21751c3), gate A + test_path_guard_hygiene.py |
2.43.0 | 785 passed | logs/merged243.log |
predicted restack C3 068d2e4e0 (tree f4d393bf, main 46fe6ac19), the same 14 files |
2.43.0 | 785 passed | logs/merged243b.log |
C1, C2 and C3 are unreferenced scratch commits made with commit-tree (C1 is 7eb84ddcd: head + #1117's head).
Merge-trees
Logs: logs/merge-trees.log, merge-trees-2.log, merge-trees-3.log.
git merge-tree --write-tree <other> b54520cea |
result |
|---|---|
#1201, eac177af9 |
clean, tree f64b574e |
#1117, 219e57c28 |
clean, tree 43171572 |
#1164, 74096eb47 |
exit 1, docs/handbook.html |
main be21751c3, 46fe6ac19, 9ec0dc79c |
exit 1 on each, docs/handbook.html |
- The handbook conflict comes from family.py: the
removethat bothmember-duplicate-idremedies name and theaddthatbump'smember-unknownnames carry--family-root <holder>, quoted for the host, so each runs as printed (#1005) #1117's old head10dc011c9, which family.py: theadd,bumpandremovelines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, andmember-already-presentprints itsremove(#1127) #1201 sits on.- Main against
eac177af9, and main against10dc011c9, give the same conflict. - With the conflict labels stripped, the three conflicted handbooks are byte-identical (sha256
827bc87c), with 7 hunks. - family.py: the
removethat bothmember-duplicate-idremedies name and theaddthatbump'smember-unknownnames carry--family-root <holder>, quoted for the host, so each runs as printed (#1005) #1117's current head merges cleanly with each main (trees3fbd8d4fand4b8666df). - This commit touches no file under
docs/.
- Main against
- The predicted restack is clean on every main read. It differs from main in exactly the stack's 12 files. Against family.py: the
add,bumpandremovelines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, andmember-already-presentprints itsremove(#1127) #1201's own restack, it is this commit's diff hunk for hunk. - C1 with family.py and the family root's siblings.py: a holder's
.gitmodulesthis user cannot read, as through a link into a folder it cannot search, isgitmodules-unreadable, never no section forremove's rollback or no url formake siblings(#1110) #1164 is clean (treef8ed4c5e). C2 with family.py and the family root's siblings.py: a holder's.gitmodulesthis user cannot read, as through a link into a folder it cannot search, isgitmodules-unreadable, never no section forremove's rollback or no url formake siblings(#1110) #1164 conflicts intests/test_path_guard_hygiene.py, but that is family.py and the family root's siblings.py: a holder's.gitmodulesthis user cannot read, as through a link into a folder it cannot search, isgitmodules-unreadable, never no section forremove's rollback or no url formake siblings(#1110) #1164's own conflict with main.
Not done, and state
- Main moved to
9ec0dc79cat the end. It changesadopt-project.py,scripts/shape_materialize.py(whichfamily.pyimports) and two adopt tests, none of them in the stack's or the gate's lists. The restack is still clean (tree44ba6c8d), but I did not gate it on this main. - The body's merge-tree section names main
be21751c3, which is now stale. The results are unchanged. - CI on
b54520ceaat 17:35Z:package,tests-git-bashand SonarCloud passed.testshas been running since 16:22Z. closingIssuesReferencesis[], which is expected while the base is family.py: theadd,bumpandremovelines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, andmember-already-presentprints itsremove(#1127) #1201's branch. After the retarget to main, check it lists family.py: three lines a remedy prints name no branch and are refusedfamily-on-tracking-branchwhen typed there:add --dry-run's andbump --dry-run'smember-duplicate-id, andinit'sfamily-holder-already-here(#1201 follow-up) #1228 only.- Not run: the full suite, gate B on 2.47.1, M8's NEXT files, a real Windows host, macOS, and Python 3.14.
- Worktrees:
/workspace/projects/openRepoShape-worktrees/rv1264-reviewandrv1264-mutare both clean and detached atb54520cea, with no probe files left in them. - Notes and logs:
/workspace/projects/.lane-worktrees/openRepoShape-5/work/rv1264/(STATE.md,logs/,probe/,tools/).
Lane: openreposhape-5 (openRepoShape-5)
…ready-here` builds its clause before it raises, so "Remediation: " is no fourth bare copy, and two rows pin that each dry run's clause names its branch from the row's id (#1228) rv1264's two NITs on #1264, both at b54520c: - NIT-1: the (c) hunk wrote `"Remediation: "` beside `_branch_clause`'s call, a fourth bare copy of that literal after #1201's three, which SonarCloud reports on #1264 as python:S1192. `_landing` now holds the clause in a local and says `f"Remediation: {clause}"`. The refusal's words are byte for byte what they were, for a plain folder and for one named with a backtick (probed before and after, same script path). - NIT-2: mutants M10a and M10b, the dry runs' clause branch built from the row's name and not its `id:`, survived because every fixture row's id is its name lowercased. Two rows rename a row by hand (`IRRS` to `Orrs`, `IRSS` to `Osss`) and assert the clause's `switch -c` names `family/remove-irrs` and `family/remove-irss`. The edit those rows and the `IR RS` row share is now `renamed_by_hand`. Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
(lane openRepoShape-5, 2026-11-08T18:25Z) Delta check of #1264's review round, Delta check of #1264 (#1228) for lane openRepoShape-5, by rv1264b (sonnet), b54520c..43d2e8b: READY There is no MUST and no SHOULD. I found four observations, none of which needs the head to move. I was read-only on the PR and ran no subagents. I made no GitHub writes and no pushes.
FindingsNIT-A (Goal paragraph is stale). The NIT-B (a no-op Observation C (main moved again). The body's merge-tree section names main Observation D (CI). Per Evidence for each check1. The delta is what the body says.
2. NIT-1.
3. NIT-2. Baseline: 86 ids (the 19-id file, the 2
The swap matches the body's "2 failed, each on 4. My own mutants. Each ran alone in
Every swap and mutant was restored and checked by sha256. 5. The writer's four edits beyond the brief.
6. Gates at the head. Python 3.12.3. The runner puts git-filter-repo from
684 is the body's 665 (13 files) plus 7. Merge-trees (
8. The body's new section and counts.
Not doneThe full suite, gate B, Notes and logs are in Lane: openreposhape-5 (openRepoShape-5) |



Goal
#1228: every
add,bumpandremoveline that afamily.pyremedy prints says the branch it runs on, whichever route reached the refusal. The dry runs ofaddandbumpthat end inmember-duplicate-id, and thefamily-holder-already-herethatinitends in, now say it as #1201 makesmember-unknownandmember-already-presentsay it, and thataddline is_cited. rv1201's one surviving mutant of #1201's rows, R5, is killed.Stacked on #1201 at
eac177af(itself on #1117 at219e57c2). The PR's base is #1201's branch,fix/1127-family-followups-stacked, so it shows two commits:b54520cea(5 files, 442 insertions and 57 deletions) and the review round's43d2e8b26, 5 files, 503 insertions and 64 deletions in all (git diff --stat eac177af9 43d2e8b26). It answers lane 5's review of #1201 (issuecomment-6372152028): SHOULD-1's optional code fix, and NIT-1 to NIT-3.What held on #1201's head
At
eac177af9, as rv1201 measured it (probes p6, p7, p9) and as this PR's rows show failing there (below), three printed lines were reached with no branch read, named none, and were refusedfamily-on-tracking-branchwhen typed where a fresh clone stands:add --dry-run'smember-duplicate-id. Under--dry-run,cmd_addskips_checked_branchand still asks_checked_member_id. Its remedy printed aremoveline and abumpline and no branch.bump --dry-run'smember-duplicate-id._bump_dry_runasks_checked_bumped_idbefore anything reads the branch. Its remedy printed aremoveline and no branch.init'sfamily-holder-already-here. Its remedy printed_add_command's line between plain backticks: no branch clause, and not through_cited, so under a folder named with a backtick the first span ended at the path's backtick (RV1117's N6 shape).What changed
scripts/family.py, (a) and (b)._checked_member_idand_checked_bumped_idtake a keyword-onlydry_run, false by default.cmd_addpassesargs.dry_run, and_bump_dry_runpasses true. The real runs' calls (_mount_and_pin, andcmd_bump's) are unchanged, and so are their words. Set, the remedy puts_off_the_tracking_branchbefore its line or lines and ends withBRANCH_LEFT, asmember-already-presentdoes. The clause names "bumpandremoverefuse" for (a), whose remedy names both lines, and "removerefuses" for (b). Itsswitch -cnamesfamily/remove-<id>, the branch theremoveline's own refusal names, so the clause and the refusal after it agree.scripts/family.py, (c). The remedy now says the clause withinit's owngit -C <holder> switch -c family/add-<Project>, then theaddthrough_cited, thenBRANCH_LEFT. The issue left the branch word to the owner. This PR takesfamily/add-<Project>, bare, as the NEXT STEPS block prints it: quoted, it would read as a value._add_branch_switchspells thatswitch -cline, and NEXT STEPS now calls it too, with the same bytes as before._branch_clauseholds the clause's words around aswitch -cline already spelled, and_off_the_tracking_branchnow calls it.member-duplicate-idasks and_off_the_tracking_branch's docstring now say that a dry run states the refusal before the branch is read, and a real run after it.tests/test_readme_windows_quoting_sentence.py.NAMEDmoves one host-quoter call from_print_next_stepsto_add_branch_switch, andFAMILY_LINESmoves theswitch -ckind with it. README's sentence already listsswitch -c, so README.md anddocs/handbook.htmlare unchanged. The changed remedies' words appear in no doc. The release notes namemember-duplicate-idas a code (v1.0.0) and thefamily/add-<Project>spelling (v1.1.0). AGENTS.md andtemplates/family-root/showgit -C <path> switch -c family/add-<Project>as NEXT STEPS print it. None of that changes.tests/test_family_remedy_lines.py. The new rows type each line as printed from the tracking branch. Typed alone, each line is refusedfamily-on-tracking-branch. Typed after the clause'sswitch -c, it exits 0, and the holder is clean and its gate green:test_add_dry_runs_duplicate_id_lines_run_from_the_tracking_branch,[remove]and[bump];test_bump_dry_runs_duplicate_id_remove_runs_from_the_tracking_branch;test_init_holder_already_heres_add_runs_as_printed_after_its_switch,[plain]and[a`b].test_a_rows_name_with_a_space_is_one_word_in_both_linesnow asserts theswitch -cline,family/bump-irrs, which is built from the row'sid:. That kills R5, which builds it from the name and getsfamily/bump-member.typed()'s docstring says that it asserts the interpreter's name and does not look it up on PATH.tests/test_family_bump_to_checks.py. Its twomember-duplicate-idrows used to assert that both runs print the same words. They now pin the real run's words as ateac177af9and the dry run's with the clause (both_refused,duplicate_id_refusal(dry_run=True)).tests/test_family_dry_run.py.duplicate-idleaves the parametrized "same under--dry-run" row and gets a row of its own,test_add_dry_runs_duplicate_id_names_the_branch_the_real_run_has_read. It pins the same code, detail, stdout and exit for both runs, and the dry run's remedy as the real one with the clause added andBRANCH_LEFTat the end.member-already-presentstill suggestsfamily/bump-<id>for both of its lines. (a)'s clause likewise names one branch,family/remove-<id>, for both itsremoveand itsbump. Either branch works for both verbs, since anything but the tracking branch does.Review round (rv1264: two NITs)
rv1264 (opus) found this PR READY at
b54520ceawith two NITs (issuecomment-6724775227). The round's commit,43d2e8b26, is the PR's second: 2 files, 67 insertions and 13 deletions (git diff --stat b54520cea 43d2e8b26),scripts/family.pyandtests/test_family_remedy_lines.py. The PR now shows both commits, 5 files, 503 insertions and 64 deletions (git diff --stat eac177af9 43d2e8b26). The Goal,What held on #1201's head,What changedandRows that fail on #1201's headdescribeb54520cea, the first commit. Mutants, Runs, Merge-trees and one bullet of Limits are read again at43d2e8b26."Remediation: ". SonarCloud reported it on this PR as python:S1192 (keyAaG7-FM2kL7yTWvgodeV): the (c) hunk's"Remediation: "atscripts/family.py:2530was a fourth copy of that literal after family.py: theadd,bumpandremovelines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, andmember-already-presentprints itsremove(#1127) #1201's three (:1713,:6577,:9497)._landingnow builds the clause first,clause = _branch_clause(_add_branch_switch(into / family), "`add` refuses"), and the third argument of theRefusalstartsf"Remediation: {clause}". family.py: theadd,bumpandremovelines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, andmember-already-presentprints itsremove(#1127) #1201's three are untouched, andgrep -n '"Remediation: "' scripts/family.pylists exactly those three. SonarCloud's analysis of43d2e8b26is not read here.probe/remedy.sh) raninitover a family folder that already holds its holder, once withb54520cea'sfamily.pyand once with this commit's, swapped into the same detached tree, from the same working directory and by the same script path, for a plain folder and for one nameda`b. The two refusal lines (theREFUSEDline and the remediation) are identical undercmp: plain 1156 bytes,a`b1148 bytes, each with exit 2 and an empty stdout. The swapped run printed oneWARNING ... DIRTYline before them, which the swap itself caused (it dirtied that tree); the file was restored with its sha256 checked (88ad8766f4e2...) andgit statusclean.test_init_holder_already_heres_add_runs_as_printed_after_its_switch[plain]and[a`b]compare the remediation with the expected words and pass at43d2e8b26(gate A, below), on git 2.43.0 and 2.47.1.id:is its name lowercased, so a branch built from the name is the same word. Two rows intests/test_family_remedy_lines.py, after (a) and (b)'s dry-run rows, rename a row by hand to a valid name that is not its id, and assert the clause'sswitch -cline as printed:test_add_dry_runs_clause_branch_is_made_from_the_rows_id:IRRSrenamedOrrs.add --dry-runofIRRS2is refusedmember-duplicate-id, and the clause namesfamily/remove-irrs, notfamily/remove-orrs.test_bump_dry_runs_clause_branch_is_made_from_the_other_rows_id:IRSSrenamedOsss.bump --dry-runofIRRSto the commit whoseproject.yamlrecordsirssis refused the same way, and the clause namesfamily/remove-irss, notfamily/remove-osss.project:line is a helper,renamed_by_hand(holder, project, name), which theIR RSrow (RV1201's NIT-1) now calls too, so it is written once. That row's asserts are unchanged.family/remove-orrsagainstfamily/remove-irrs), and M10b alone fails the second row and only that row (family/remove-osssagainstfamily/remove-irss): 1 failed, 18 passed in each run of the file's 19 ids.scripts/family.pycame back to sha256b7f0d8c5f2db...after each, withgit statusclean.add,bumpandremovelines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, andmember-already-presentprints itsremove(#1127) #1201's production code. Witheac177af9'sfamily.py(sha256b8199379c23f...) swapped in under this commit's tests, in the same detached tree: 2 failed, each on[] == [the switch line], since family.py: theadd,bumpandremovelines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, andmember-already-presentprints itsremove(#1127) #1201's dry runs print no clause. Restored (sha256b7f0d8c5f2db...),git statusclean.Rows that fail on #1201's head
The commit's new and changed rows (73 ids: the 5 new
test_family_remedy_lines.pyids and the NIT-1 row, the 2test_family_bump_to_checks.pyrows,test_family_dry_run.py's new row and the 18 ids of its parametrized row, and the 46 ids oftest_readme_windows_quoting_sentence.py) were run witheac177af9'sscripts/family.py(sha256b8199379c23f...) swapped in under this commit's tests, in a second worktree. The file was restored after the run and its sha256 checked:88ad8766f4e2..., matching, withgit statusclean. Result: 9 failed, 64 passed, every failure anAssertionErroron the remedy text:eac177af9'sfamily.py[remove],[bump]BRANCH_LEFT[plain],[a`b]test_family_dry_run.py's new rowtest_family_bump_to_checks.pyrowsNAMED[scripts/family.py]_add_branch_switchid:(the assert kills R5, below), and the rest pin unchanged behaviourMutants
Each was applied alone in the second worktree at
b54520cea(clean), run through lane 5's runner, and restored, with the sha256 of every touched file checked andgit statusclean after each. The #1228 mutants ran against all 73 ids above (84 with the rest oftest_family_remedy_lines.py); M8 also ran againsttests/test_git_bash_dialect.pyandtests/test_family_switch_branch_exists.py, which read NEXT STEPS.[remove],[bump],test_family_dry_run.py's new rowtest_family_bump_to_checks.pyrows[plain],[a`b]dry_runignored inadd(always false)dry_runignored inbump(always false)_cited: theaddbetween plain backticks[a`b]bump's, notremove'sBRANCH_LEFTBRANCH_LEFTid:test_add_dry_runs_clause_branch_is_made_from_the_rows_id(it survived atb54520cea, 84 passed)id:test_bump_dry_runs_clause_branch_is_made_from_the_other_rows_id(it survived atb54520cea, 84 passed)dry_runinverted: the real run gets the clauseswitch -cnames--into, not the holderremoverefuses"_branch_clauseprints itsswitch -cbaref"Remediation: {clause}"without its clause[plain],[a`b]_citedalways prints the command bare_family_linequotes forshon every hostNAMED[scripts/family.py]member-already-presentloses its clausemember-already-present's branch from the nametest_a_rows_name_with_a_space_is_one_word_in_both_lines(it survived ateac177af9)member-already-presentnames the typed projecttest_family.py)cdfrom the sentencetest_the_kinds_of_family_lines_are_the_ones_the_sentence_listsM1 to M9 are n1228's, and they cover the issue's list: the clause dropped from each of (a), (b) and (c),
dry_runignored, and_citeddropped from (c). M10a to M15 are the first commit's own, and M3b the second's. R1 to R7 are rv1201's again: R1 to R6 againsttest_family_remedy_lines.py, with R6 also againsttest_family.py's spelling row, and R3 and R7 against the sentence file.scripts/family.pycame back to sha25688ad8766f4e2...after each run, README.md to71d9fecafbd6...anddocs/handbook.htmlto1d01f0823e60...after R7, andgit statuswas clean after the last one. At43d2e8b26, M10a, M10b and M3b each ran alone in the same detached tree, clean at that commit, overtests/test_family_remedy_lines.py's 19 ids (1 failed and 18 passed, 1 failed and 18 passed, 2 failed and 17 passed), withscripts/family.pyback to sha256b7f0d8c5f2db...after each andgit statusclean after the last. The other rows' results are those ofb54520cea, which rv1264 re-ran and matched.Runs
All through lane 5's
run10.sh: aghstub first on PATH,GH_TOKENbogus, an emptyGH_CONFIG_DIR, a blank git identity,GIT_SSH_COMMAND=false,GIT_CONFIG_GLOBAL=/dev/null,EMAILexported, and git-filter-repo 2.47.0 (CI's pin) on PATH. EverySTARTline readshead=b54520cea716b44df1a95139a46901a478d616fdanddirty=0, with three exceptions. The swapped run and each mutant readdirty=1, the one file changed, except R7, which changes two and readsdirty=2. The predicted merge's run names its own head. Python 3.12. The second commit's runs, marked in the table, readhead=43d2e8b26...anddirty=0, except the mutants' and the swapped runs, which readdirty=1, and the pre-commit run of the new rows, which readhead=b54520cea...anddirty=2.test_family_remedy_lines.py,test_readme_windows_quoting_sentence.py,test_remove_add_remedies_family_root.py,test_bump_remedies_family_root.py,test_family.py,test_family_add_member_id.py,test_family_bump_to_checks.py,test_family_printed_quoting.py),test_family_dry_run.py, which this commit changes, and four of the hygiene five (test_repo_hygiene.py,test_agents_md_codes.py,test_cli_reference.py,test_pinned_copy_text.py). The fifth,test_path_guard_hygiene.py, is not at this head. family.py: theadd,bumpandremovelines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, andmember-already-presentprints itsremove(#1127) #1201 sits on family.py: theremovethat bothmember-duplicate-idremedies name and theaddthatbump'smember-unknownnames carry--family-root <holder>, quoted for the host, so each runs as printed (#1005) #1117's older head10dc011c9, whose base6da757cbepredates 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. family.py: theremovethat bothmember-duplicate-idremedies name and theaddthatbump'smember-unknownnames carry--family-root <holder>, quoted for the host, so each runs as printed (#1005) #1117's current head,219e57c28, has it from its merge of main. It runs on the predicted merge instead.test_family_init_landing.py,test_git_bash_dialect.py,test_family_switch_branch_exists.py,test_family_tracking_branch.py,test_validate_family_branch_step.py,test_validate_family_remedies_typed.py,test_validate_family_duplicates.py,test_family_members_not_a_list.py,test_family_bump_to_a_tag.py.logs/rows.logeac177af9'sfamily.pyswapped inlogs/baserows.loglogs/gateA243.loglogs/gateA243b.loglogs/gateA247.loglogs/gateB243.loglogs/gateB247.logtest_path_guard_hygiene.py(23 files) on the predicted merge, scratch commite22074def(below),head=e22074def...logs/merged243.loglogs/rep-head243-*.log43d2e8b26: the two new rows and theIR RSrow, before the commit (tree dirty)work/n1228e/logs/newrows-head.logeac177af9'sfamily.pyswapped inwork/n1228e/logs/newrows-on-eac177af9.logtests/test_family_remedy_lines.pywork/n1228e/logs/mut-*.logwork/n1228e/logs/gateA243.logwork/n1228e/logs/gateA247.logtests/test_path_guard_hygiene.py(14 files) on the predicted restack, scratch commitf168938c4,head=f168938c4...work/n1228e/logs/restack243.logtests/test_family_init_landing.py, the file offamily-holder-already-here's detailwork/n1228e/logs/init-landing243.logThe 2.47.1 runs put lane 5's git 2.47.1 first on PATH, and each log's first line reads
git version 2.47.1. No run had a skip. The full suite was not run. Gate A of the second commit is 665 ids, the first commit's 663 and the two new rows; git-filter-repo 2.47.0 (CI's pin) was on PATH in every run.tests/test_path_guard_hygiene.pyis still not at this head (see Gate A above), so gate B (the first commit's 9 files) was not run on the second commit, which the brief did not ask for. It changes_landing's remedy and one test file only.Merge-trees
Read on 2026-11-08 at about 17:55Z, after fetching main at
9729315f1, with #1201 ateac177af9, #1117 at219e57c28and this PR at43d2e8b26read back fromgh pr view. Of the 50 open PRs, three others touch this PR's five files: #1117 and #1201 (scripts/family.pyand test files) and #1164 (scripts/family.py). The results are the same on git 2.43.0 and 2.47.1 (work/n1228e/logs/merge-trees-2.log).git merge-tree --write-tree <other> 43d2e8b26eac177af9dca51cc9a756219e57c28584c537c5aa474096eb47docs/handbook.html9729315f1docs/handbook.htmldocs/. family.py: theadd,bumpandremovelines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, andmember-already-presentprints itsremove(#1127) #1201's own head conflicts with main indocs/handbook.htmltoo: it sits on family.py: theremovethat bothmember-duplicate-idremedies name and theaddthatbump'smember-unknownnames carry--family-root <holder>, quoted for the host, so each runs as printed (#1005) #1117's older head,10dc011c9, whose handbook conflict rv1201 traced and family.py: theremovethat bothmember-duplicate-idremedies name and theaddthatbump'smember-unknownnames carry--family-root <holder>, quoted for the host, so each runs as printed (#1005) #1117's current head mends. rv1264 measured the same conflict against three mains and mended it with family.py: theremovethat bothmember-duplicate-idremedies name and theaddthatbump'smember-unknownnames carry--family-root <holder>, quoted for the host, so each runs as printed (#1005) #1117's head.12122952b,46fe6ac19,9ec0dc79c,9729315f1) past thebe21751c3this body named before, to9729315f1(13:47 -04:00). They change 13 files:README.md,docs/handbook.html,adopt-project.py,shape-doctor.py,scripts/shape_advisory.py,scripts/shape_materialize.py(whichfamily.pyimports), a template and six tests. None is one of this PR's five files. One,tests/test_pinned_copy_text.py, is a gate-A file, so the restack gate below runs it as main has it.removethat bothmember-duplicate-idremedies name and theaddthatbump'smember-unknownnames carry--family-root <holder>, quoted for the host, so each runs as printed (#1005) #1117's head (scratch commit02f5c651c, tree584c537c5aa4), then with main9729315f1, gives treedad4a74a1cde(exit 0, scratch commitf168938c4). It differs from main in exactly the stack's 12 files (1404 insertions, 99 deletions). Against the restack ofb54520ceaon the same main (treec6ce0111f823), it differs in exactly this round's 2 files, 67 insertions and 13 deletions, and the changed lines of that delta are this commit's changed lines (work/n1228e/logs/restack-delta.log). The scratch commit with family.py and the family root's siblings.py: a holder's.gitmodulesthis user cannot read, as through a link into a folder it cannot search, isgitmodules-unreadable, never no section forremove's rollback or no url formake siblings(#1110) #1164's head is clean too (tree4ebd3c638f84).tests/test_path_guard_hygiene.py(14 files, which main has) onf168938c4: 787 passed in 401.58s on git 2.43.0 (work/n1228e/logs/restack243.log,head=f168938c4...,dirty=0).removethat bothmember-duplicate-idremedies name and theaddthatbump'smember-unknownnames carry--family-root <holder>, quoted for the host, so each runs as printed (#1005) #1117 lands, family.py: theadd,bumpandremovelines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, andmember-already-presentprints itsremove(#1127) #1201 takes its three merges first. This branch then merges family.py: theadd,bumpandremovelines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, andmember-already-presentprints itsremove(#1127) #1201's new head, which carries them, and the gates run again. If family.py: theadd,bumpandremovelines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, andmember-already-presentprints itsremove(#1127) #1201 has not yet taken them, this branch takes them itself: family.py: theremovethat bothmember-duplicate-idremedies name and theaddthatbump'smember-unknownnames carry--family-root <holder>, quoted for the host, so each runs as printed (#1005) #1117's final head, then family.py: theremovethat bothmember-duplicate-idremedies name and theaddthatbump'smember-unknownnames carry--family-root <holder>, quoted for the host, so each runs as printed (#1005) #1117's squash commit on main, then main.add,bumpandremovelines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, andmember-already-presentprints itsremove(#1127) #1201 lands (its squash isS), this branch takes three merges: family.py: theadd,bumpandremovelines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, andmember-already-presentprints itsremove(#1127) #1201's final head; thenS, keeping this branch's side inscripts/family.py,tests/test_family_bump_to_checks.py,tests/test_family_dry_run.py,tests/test_family_remedy_lines.pyandtests/test_readme_windows_quoting_sentence.pywhere they conflict; then main. The base is then retargeted tomain, the gates run again, andclosingIssuesReferencesmust list family.py: three lines a remedy prints name no branch and are refusedfamily-on-tracking-branchwhen typed there:add --dry-run's andbump --dry-run'smember-duplicate-id, andinit'sfamily-holder-already-here(#1201 follow-up) #1228 and only family.py: three lines a remedy prints name no branch and are refusedfamily-on-tracking-branchwhen typed there:add --dry-run's andbump --dry-run'smember-duplicate-id, andinit'sfamily-holder-already-here(#1201 follow-up) #1228.Limits
id:, and two rows now tell it from the name (mutants M10a and M10b, killed since43d2e8b26; see the review round). Every other fixture row's id is its project lowercased, so only a row renamed by hand tells them apart. The old gap was harmless in effect, as rv1201 found for R5: the clause'sswitch -cneed only leave the tracking branch.python3from PATH, 12 of 12.add,bumpandremovelines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, andmember-already-presentprints itsremove(#1127) #1201's is, since a dry run says the refusal before the branch is read. A person already off the tracking branch reads a clause that does not apply to them.init's<Project>stays a placeholder in the clause'sswitch -c, as NEXT STEPS print it.add's own refusal names the branch lowercased; any spelling works.tests/test_family_dry_run.py::test_bump_dry_run_of_a_repair_to_the_pinned_commit_is_a_move[digest]failed:nothing to do: InkRouter/IRRS is already pinned at ab0fea7e32a2 and every fact agrees.and exited 1._nothing_to_record, agit diff --quiet HEADmade afterwrite_lfrewritesfamily.yamlin place. The row's hand edit keeps the file's size._nothing_to_record,_rewrite_members,force_stageortree_digest. Its onlytree_digestlines are one sentence of_bump_dry_run's docstring, rewrapped. The row has no duplicate id, so thedry_runflag in_checked_bumped_idnever shows.git addandgit commit, thengit diff --quiet HEAD) missed none of 200 rewrites on 2.43.0 and none of 200 on 2.47.1. The failure's log islogs/gateA243.log, and the probe isprobe/racy.py.tests/test_path_guard_hygiene.pyran on the predicted merge only. It is not at this head (see Runs). This commit adds no file-system guard: itsfamily.pyhunks add nois_file,is_dir,exists,is_symlinkoros.pathcall.ast.dump,repr(,sys.versionor!r. The 442 added lines are ASCII and at most 79 columns. None of the commit's new asserts joins conditions withandoror. The one such assert intest_family_dry_run.py(line 1431) iseac177af9's line 1394, moved down.removethat bothmember-duplicate-idremedies name and theaddthatbump'smember-unknownnames carry--family-root <holder>, quoted for the host, so each runs as printed (#1005) #1117 and family.py: theadd,bumpandremovelines its remedies name begin with the interpreter and the script's path, quote the row's name, fill in the family's org, say the branch they run on, andmember-already-presentprints itsremove(#1127) #1201, by the restack plan above.Closes #1228
Lane: openreposhape-5 (openRepoShape-5)
🤖 Generated with Claude Code
Summary by Sourcery
Make family remediation guidance executable from tracking-branch checkouts across dry-run duplicate-ID and init holder-already-present refusals.
New Features:
Bug Fixes:
Enhancements:
Tests: