Repository navigation
family.py, scaffold-project.py and bump-leg.py: init's plan rows and seven refusals name a holder, a folder, a lock, a remote path or a root as it is, so an ESC, a line break or U+202E in a path reaches the terminal as #692's refusals no longer let it #766
Description
Goal: every path a plan row or a refusal of these three tools names is spelled as shown_path spells it (as it is, and as Python's repr spells it where a character in it is not printable), so a path a person supplied, or git keeps, cannot forge a line or send an escape sequence.
Filed by lane openRepoShape-5, from rv666-692's review of #692's PR (#731, head ab55c29f; its two checklist candidates, "Left out, and why") and from rv672's candidate 3 on #672's PR (#746: {root} printed raw).
Lands after: lane 5's #692 (PR #731, fix/692-raw-folder-paths), which spells a path in folder_cannot_be_made, refuse_unmakeable_folder, make_folder, scaffold-target-exists, family-target-exists and family-operation-in-progress, and #672 (PR #746, fix/672-bump-leg-branch-and-push-line), which spells each dirty file's name. #731 uses shown_source, which since #634 returns repo_shape.shown_path(source); this issue calls shown_path directly. Line numbers are at main 66b230ed.
Lines
-
family.py init's plan prints the holder and the folder raw.scripts/family.py:1804-1807:print(f"landing {holder} (--work-dir: no family folder)"),print(f"folder {folder} a plain folder, NOT a repository")andprint(f"landing {holder} the holder clone, inside it"). Measured by w692 onab55c29f: a--work-dirholding a line break and thenWARNING forged-line: pwnedprints that forged line as a line of the plan, on a dry run and on a run refused after the plan. The plan is on stdout before any refusal, so shape_materialize.py, scaffold-project.py and family.py: the folder refusals andfamily-operation-in-progressname a path asshown_sourcenames it, so an ESC or a line break in a path no longer reaches the terminal (#692) #731's spelled refusals do not cover it. Direction: the two names go throughshown_path, as the refusals now do. - Seven refusals name a path as it is (read from source, not measured; shape_materialize.py, scaffold-project.py and family.py: the folder refusals and
family-operation-in-progressname a path asshown_sourcenames it, so an ESC or a line break in a path no longer reaches the terminal (#692) #731's "Left out" lists them):family-holder-detachedandfamily-on-tracking-branch({family.root},scripts/family.py:774-784),family-index-locked(lock.as_posix()in the detail,:6349-6353; therm -fin its remedy is quoted, the detail is not),family-landing-in-standard({holder},{checkout}and{beside / family / family},:1457-1470),family-holder-already-here({into}and{into / family},:1504-1508),family-remote-exists({target}, which is a local remote path under--local-remote-dir,:1913-1915) andscaffold-remote-exists({target},scaffold-project.py:569-572). Each names a path the person supplied or git keeps, so a folder named with a line break or an ESC reaches the terminal the way scaffold-project.py, family.py and shape_materialize.py: #622's folder refusals print a folder name raw, so an ESC or a line break reaches the terminal, andfamily-operation-in-progressprints a holder's path across two lines #692's measured. Direction: the spelling shape_materialize.py, scaffold-project.py and family.py: the folder refusals andfamily-operation-in-progressname a path asshown_sourcenames it, so an ESC or a line break in a path no longer reaches the terminal (#692) #731 gives its sites, in each; where a name is only ever a GitHuborg/repo(a remote that is not local) leave it as it is. -
bump-leg.py's refusals print{root}raw.scripts/bump-leg.py:1129(bump-leg-root-dirty),:1192and:1199(bump-leg-root-detachedandbump-leg-on-tracking-branch) and:1697(the dry run's "the real run needs a branch"). Measured by rv672 ona5d473ee: a root underr, U+202E,o, U+009B,ot/put both characters raw into the first line of thebump-leg-root-dirtyrefusal. bump-leg.py: the branch is read in full and pushed asrefs/heads/<b>:refs/heads/<b>, dirty names are spelled,committedlists what git holds, and the dry run says it runs no validator (#672) #746 spells each listed file name (spelled(line),a5d473ee:scripts/bump-leg.py:1162-1163) and not the root on the line above them.repo_shape.shown_path(scripts/repo_shape.py:2875, Pinned shape copies: AGENTS-shape.md'sCONTRACTS_DIRsentence is false for every scaffolded root, and the chain WARNING offers remedies the scaffold lacks (one shape bump before the v1.1.0 tag) #610) is the spelling to use and is already imported by the tools that print paths.
Verify: tests/test_dry_run_parity.py (the SCAFFOLD_CASES and FAMILY_CASES rows #731 adds), tests/test_family_refuse_up_front.py, tests/test_family_init_landing.py, tests/test_family_dry_run.py and tests/test_bump_leg_branch_and_push_line.py pass on Linux. One new row per line: a path with an ESC, and one with a line break and then WARNING forged-line: pwned, in each of the plan and the seven refusals (a --work-dir or --into for the family rows, a root for bump-leg), asserting exit status, no ESC and no second line starting WARNING, and that a printable path is named byte for byte so no existing row changes. POSIX only for the names the disk must hold (skipped where it refuses them).
Overlaps. fix/692-raw-folder-paths (#731) edits scripts/family.py, scaffold-project.py and scripts/shape_materialize.py; fix/672-bump-leg-branch-and-push-line (#746) edits scripts/bump-leg.py; six other open branches edit scripts/family.py (#646, #684, #686, #688, #689, #693), and fix/693-local-remote-dir also scaffold-project.py; lane 4's #688 (init values: --created-by and GIT_AUTHOR_NAME printed in the plan) edits the same plan block, so read its head before starting. None of these files is a pinned copy.
Parent: #459 (family.py, scaffold-project.py) and #458 (bump-leg.py).
Milestone: v1.1.1. Each needs a directory named with a control character, which the person made; nothing is lost, and none is a regression.
🤖 Generated with Claude Code
Activity
brettheap commentedon Oct 17, 2026
CLAIMED — lane openRepoShape-2, session 81650e29-c906-4fe8-96c9-55580bfe0c44@Eagle, 2026-10-17T10:31:57Z, for #766
Logged in
lanes/log/openRepoShape-2.mdat95a3c3283eb5b23f1cf3b4ca9c7c1df99e16981b.The three reads (lane-collision-protocol Rule 1):
family.py and #766's tests: `family-holder-already-here` and `bump-le…
Merge lane 5's #746 final head a5139a3 (fix/672-bump-leg-branch-and-p…
Merge main fc86720 after #731 landed as d66a905 and #746 as fc86720
family.py: one import of shown_path, now that #688 has landed beside #…
Merge main d6071c7 into fix/765-branch-read-bytes: both sides' tests …
update-shape.py, family.py and bump-leg.py: the branch HEAD is on is …