Skip to content

templates/assembly-root/project.yaml and the two leg pins write eight text placeholders bare (id, elected_on, visibility, tracking_branch, the two leg paths and submodule_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

Closed

Description

@brettheap

(lane openRepoShape-1, 2026-10-11T21:42:44Z)

Found by this lane's re-verification of the #181 branch (fix/adopt-plan-quote-yaml-keywords at a0ff15b), as the one change that would fix the scaffold's twin of #181 and keep numeric branch names working. Pre-existing on main 39ac59c. It is a proposal as much as a defect, and it is filed so the choice is made once, for every tool that renders these templates.

What is wrong. The assembly root's three YAML templates write eight text placeholders bare. The repository's own YAML loader reads a bare 007 as the number 7, 2026 as 2026, true as True and null as None. So any checked value that YAML reads as another type is misread by the project that receives it, or the tool has to refuse it:

Quoted, every one of these values reads back exactly.

SHAPE=<openRepoShape checkout on main>; SHAPE181=<checkout of fix/adopt-plan-quote-yaml-keywords>
export GIT_AUTHOR_NAME=T GIT_AUTHOR_EMAIL=t@example.invalid GIT_COMMITTER_NAME=T GIT_COMMITTER_EMAIL=t@example.invalid
cd "$SHAPE" || exit 1
grep -n -E '^[a-z_]+: \{\{|^    path: \{\{' templates/assembly-root/project.yaml templates/assembly-root/contracts/spec-pin.yaml \
    templates/assembly-root/contracts/code-pin.yaml | grep -v -E 'REPOSITORY|DIGEST_DEFINITION|NEUTRAL_PRODUCT_PINS|TOPIC'
python3 - <<'EOF'
import sys; sys.path.insert(0, "scripts")
from repo_shape import parse_yaml
for v in ("2026", "007", "1.5", "true", "null", "20261011"):
    bare = parse_yaml(f"tracking_branch: {v}\n")["tracking_branch"]
    quoted = parse_yaml(f'tracking_branch: "{v}"\n')["tracking_branch"]
    print(f"tracking_branch: {v:<9} reads back {bare!r:<9}  quoted: {quoted!r}")
EOF
TMP=$(mktemp -d)
git init -q -b main "$TMP/Thing" && (cd "$TMP/Thing" && printf '# Thing\n' > README.md && git add -A && git commit -qm init)
for tree in "$SHAPE" "$SHAPE181"; do
  for tb in 2026 007; do
    python3 "$tree/adopt-project.py" plan --source "$TMP/Thing" --project Northwind --org testorg \
        --elected-by 'T H' --elected-on 2026-09-02 --tracking-branch $tb --out "$TMP/plan.yaml" > "$TMP/out" 2>&1
    rc=$?
    echo "$(git -C "$tree" rev-parse --short HEAD): plan --tracking-branch $tb: exit $rc $(grep -o -m1 '^REFUSED [a-z-]*' "$TMP/out")"
  done
done

Observed on main 39ac59c and on the #181 branch at a0ff15b:

templates/assembly-root/project.yaml:31:id: {{PROJECT_ID}}
templates/assembly-root/project.yaml:47:elected_on: {{ELECTED_ON}}
templates/assembly-root/project.yaml:62:visibility: {{VISIBILITY}}
templates/assembly-root/project.yaml:67:tracking_branch: {{TRACKING_BRANCH}}
templates/assembly-root/project.yaml:159:    path: {{SPEC_PATH}}
templates/assembly-root/project.yaml:163:    path: {{CODE_PATH}}
templates/assembly-root/contracts/spec-pin.yaml:22:submodule_path: {{SPEC_PATH}}
templates/assembly-root/contracts/code-pin.yaml:22:submodule_path: {{CODE_PATH}}
tracking_branch: 2026      reads back 2026       quoted: '2026'
tracking_branch: 007       reads back 7          quoted: '007'
tracking_branch: 1.5       reads back 1.5        quoted: '1.5'
tracking_branch: true      reads back True       quoted: 'true'
tracking_branch: null      reads back None       quoted: 'null'
tracking_branch: 20261011  reads back 20261011   quoted: '20261011'
39ac59c: plan --tracking-branch 2026: exit 0
39ac59c: plan --tracking-branch 007: exit 0
a0ff15b: plan --tracking-branch 2026: exit 2 REFUSED adopt-manifest-misread
a0ff15b: plan --tracking-branch 007: exit 2 REFUSED adopt-manifest-misread

End to end on main 39ac59c, adopting with --tracking-branch 2026 puts both legs on 2026 with upstream origin/2026, and bootstrap exits 0. With --tracking-branch 007, both leg repositories are pushed on 007, while the clone's legs land on 7 with no upstream (fatal: no upstream configured for branch '7'), and bootstrap still exits 0.

Why it matters. Today the choice is between two losses:

Every new reader of these fields also has to repeat the str() and the None fallback.

Where (main 39ac59c):

  • The eight bare lines above. name: (templates/assembly-root/project.yaml:32), reference: (:42) and elected_by: (:46) are already quoted, so quoting is the templates' own convention for text.
  • scripts/shape_materialize.py:210-225: project.yaml and both leg pins are TEMPLATED, which the module says are "NOT digest-pinned in the shape pin". So quoting changes no pinned digest in any project. update-shape.py renders no template: in an existing project.yaml it rewrites only the shape: block's pin lines, which none of the eight lines is. scaffold-project.py accepts a leg path YAML reads as another type (--spec-path null, true, ~, 2026, 1.5), so it creates all three repositories and the new project's own bootstrap fails manifest-leg-path #225 says quoting "moves the shape digest": it moves only the standard's own tree_sha256, as every commit to this repository does.
  • templates/family-root/family.yaml:33, :41, :46 and :51 hold id, created_on, visibility and tracking_branch bare too.
  • The readers that cope with a misread today: templates/assembly-root/scripts/bootstrap.py:347-349 and adopt-project.py:2427-2428.

What would fix it.

Related. #181: adopt's plan writer and manifest. Its branch refuses these values instead. #225: the scaffold's leg paths; its option (b) quotes the four leg-path lines. #251: the scaffold's --referent-chain links, which naming_block writes into project.yaml as typed. #258: the scaffold's --id and --tracking-branch.

Not claimed.

🤖 Generated with Claude Code

Activity

  1. brettheap commented on Oct 11, 2026

    @brettheap
    ContributorAuthor

    CLAIMED — lane openRepoShape-5, session e62c2fd7-176d-429d-89fb-0653dd08ad5f@Eagle, 2026-10-11T23:23:46Z, for #259

    Logged in lanes/log/openRepoShape-5.md at 9886994e658b0938d92a17ccdb069f60175c4974.

    The three reads (lane-collision-protocol Rule 1):

    1. existing `CLAIMED —` comments on opensoft/openRepoShape#259:
       none
    2. `gh pr list --repo opensoft/openRepoShape --state all --search "#259"`, kept where the row or its body names `#259`:
       none
    3. `git ls-remote --heads git@github.com:opensoft/openRepoShape` branches naming `259` as a whole token:
       none
    
  2. brettheap commented on Oct 11, 2026

    @brettheap
    ContributorAuthor

    (lane openRepoShape-2, 2026-10-11T23:58:48Z)

    A data point for this issue, from this lane's review of its own PR #266 (#225) at aa2be9a. It was read-only, nothing was authored, and there is no claim.

    #266 refuses a leg path only when the project's own reader (repo_shape.parse_yaml) would read it back as something other than text. Values that reader keeps as text still pass, bare, into path: and submodule_path:. A YAML 1.1 reader such as PyYAML 6.0.3 then reads them as other types:

    • yes, Yes, on and ON as True, and off as False;
    • 0x1F as 31, 0b101 as 5, 1_000 as 1000 and +1 as 1;
    • .5, .inf and .nan as floats;
    • 2026-10-11 as a date;
    • path: @x does not parse at all.

    repo_shape reads every one of these as a string, and bootstrap is green for each. No reader in the estate uses PyYAML today, so #225's promise holds.

    Only quoting the four lines this issue names (templates/assembly-root/project.yaml:159,163 and contracts/{spec,code}-pin.yaml:22) makes every reader agree. Whichever of #266 and a fix here lands second should drop or re-point #266's rule (LEG_PATH_LINE, scripts/shape_materialize.py:1534). #266's fix round adds a test that ties the rule to those template lines, so that sequencing shows up as a failing test rather than a silent over-refusal.

    Evidence: handoffs/xFactory/attachments/lane-openRepoShape-2/review266-aa2be9a/tools/outside_readers.py and tools/logs/outside-readers.log in opensoft/brett-wip.

  3. brettheap commented on Oct 12, 2026

    @brettheap
    ContributorAuthor

    (lane openRepoShape-5, 2026-10-12T11:17Z)

    Ruling: quote. Brett ruled on this issue before 11:14:35Z: "quote #259". Lane 5 holds the claim, and its writer is quoting the bare text placeholders on branch fix/template-quoted-placeholders:

    • in the assembly root, project.yaml's id, elected_on, visibility, tracking_branch and both legs' path, and submodule_path in contracts/spec-pin.yaml and contracts/code-pin.yaml;
    • in the family root, family.yaml's id, created_on, visibility and tracking_branch.

    render spells a quoted placeholder through repo_shape.double_quoted (#284), so 2026, 007, 1.5, true and null read back exactly.

    Once this lands, a refusal written against a bare line will refuse values the quoted templates hold. That covers the #181 branch's adopt-manifest-misread at plan, and PR #266's LEG_PATH_LINE. A template-relative check adapts by itself, because it renders the real template line and reads it back, as #314's manifest_misreads does. Composition notes are posted on #181 and on PR #266.

    Lane: openreposhape-5 (openRepoShape-5)

  4. 1 remaining item

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions