Repository navigation
scaffold-project.py holds --tracking-branch only to checked_value's alphabet, so a name git refuses as a branch (~, a..b, x.lock, a trailing /) passes --dry-run and, against GitHub, dies at the first git init -b after gh repo create has made all three repositories #283
Description
(lane openRepoShape-2, 2026-10-11T23:46:37Z)
Found while this lane probed --tracking-branch ~ for the YAML misread #258 reports (the ~ is the one value that misreads and is also not a branch), reproduced on main c3f4987 (git 2.43.0, Python 3.12.3). Pre-existing. Not claimed.
What is wrong. scaffold-project.py takes --tracking-branch through checked_value (scaffold-project.py:1009), which holds it to SAFE_ARG_RE (^[A-Za-z0-9._/@+~-]{1,255}$) and refuses a leading -. That alphabet is wider than the names git accepts as a branch. Nothing asks git (git check-ref-format --branch), so these pass --dry-run (exit 0) and the plan:
--tracking-branch |
git check-ref-format --branch |
--dry-run |
git init -b |
|---|---|---|---|
~, a~1 |
128 | exit 0 | 128 invalid initial branch name |
a..b |
128 | exit 0 | 128 |
x.lock |
128 | exit 0 | 128 |
a/, /a, a//b |
128 | exit 0 | 128 |
.a, a. |
128 | exit 0 | 128 |
A real run then fails at the first git init -b <name>. With --local-remote-dir that is the first bare repository, so nothing is left. Against GitHub the order is different: _create_remotes runs gh repo create for all three repositories, and only _seed_legs runs git init -b <name> on the first working tree. The run exits 2 and the three repositories exist with no commit. The re-run is then refused scaffold-remote-exists for the two legs, which the operator must delete on GitHub first (the assembly root takes --reuse-empty-repo). The GitHub route is read from the stubbed run below and from the code; a real scaffold must not reach GitHub from a review, so none was made.
SHAPE=<openRepoShape checkout>
export GIT_AUTHOR_NAME=T GIT_AUTHOR_EMAIL=t@example.invalid GIT_COMMITTER_NAME=T GIT_COMMITTER_EMAIL=t@example.invalid
# A stand-in for gh that logs its calls and succeeds, so nothing here reaches GitHub.
STUB=$(mktemp -d); export STUB_LOG="$STUB/calls.log"
cat > "$STUB/gh" <<'EOF2'
#!/bin/sh
echo "gh $*" >> "$STUB_LOG"
[ "$1 $2" = "repo view" ] && exit 1 # no such repository yet
exit 0
EOF2
chmod +x "$STUB/gh"
for tb in '~' 'a..b' 'x.lock' 'a/'; do
: > "$STUB_LOG"; TMP=$(mktemp -d)
PATH="$STUB:$PATH" python3 "$SHAPE/scaffold-project.py" --org testorg --project Atlas --elected-by 'T H' \
--elected-on 2026-09-02 --tracking-branch "$tb" --dry-run > /dev/null; dry=$?
PATH="$STUB:$PATH" python3 "$SHAPE/scaffold-project.py" --org testorg --project Atlas --elected-by 'T H' \
--elected-on 2026-09-02 --tracking-branch "$tb" --work-dir "$TMP/work" > "$TMP/out" 2> "$TMP/err"
rc=$?
echo "== --tracking-branch '$tb': --dry-run exit $dry, real run exit $rc, $(grep -c '^gh repo create' "$STUB_LOG") x gh repo create, then: $(grep -a -m1 '^ git init' "$TMP/err" | sed "s#$TMP#<TMP>#" | cut -c1-70)"
done
echo "-- the calls the last run made:"; cut -c1-70 "$STUB_LOG"Observed on main c3f4987:
== --tracking-branch '~': --dry-run exit 0, real run exit 2, 3 x gh repo create, then: git init -q -b ~ <TMP>/work/Atlas-spec
== --tracking-branch 'a..b': --dry-run exit 0, real run exit 2, 3 x gh repo create, then: git init -q -b a..b <TMP>/work/Atlas-spec
== --tracking-branch 'x.lock': --dry-run exit 0, real run exit 2, 3 x gh repo create, then: git init -q -b x.lock <TMP>/work/Atlas-spec
== --tracking-branch 'a/': --dry-run exit 0, real run exit 2, 3 x gh repo create, then: git init -q -b a/ <TMP>/work/Atlas-spec
-- the calls the last run made:
gh repo view testorg/Atlas --json name
gh repo view testorg/Atlas-spec --json name
gh repo view testorg/Atlas-code --json name
gh repo create testorg/Atlas-spec --private --description Atlas — sp
gh repo create testorg/Atlas-code --private --description Atlas — co
gh repo create testorg/Atlas --private --description Atlas — assembl
gh api orgs/testorg --jq .plan.name
With --local-remote-dir and no stub, --tracking-branch '~' exits 2 at once (fatal: invalid initial branch name: '~', from git init -q --bare -b ~ on the first remote) and creates nothing.
What should happen. The scaffold asks git whether the name is a branch (git check-ref-format --branch <name>) next to checked_value, before any work directory or remote exists and under --dry-run too, and refuses it by name with the rule git gave. That is the same placement refuse_bad_leg_paths has for the leg paths (#179). A test per row above runs through --dry-run and asserts exit 2 with no gh repo create recorded and no directory made.
Where (main c3f4987):
scaffold-project.py:1009-1010:--tracking-branchthroughchecked_valueand nothing else.scripts/repo_shape.py:94(SAFE_ARG_RE) and:1761(checked_value).scaffold-project.py:798: the local route'sgit init -q --bare -b;:780-815_create_remotes, which isgh repo createonly on the GitHub route;:833_seed_legsintoscripts/shape_materialize.py:737-748git_init_commit, wheregit init -q -b <branch>first runs on that route;:903: the assembly root's owngit init.adopt-project.py:1381-1382andscripts/family.py:454take--tracking-branchthrough the samechecked_value; neither was run here.
Related.
- scaffold-project.py writes
--idand--tracking-branchinto project.yaml unquoted, so--id nullor--id truescaffolds a project whose first bootstrap failsmanifest-id, and--tracking-branch true,007ornullputs the clone's legs onTrue,7ormainwith no upstream: #181's defect on the scaffold side, beside #225 #258: the YAML half of the same flag (--tracking-branch trueand007misread). A fix that refuses at the flag, as scaffold-project.py writes--idand--tracking-branchinto project.yaml unquoted, so--id nullor--id truescaffolds a project whose first bootstrap failsmanifest-id, and--tracking-branch true,007ornullputs the clone's legs onTrue,7ormainwith no upstream: #181's defect on the scaffold side, beside #225 #258 proposes, is the natural place for this check too. - templates/assembly-root/project.yaml and the two leg pins write eight text placeholders bare (
id,elected_on,visibility,tracking_branch, the two legpaths andsubmodule_paths), so a checked value YAML reads as another type (007,2026,true,null) is misread by the project or refused by the tool; quoting them reads every one back exactly and lets numeric branch names adopt #259: quoting the template placeholders. - scaffold-project.py
--pin spec@<sha>(orcode@,shape@, or two--pins whose products differ only by case) passes--dry-run, then creates all three repositories, pushes both legs and dies withREFUSED materialize-collision, because the pin'scontracts/<product>-pin.yamlis a file the shape already writes #241: the same shape of defect for--pin spec@<sha>(passes--dry-run, creates all three repositories, then dies).
Not claimed.
🤖 Generated with Claude Code
Activity
brettheap commentedon Oct 12, 2026
CLAIMED — lane openRepoShape-2, session 5b7b5f3d-dce7-452d-a33d-ab8537d91db3@Eagle, 2026-10-12T10:59:17Z, for #283
Logged in
lanes/log/openRepoShape-2.mdat46554f34afdb41770257539ab53eb4cf1ff40e3d.The three reads (lane-collision-protocol Rule 1):
scaffold-project.py: `--elected-by`, `--reference`, `--name` and the …
scaffold-project.py and family.py: `--tracking-branch` is asked of gi…
test_tracking_branch_ref.py: the leading-dash test passes `-x`, so it…
tests/test_scaffold_manifest_values.py, tests/test_adopt_manifest_val…
family.py: `init` asks the holder's reader and validate-family.py abo…
Merge origin/main 437a360 into fix/adopt-root-kept-symlinks: #329's `…
Merge main 437a360 into fix/adopt-plan-quote-yaml-keywords (#181)
adopt-project.py: a tracking branch the loader misreads that git will…
templates: `project.yaml`, the leg pins and `family.yaml` quote their…
adopt-project.py: #181's misread gate names a tracking branch only wh…
bootstrap.py and validate-manifest.py: a branch holding a NUL or a lo…
shape_materialize.py: `readable_branch` and `UNSEEN` move out of the …