Repository navigation
bump-leg.py: a root on +x is told push -u origin +x (forced), a branch named like a tag passes bump-leg-on-tracking-branch, bump-leg-root-dirty prints C1 controls raw, and committed <sha>: lists paths the commit lacks #672
Description
Goal: bump-leg.py reads the root's branch the way git spells it, prints a push line that cannot force-push or miss the branch, shows file names the way the other tools do, and says what the commit holds.
Filed by lane openRepoShape-5 on Brett Heap's word of 2026-10-16T12:17:02Z ("#458's update-shape/bump-leg (~22)"), from lines on #458: the body (AT-scaffold N9), 6172776355 (N9's other half), 6175499929 (the dry run), 6214544478 (the raw controls), 6214576643 (the push line) and 6214653267 (the validator's environment). Lane 4 judged the comment lines OPEN, or DOUBTFUL for the validator's environment, on main 95780dd (#473 comment 6215386495); N9 was not in its list. Lane 5 re-measured every line on main 879d804 with a scaffolded project and local bare remotes.
Lands after: #647 (fix/647-branch-left-bump-leg-setup, lane 5's own). It edits scripts/bump-leg.py _switch_command, _branch_to_bump_from and the dry run's closing lines in cmd_bump, which this issue's dry-run and push-line changes sit beside.
Lines (path:line at main 879d804; every measurement below was run on that tree, git 2.43.0)
- The NEXT push line can force-push.
scripts/bump-leg.py:1709-1710printsNEXT git -C <root> push -u origin <branch>, and<branch>iscurrent_branch(root)(:1168) used as it is. Measured: a root on a branch named+x(git accepts the name) bumps with exit 0 and prints... push -u origin +x. Git reads+xas the forced refspecx: with a localxand origin'sxat another commit, the pasted line says+ 7dde1f2...6cf8d01 x -> x (forced update)and origin'sxis overwritten. (Tracking: update-shape.py and bump-leg.py, keeping a project's shape copies and leg pins current #458's 6214576643, first line; R421 O3.) - A branch with a tag of its own name is read as
heads/<branch>.current_branch(:1070-1074) usesgit symbolic-ref --quiet --short HEAD, which printsheads/<b>when a tag has that name (measured: branchv1with tagv1givesheads/v1). Measured on a scaffolded root: with a tagmainbeside the tracking branchmain,bump-leg.pyis NOT refusedbump-leg-on-tracking-branch(the compare at :1176 readsheads/mainagainstmain), it commits78951bc6onmain, and printsNEXT ... push -u origin heads/main: a push to the default branch, the thing the refusal exists to stop. The line's own symptom,error: src refspec v1 matches more than onefor a baregit push -q -u origin v1, does not reach bump-leg's line, because theheads/spelling pushes (git-level:push -u origin heads/v1exits 0). (Tracking: update-shape.py and bump-leg.py, keeping a project's shape copies and leg pins current #458's 6214576643, second line, bump-leg half; theupdate-shape.pyhalf is in the issue forapply.) -
bump-leg-root-dirtyprints C1 controls and bidi overrides raw._ensure_root_clean(:1098-1133) shows the dirt asdirt.decode("utf-8", "backslashreplace")(:1126). Measured undercore.quotePath=false(an operator's setting; the tool does not set it): a tracked, modified file namedcaf<U+009B><U+202E>evil.ymlis listed with both bytes raw on stderr (od -c:302 233 342 200 256). With the defaultcore.quotePathgit quotes them itself. (Tracking: update-shape.py and bump-leg.py, keeping a project's shape copies and leg pins current #458's 6214544478.) - The
committedline names the paths staged, and the repair case readsX -> X.:1702printscommitted <sha>:+ thestagedlist,:1643and the commit message body (:1685) print{was[:12]} -> {commit[:12]}, andwasis the gitlink (_recorded_leg_commit, :1206). Measured: after a person moves the gitlink by hand to commit79775b0826f5(committed, so the root is clean) and the pin still names the old one,bump-leg.py --to 79775b0826f5printscommit 79775b0826f5 -> 79775b0826f5, writes the sameX -> Xinto the commit message, and printscommitted 0eadecf07cce: spec, contracts/spec-pin.yaml, whilegit show --name-only HEADlists onlycontracts/spec-pin.yaml. (The body's AT-scaffold N9;update-shape.py's half is in the issue forapply.) - A dry run over a pin its validators already refuse exits 0.
--dry-runreturns at :1666-1678 before any validator runs, and its closing paragraph does not say so. Measured: anote:line followed by a tab-indented line appended tocontracts/spec-pin.yaml(committed):validate-pins.pyexits 2yaml-unparsableon the tree as it stands,bump-leg.py --dry-runexits 0 and prints the move, and the real run exits 2bump-leg-validators-redand rolls back. (Tracking: update-shape.py and bump-leg.py, keeping a project's shape copies and leg pins current #458's 6175499929, last line; bump-leg.py:--dry-rungives the real run's answer, so--toan annotated tag's own id is peeled to the commit it names in both runs, and a pin layout the rewrite refuses is refused by the dry run too (#534, #438) #561 closed the split only for layoutsrewrite_pinjudges.)
Rides along (backlog; tick it on #458 if the same change covers it)
-
run_validatorstarts the root's own validator withsiteon and the whole environment.:1000-1018runs[sys.executable, script]withcaptured_child_environment()(scripts/shape_materialize.py:256-276:os.environplus the encoding), where the doctor's children start with-Sand withoutPYTHONPATH(shape-doctor.py:1622-1627, shape-doctor.py:run_validatorstarts a root's OWN validator under the doctor's interpreter withsiteprocessing on, so a.pthin the operator's user site or in a virtualenv's site-packages that names a directory holdingrepo_shape.pystands in for the copy the root lost, and themanifestrow saysok ... passesfor a gate that fails in CI onModuleNotFoundError: the false pass #238 removed and #306 closes forPYTHONPATHalone #342). Measured, so it is not a verdict bug: with the root'sscripts/repo_shape.pylost (committed deletion),bump-leg.pyreaches the gate (it does not refusecopy-missingfirst); withPYTHONPATH=<standard>/scriptsthe gate imports the standard's copy and reportsFINDING shape-copy-missing(exit 1), without it the gate dies inModuleNotFoundError(exit 1): red either way, andvalidate-pins.pynames every pinned copy that is absent, so no shipped validator is let through. The two tools differ in the message only. (Tracking: update-shape.py and bump-leg.py, keeping a project's shape copies and leg pins current #458's 6214653267; lane 4: DOUBTFUL.)
Direction
- Push line and branch read: read HEAD's branch without
--short(git symbolic-ref --quiet HEAD, striprefs/heads/), so a tag cannot change the name and the tracking-branch compare sees the real branch, and print the push as a refspec no name can bend:refs/heads/<branch>alone is enough (measured:git push -u origin refs/heads/+xpushes+xunforced and leaves origin'sx;refs/heads/v1pushes beside a tagv1), or R421'srefs/heads/<b>:refs/heads/<b>(measured onv1andx; it sets the upstream). One form for the three tools:update-shape.pyhas the same line (theapplyissue), andfamily.py's is on Tracking: setup, scaffold, family and the shared helpers, creating projects and families #459. The tests that hold today's form change with it (tests/test_bump_leg.py:235,tests/test_bump_leg_paste_lines.py:279-342), and AGENTS.md:339 and README.md:525 showpush -u origin <branch>as a placeholder line, to be kept or matched. - Raw controls: pass each listed line through
spelledfromscripts/repo_shape.py(it escapes the C0 and C1 controls, surrogates, line and paragraph separators and the bidi controls and leaves every other character as it is;bump-leg.pyalready imports from that module). committedandX -> X: after the commit, name the paths from git (git show --name-only --format= <sha>) and not thestagedlist; where the gitlink already holds the new commit, say that and print the pin's own oldcommit:(read frompin_text) as theold, in the report, the commit message and thecommitline.wasstays what the rollback restores.- Dry run: say, in the closing paragraph, that the project's own validators are not run by a dry run and that the real run can still end
bump-leg-validators-red, rolled back (recommended; no behaviour changes). Running the validators on the unmodified tree instead would putvalidate-pins.py'sgh apicall (make bootstrap,bump-leg.pyandupdate-shape.py applyrun the project's ownvalidate-pins.pyin the operator's environment, so for a project that pins a neutral product with no local checkout of it each one callsgh api repos/<org>/<product>/git/trees/<sha>with the operator's token, thoughbump-leg.pysays "a project runs its own gate offline" andupdate-shape.pythat "applyis otherwise offline": the three callers #297 named and left #411) into a command whose promise is "fetches into the leg and writes nothing else", and would see only what is already red. - Validator environment (rides along): one rule with the doctor,
-Sand noPYTHONPATH, inbump-leg.py'srun_validator; or leave it, since no verdict differs.
Overlaps: #647 (above). No open PR touches scripts/bump-leg.py otherwise. update-shape.py's current_branch (:2223) is a copy of this helper under the same name ("named the same as bump-leg.py's own helper"); the apply issue changes that one.
Mark each line → this issue on #458, and tick it there when this closes. The body's AT-scaffold N9 and the --short line carry a second half in the apply issue: tick them when both close.
🤖 Generated with Claude Code
Activity
bump-leg.py and setup-project.py: the `git switch -c` line says under…
brettheap commentedon Oct 16, 2026
CLAIMED — lane openRepoShape-5, session 50cede29-ab82-4ca3-9f67-d98e1a58c8d2@Eagle, 2026-10-16T14:04:10Z, for #672
Logged in
lanes/log/openRepoShape-5.mdatac7aa9db61c4980debc093e5e63cccce8884327d.The three reads (lane-collision-protocol Rule 1):
bump-leg.py: the branch read keeps a trailing no-break space, the dir…
Merge #723's head (6ba8c5d, fix/663-non-utf8-name-tests) into fix/672…
update-shape.py: `apply`'s rollback line says to make a shut director…
Merge main fc86720 into l3-733-ignored-sections
docs/release-notes/v1.1.0.md: entries for #748, #731, #810, #809, #816,
Merge origin/main (fc86720) into l3-563-docs
docs/release-notes/v1.1.0.md: #746's entry, v1.0.0's staged-paths lim…
docs/release-notes/v1.1.0.md: #795's entry, the list complete to main 6…
family.py, scaffold-project.py and bump-leg.py: `init`'s plan rows, s…