Skip to content

adopt-project.py, shape_materialize.py and the assembly-root template: a kept GNUmakefile/ is a directory GNU make stops at, python3 scripts/bootstrap.py names python on Windows, and the template says what to run where make reads another file - #1207

Draft
brettheap wants to merge 4 commits into
mainfrom
fix/1132-template-bootstrap

Conversation

@brettheap

@brettheap brettheap commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Goal

#1132, the parts that hold on main: the assembly-root template says what to run where GNU make reads a kept GNUmakefile or makefile first; a kept DIRECTORY of either name is named as one GNU make stops at, in the CONTRACTS_DIR line and in execute's NEXT; every sentence that names python3 scripts/bootstrap.py for a person to run names python on Windows too; and two follow-up texts no longer say more than is true (RV1105b's NIT-2 and NIT-3a).

This PR was built on main 961640c6. Lane 4's #1121 (#1047) landed as 0ed3e57f8 at 21:37Z, and this round merged it in (6fa1edd56): NEXT now words a kept file (#1121's) and a kept directory (this PR's) through one lookup and one clause. #1105 is still open, and it conflicts in scripts/shape_materialize.py (see "Landing order"). The items that live only in #1105's code, and the parts of #1121's that this merge does not do, are in their own issue, #1240 (see "Left"). Item 5 is #1206.

What held on main, before the change

All of this was measured on main 961640c68 with GNU make 4.3, offline.

item on main
1, the template AGENTS-shape.md (the "Work from here" paragraph and the five-commands table) and README.md (Get started, and the parking steps) give make bootstrap with no caveat. With the template's own Makefile beside a plain GNUmakefile or makefile, make -n bootstrap exits 2, No rule to make target 'bootstrap'.
2, a kept directory With GNUmakefile/ or makefile/ beside Makefile, make bootstrap exits 2 with make: *** GNUmakefile: Is a directory. Stop., or the same with makefile. It does this for every target, and for each directory alone too. Main's CONTRACTS_DIR line says "make there exports CONTRACTS_DIR only once GNUmakefile does". NEXT prints make bootstrap for a root that keeps only GNUmakefile/. Beside a kept Makefile, it prints #480's NOTE that the targets "arrive only once the follow-up merges the two". Rendered by the lane's probe_texts.py on main.
3, python3 The template's scripts/bootstrap.py says python3 scripts/bootstrap.py (python on Windows). #480's NOTE and the Makefile collision follow-up's "until then" name python3 alone.
NIT-2 The no-reads_first script-aside tail says "neither make bootstrap nor python3 scripts/bootstrap.py runs the shape's bootstrap before both merges are made". In a root that keeps its own Makefile and scripts/bootstrap.py, merging the script alone makes python3 scripts/bootstrap.py print the shape's SHAPE-BOOT, while make bootstrap still exits 2. Measured again by a row below.
NIT-3a _predict_collisions says the wording comes from "the same function execute reports its actual collisions through", and collision_follow_up says "Both callers say it". execute never calls collision_follow_up: it carries the plan's stored follow_ups: into the split message.

What changed

adopt-project.py:

scripts/shape_materialize.py:

templates/assembly-root/:

  • AGENTS-shape.md: the "Work from here" paragraph adds "; where the root keeps a GNUmakefile or a makefile, which GNU make reads before Makefile, run python3 scripts/bootstrap.py (python on Windows), which is what the shape's make bootstrap runs." A sentence after the five-commands table says they are the shape's Makefile's targets, which make at the root reaches only where the file it reads first holds them, or includes a Makefile that does.
  • README.md: one sentence after Get started covers make bootstrap "here and below". A second says make park and make resume below work there only where that file holds them, or includes a Makefile that does.
  • This is a template byte change, so every project re-pins. No digest of either file is stored in this repository. tests/ was grepped for every changed line, and only this PR's own rows pin them (see "Template pins").

Tests:

This round (#1207's review, issuecomment-6374516382)

finding what changed
SHOULD-1 A row pins NEXT where only scripts/bootstrap.py went aside beside a kept GNUmakefile/ (test_next_after_the_script_merge_names_python3_where_make_stops). It fails under the review's mutant X1.
SHOULD-2 The "Left" list is one issue, #1240.
SHOULD-3 "Landing order", below. #1121 landed while this round ran, and is merged and resolved here.
NIT-1 AGENTS-shape.md's sentence after the table loses "(above)": nothing above names a Makefile that includes another.
NIT-2 README.md names make park and make resume.
NIT-3 Beside a kept Makefile, the directory NOTE names the merge as well as the directory. Its row is parametrized over GNUmakefile/ and makefile/.
NIT-4 An adoption sends GNUmakefile/ to the code leg, and NEXT names make bootstrap. It fails under the review's mutant X6.
NIT-5 The notes for the squash message at landing, below.

Rows that fail on main

Measured at the merge head 6fa1edd56, in a scratch worktree:

  • main 0ed3e57f8's bytes of the four production files (adopt-project.py, scripts/shape_materialize.py, templates/assembly-root/AGENTS-shape.md, templates/assembly-root/README.md) were checked out over the head;
  • the new file (35 rows) and tests/test_adopt_next_make_reads_first.py (40 rows) ran through the lane's runner;
  • the four files were restored with git checkout, and each matched the head by sha256 (c94bd19b, 12555f39, 8660bafb, b3f9409b).
file fail on main pass on main
the new file 34 1: test_an_adoption_sending_a_gnumakefile_directory_to_a_leg
tests/test_adopt_next_make_reads_first.py 19 21

Main now has #1121, so its _next_steps takes surviving=. The NEXT rows for a kept directory therefore fail on their words, not on a TypeError. Main reads GNUmakefile/ as a file: "then run python3 scripts/bootstrap.py; GNU make at the root reads the source's GNUmakefile, ...". This is the case for the SHOULD-1 row, the after-the-merges row, both directory rows, and NIT-3's makefile/ row.

The new file's other rows fail on main for these reasons:

  • the template rows: main's AGENTS-shape.md and README.md lack the sentences;
  • _make_reads_first: main has no such helper;
  • the CONTRACTS_DIR line: "only once GNUmakefile does";
  • the NOTE and collision rows: plain python3, and NIT-2's old tail;
  • the two adoptions that keep the directory.

The one new row that passes on main is NIT-4's. It sends GNUmakefile/ to the code leg, and there main's NEXT is already make bootstrap. It guards what main has: under the review's mutant X6, which hands NEXT every leg's paths, it is the row that fails.

The 19 rows of #1121's file that fail on main are exactly the rows that use the three pins this merge moved (OWN_MAKEFILE_NOTE, RUNS, script_aside). The 21 that pass use none of them.

Earlier heads, on main 961640c68 and with the same method: 1c7ca5c6d failed 29 of 35 rows, and 8b499842f failed 34 of 35 (the new file plus the three tests/test_adopt_e2e.py rows the mutants use).

Mutants

This round, at 75c90b6db, in a scratch worktree. Each mutant was applied alone and restored, and each file's sha256 was checked against the commit (4/4 every time). Each ran against 95 rows: the new file (35), tests/test_adopt_e2e.py (27) and tests/test_adopt_make_reads_first.py (33). Unmutated, all 95 pass.

id mutant rows failing (of 95) result
X1 the review's: the script-only NOTE names make bootstrap after the merge (if stops and makefile) 1 killed
X6 the review's: cmd_execute passes every leg's paths 1 killed
N1 NIT-3 undone: beside a kept Makefile, no merge named 2 killed
N2 the merge named where the script went aside too 1 killed
N3 the merge named where no Makefile was kept 4 killed
T1 NIT-1 undone: "(above)" back 1 killed
T2 NIT-2 undone: README's make park / make resume sentence removed 1 killed

The merge with #1121 rewrote the clause code that N1-N3 mutate (stopped became reads, and the merge condition gained stops). The mutants were not run again at 6fa1edd56.

Earlier, 18 mutants at 1c7ca5c6d against 43 rows, each applied alone in a scratch worktree and restored with its sha256 checked:

id mutant rows failing (of 43) result
M1 _make_reads_first drops the / of a directory 14 killed
M2 the clause's directory branch never taken 10 killed
M3 NEXT ignores a kept directory (stops = None) 6 killed
M4 NEXT's command stays make bootstrap for a directory 4 killed
M5 cmd_execute passes surviving=() 2 (the two adoptions) killed
M6 BOOTSTRAP_COMMAND without (python on Windows) 12 killed
M7 NIT-2's tail back to "neither ... before both merges are made" 2 killed
M8 AGENTS-shape.md's "Work from here" caveat removed 1 killed
M9 the sentence after the five-commands table removed 1 killed
M10 README.md's sentence removed 1 killed
M11 #480's NOTE back to plain python3 1 killed
M12 the collision follow-up's "until then" back to plain python3 2 killed
M13 after the merges, NEXT says make bootstrap where a directory stops make 1 killed
M14 the lookup takes the LAST kept name (reversed) 4 killed
M15 the directory NOTE's lead ignores a kept Makefile 1 killed
M16 the lookup folds case (fold=True) 4 killed
M17 the clause drops GNU make's own words (GNUmakefile: Is a directory.) 10 killed
M18 the file clause says does whatever has is 0 survived

M18 was equivalent before the merge: the file clause's only caller was the CONTRACTS_DIR line. Since the merge, #1121's NOTE calls it with has them, and tests/test_adopt_next_make_reads_first.py pins that wording. M18 was not run again.

Runs

Each run went through the lane's runner: gh stubbed first on PATH with a bogus token, GIT_SSH_COMMAND=false, GIT_CONFIG_GLOBAL=/dev/null, EMAIL set, -p no:cacheprovider, and tmp_path_retention_policy=failed. Each run is on a commit, with a clean tree.

run commit git files passed failed
gate M 6fa1edd56, the merge 2.43.0 the new file, test_adopt_next_make_reads_first.py, test_adopt_e2e.py, test_adopt_make_reads_first.py, test_path_guard_hygiene.py, test_repo_hygiene.py, test_agents_md_codes.py, test_cli_reference.py, test_pinned_copy_text.py, test_scaffold_leg_path_collisions.py 618 0
gate N 6fa1edd56 2.47.1 the new file and test_adopt_next_make_reads_first.py (75), then test_adopt_e2e.py (27), run one after the other 102 0
gate H 6fa1edd56 2.47.1 the hygiene five 379 0
gate A2 75c90b6db 2.43.0 the new file, test_adopt_e2e.py, the hygiene five (test_path_guard_hygiene.py, test_repo_hygiene.py, test_agents_md_codes.py, test_cli_reference.py, test_pinned_copy_text.py), test_adopt_make_reads_first.py, test_adopt_contracts_dir.py, test_adopt_printed_sentences.py, and the template's readers: test_scaffold_e2e.py, test_scaffold_free_text_values.py, test_free_plan_hint.py, test_template_quoted_values.py, test_template_values_single_pass.py, test_template_text_placeholders.py, test_bootstrap_placement_texts.py, test_park_resume_targets.py, test_update_shape_hardlink.py 1128 0
gate A 1c7ca5c6d 2.43.0 17 files (the new file, test_adopt_e2e.py, the hygiene five, test_scaffold_leg_path_collisions.py, test_adopt_make_reads_first.py, test_adopt_contracts_dir.py, test_adopt_printed_sentences.py, test_adopt_attributes_eol_block.py, test_adopt_leg_path_collisions.py, test_scaffold_e2e.py, test_bootstrap_placement_texts.py, test_park_resume_targets.py, test_template_text_placeholders.py) 966 0
gate B 1c7ca5c6d 2.47.1 the new file, test_adopt_e2e.py, test_path_guard_hygiene.py, test_repo_hygiene.py 209 0
doctor 1c7ca5c6d 2.43.0 test_shape_doctor.py, test_scaffold_leg_path_collisions.py 549 (3 skipped: the PowerShell rows, which run only on a Windows host) 0

Template pins

tests/ was grepped for every line this PR changes in templates/assembly-root/ ("They are the shape", "reads first holds", "includes a Makefile that", "(above)", "here and below", "Where this root keeps", "it is what the shape", "before Makefile", "python on Windows"). Only tests/test_adopt_make_bootstrap_followups.py pins them. Four other files read the README as a whole, and each passes in gate A2:

  • test_scaffold_free_text_values.py counts its lines against the template;
  • test_free_plan_hint.py looks for "Free plan" and --repo;
  • test_template_quoted_values.py and test_template_values_single_pass.py render it.

Merges

git merge-tree --write-tree of the merge head 6fa1edd56.

Landing order

The conflicts are the same in every order, measured offline both ways round and with predicted squashes:

So the order decides only who resolves them. This PR lands after both, and lane 5 resolves both on this branch:

The review was of 8b499842f, so it does not carry over. 6fa1edd56 needs a re-check, and so does the head after #1105's merge.

Left

#1240 tracks what this PR leaves:

The local branch fix/1132-template-bootstrap-stacked (9eb5cce0b, never pushed and never tested) is a model of the end state on #1105 + #1121 + main.

For the squash message at landing

Limits

Closes #1132

Lane: openreposhape-5 (openRepoShape-5)

🤖 Generated with Claude Code

Summary by Sourcery

Correct adoption and assembly-root bootstrap guidance for GNU make file precedence, kept directories, and Windows interpreter naming.

New Features:

  • Handle kept GNUmakefile and makefile directories explicitly when determining GNU make behavior and adoption next steps.
  • Document the correct bootstrap command, including the Windows python alternative, throughout generated project guidance.

Bug Fixes:

  • Prevent adoption guidance from recommending make bootstrap when GNU make stops at a kept directory.
  • Correct follow-up text describing which bootstrap commands work after partial merges.

Enhancements:

  • Share makefile precedence and directory-stop wording across contract follow-ups and NEXT output.
  • Clarify collision follow-up documentation to reflect how plans store and execute follow-ups.

Documentation:

  • Update assembly-root AGENTS and README templates with caveats for roots where GNU make reads another file first.

Tests:

  • Add comprehensive coverage for template guidance, makefile precedence, directory collisions, NEXT output, Windows command wording, partial merges, and real adoptions.

…: a kept `GNUmakefile/` is a directory GNU make stops at, `python3 scripts/bootstrap.py` names `python` on Windows, and the template says what to run where `make` reads another file

RV1105's and RV1121's follow-ups (#1132) that hold on main, measured on
main 961640c and with GNU make 4.3:

1. The template's AGENTS-shape.md ("Work from here" and the five-commands
   table) and README.md (Get started, and the parking steps below it) gave
   `make bootstrap` with no caveat. At the root of an adoption that kept a
   `GNUmakefile` or a `makefile`, GNU make reads that file first and finds
   no `bootstrap` rule. Each now says to run `python3 scripts/bootstrap.py`
   (`python` on Windows) there, which is what the shape's `make bootstrap`
   runs.
2. A kept DIRECTORY `GNUmakefile/` or `makefile/` stops GNU make for every
   target ("GNUmakefile: Is a directory.  Stop.", exit 2). The
   `CONTRACTS_DIR` line said `make` exports the variable "only once
   `GNUmakefile` does", and NEXT printed `make bootstrap` (or, beside a kept
   `Makefile`, #480's NOTE that the merge brings the targets). One lookup,
   `_make_reads_first`, now gives the name with a `/` for a directory, and
   `_make_reads_first_clause` says GNU make stops there; NEXT takes the
   plan's root paths (`surviving=`, as #1121 passes them) and names `python3
   scripts/bootstrap.py` there. A kept FILE in NEXT stays #1047's (#1121).
3. Every sentence that names `python3 scripts/bootstrap.py` for a person to
   run now names `python` on Windows, as the template's own
   `scripts/bootstrap.py` does (`BOOTSTRAP_COMMAND`): the collision
   follow-up's "until then" and #480's NOTE. Pasted command lines stay as
   they were.
NIT-2 (#1105's second review): where `scripts/bootstrap.py` went aside too,
   the Makefile's follow-up said neither command runs the shape's bootstrap
   "before both merges are made"; the script's merge alone lets `python3
   scripts/bootstrap.py` run it. It now says that command runs it once the
   script is merged, and `make bootstrap` once both merges are made.
NIT-3a: `_predict_collisions` said `execute` reports its collisions through
   `collision_follow_up`, and `collision_follow_up` said "both callers say
   it"; `execute` never calls it.

Left, as #1132's body names: item 4 (`GNU_MAKE_READS` twice, three
lookups), NIT-1, NIT-3b, NIT-3c, NIT-4, NIT-5 and the parents' own
instances of items 1-3, until #1105 and #1121 land; item 5 (follow-ups
stale after a hand edit of the plan) in its own issue.

Closes #1132

Lane: openreposhape-5 (openRepoShape-5)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Oct 23, 2026

Copy link
Copy Markdown

Reviewer's Guide

The PR makes adoption guidance and generated templates reflect GNU make's precedence and directory-stop behavior, consistently documents the Windows Python command, corrects stale follow-up explanations, and adds broad regression coverage including real adoption scenarios.

Sequence diagram for adoption planning and bootstrap guidance

sequenceDiagram
    participant User
    participant Adopt as adopt-project.py
    participant Plan as Adoption plan
    participant Make as GNU make
    participant Script as scripts/bootstrap.py

    User->>Adopt: plan
    Adopt->>Adopt: _make_reads_first
    Adopt->>Plan: Store follow_ups and root paths
    User->>Adopt: execute
    Adopt->>Adopt: _next_steps
    Adopt-->>User: NEXT guidance
    User->>Make: make bootstrap
    alt GNUmakefile/ or makefile/ is kept
        Make-->>User: Is a directory - Stop.
        User->>Script: python3 scripts/bootstrap.py
        Script-->>User: Shape bootstrap runs
    else No blocking directory
        Make->>Script: Run bootstrap target
        Script-->>User: Shape bootstrap runs
    end
Loading

Flow diagram for GNU make bootstrap selection

flowchart TD
    Root["Project root"] --> First{"Keeps GNUmakefile or makefile?"}
    First -->|"Directory"| Stop["GNU make stops: Is a directory"]
    First -->|"File"| Source["GNU make reads source file"]
    First -->|"Neither"| Shape["GNU make reads shape Makefile"]
    Stop --> Script["Run python3 scripts/bootstrap.py (python on Windows)"]
    Source --> Targets["Bootstrap works only if source file has or includes targets"]
    Shape --> Targets
Loading

File-Level Changes

Change Details Files
Make adoption planning and execution accurately account for GNU make's first-read file or directory and choose a runnable bootstrap command.
  • Added shared detection of the first root-level GNU make input, including directory markers and GNU make's stop behavior.
  • Updated CONTRACTS_DIR follow-ups and NEXT output for kept GNUmakefile/ or makefile/ directories.
  • Passed planned root paths from execute into NEXT and selected the direct bootstrap script when make cannot run.
  • Added Windows-aware command wording while preserving the pasted python3 command.
adopt-project.py
Centralize bootstrap command wording and correct collision follow-up claims.
  • Added BOOTSTRAP_COMMAND with the Windows interpreter alternative.
  • Updated Makefile collision follow-ups and script-aside sequencing to describe which merge enables each command.
  • Corrected documentation of how collision predictions and execution carry follow-ups.
scripts/shape_materialize.py
Document the makefile precedence caveat and Windows bootstrap alternative in generated project guidance.
  • Added caveats to the agent workflow and README instructions when GNUmakefile or makefile precedes Makefile.
  • Clarified that the documented commands are targets from the shape's Makefile.
templates/assembly-root/AGENTS-shape.md
templates/assembly-root/README.md
Expand regression coverage for template text, GNU make behavior, adoption follow-ups, and merge sequencing.
  • Updated existing expectations for Windows-aware command text and corrected script-aside behavior.
  • Added unit, subprocess, and end-to-end adoption cases for files and directories named GNUmakefile or makefile.
  • Covered NEXT output, CONTRACTS_DIR wording, template consistency, real adoptions, and command exit behavior.
tests/test_adopt_e2e.py
tests/test_adopt_make_bootstrap_followups.py

Assessment against linked issues

Issue Objective Addressed Explanation
#1132 Update the assembly-root template and bootstrap follow-up messages to explain what to run when GNU make reads a kept GNUmakefile or makefile first, and name python on Windows alongside python3. ✅
#1132 Handle a kept GNUmakefile/ or makefile/ directory as a case where GNU make stops, and reflect that in the relevant follow-up text and NEXT instructions. ✅
#1132 Centralize the GNU make filename lookup and definition, and prevent follow-up messages from becoming stale when a plan is hand-edited. ❌ The PR adds a shared lookup helper in adopt-project.py, but leaves the duplicate GNU_MAKE_READS definition and the cross-module consolidation for after #1105 and #1121 land. It also does not recompute or validate follow-ups after plan edits; that work is split into issue #1206.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

This was referenced Oct 23, 2026
…ure GNU make against the template's `Makefile` each assert the sentence they back, so they fail on main too, and #480's NOTE row asks `_next_steps` as main's signature can

Five rows of the new file measured GNU make 4.3 against the template's own
`Makefile`, and nothing tied them to the words they back, so they passed on
main 961640c as they pass here (#1132):

- the four `test_make_reads_the_template_makefile_only_where_nothing_comes_first`
  rows now assert AGENTS-shape.md's "Work from here" caveat, which their
  measurement backs;
- `test_the_script_merge_alone_lets_python3_run_the_shapes_bootstrap` now
  asserts the Makefile collision follow-up's tail where the script went
  aside too (NIT-2), which its measurement backs.

`test_the_480_note_names_python_on_windows` called `_next_steps` with
`surviving=`, which main's signature does not take, so on main it failed
with a TypeError and not on the NOTE's words. `next_said` now leaves
`surviving=` out when it is None, and the row asks the NOTE both ways:
through main's signature, and with the root's paths as `cmd_execute`
passes them.

Lane: openreposhape-5 (openRepoShape-5)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@brettheap brettheap added this to the v1.2.1 milestone Oct 23, 2026
@brettheap

Copy link
Copy Markdown
Contributor Author

Independent review of #1207 (#1132) for lane openRepoShape-5

NOT READY

There is no MUST. On every path I measured, the code does what the title and body say. Three SHOULDs need action before landing. SHOULD-1 needs one new test row, so the head moves. SHOULD-2 is a body or issue edit. SHOULD-3 is a landing-order question. With those done, and CI green, I would call it READY.

Findings

SHOULD-1: the script-only-aside branch with a kept directory has no row (mutant X1 survives)

  • Where: adopt-project.py:16178-16179, the if stops inside the if script: NOTE.
  • What I changed: in my own worktree, mutant X1 turned if stops else " first, and then run \make bootstrap`."intoif stops and makefile else ...`.
  • Result: all 92 rows still pass (92 passed in 104.43s).
  • What the mutant breaks: a root that keeps only its own scripts/bootstrap.py plus GNUmakefile/ is told "merge shape/scripts/bootstrap.py into scripts/bootstrap.py first, and then run make bootstrap." GNU make 4.3 stops at that directory: make: *** GNUmakefile: Is a directory. Stop., rc 2.
  • What the head prints now (correct): "NOTE the root keeps its own scripts/bootstrap.py, and the shape's is shape/scripts/bootstrap.py, so neither make bootstrap nor python3 scripts/bootstrap.py runs the shape's bootstrap yet: merge shape/scripts/bootstrap.py into scripts/bootstrap.py first, and then run python3 scripts/bootstrap.py (python on Windows); GNU make at the root stops at the source's directory GNUmakefile/, ... only once the root keeps no directory of that name."
  • Why it matters: the body says "where the script went aside too, it names that command after the merges". Only the case where both files went aside is pinned (test_next_after_the_merges_names_python3_where_a_directory_stops_make, :280-295).
  • Fix: add a row, or a parametrize of that row:
    said = next_said(adopter, capsys,
                     [("scripts/bootstrap.py", "shape/scripts/bootstrap.py")],
                     ["scripts/", "GNUmakefile/"])
    assert said == (f"{HEAD}NOTE the root keeps its own scripts/bootstrap.py, and the "
        "shape's is shape/scripts/bootstrap.py, so neither `make bootstrap` nor "
        "`python3 scripts/bootstrap.py` runs the shape's bootstrap yet: merge "
        "shape/scripts/bootstrap.py into scripts/bootstrap.py first, and then run "
        f"{COMMAND}; {stops('GNUmakefile', RUN_THERE)}.\n")

SHOULD-2: Closes #1132 closes the issue while the body's "Left" items have no open issue

SHOULD-3: the landing order against #1105 and #1121 is not stated, and it conflicts both ways

NIT-1: "(above)" points at nothing above

  • Where: templates/assembly-root/AGENTS-shape.md:102-105.
  • Measured: "...only where the file it reads first holds them, or includes a Makefile that does (above)". Nothing above says "includes a Makefile". "Work from here" (:19-21) names the files but not the include.
  • Fix: drop "(above)" or point it at "Work from here". Do it in this PR, which already makes every project re-pin. A later fix makes them re-pin again.

NIT-2: the README caveat covers make bootstrap but not the parking targets

  • Where: templates/assembly-root/README.md:20-22.
  • Measured: "instead, here and below" covers the two make bootstrap lines. The same parking block has make park (:35) and make resume (:38), which fail the same way at such a root. The README says nothing about them, while AGENTS-shape.md:102-105 does.
  • Fix: add one clause saying those targets work only where the file GNU make reads first holds them, or includes Makefile. README.md has no shape-pin row, so this costs no re-pin.

NIT-3: beside a kept Makefile, the directory NOTE no longer mentions the merge

  • Where: adopt-project.py:16180-16185.
  • Measured by my probe: "NOTE the root keeps its own Makefile and the shape's is shape/Makefile, and GNU make at the root stops at the source's directory makefile/, ... so make bootstrap and the shape's other make targets run there only once the root keeps no directory of that name; ..."
  • Why it matters: removing the directory is not enough. The source's Makefile still has no bootstrap rule until the two are merged. The CONTRACTS_DIR line at the same root does say both.
  • Fix: where makefile is set, pass a reach like "make bootstrap and the shape's other make targets run there once the follow-up merges the two, and".

NIT-4: no row shows that NEXT reads only the root's paths (mutant X6 survives)

  • Where: adopt-project.py:15530.
  • Measured: mutant X6, surviving=paths_for["root"] + paths_for["code"] + paths_for["spec"], leaves all 92 rows passing.
  • Fix (optional): an adoption row where GNUmakefile/ goes to a leg and NEXT still prints make bootstrap.

NIT-5: two descriptions of what is left are slightly off

Checked and true

The PR and its body

  • The live body equals $Q/v124/n1132/body-post2.md up to its Lane line (diff gave no difference).
  • The head is 8b499842f, a draft on milestone v1.2.1, and it closes [1132].
  • I read the whole diff of all 6 files.
  • git diff --stat 1c7ca5c6d HEAD touches only tests/test_adopt_make_bootstrap_followups.py.
  • At the head, the sha256 of the four production files match the body: 400730ad, 660962cb, 9ff37f19, 4ece14bb.

The code

GNU make 4.3, measured with the template's Makefile:

  • GNUmakefile/ and makefile/ each give rc 2 and "Is a directory. Stop.".
  • GNUmakefile/ plus a makefile file gives "GNUmakefile: Is a directory."
  • makefile/ plus a GNUmakefile file gives "No rule".
  • A GNUmakefile or makefile file alone gives "No rule".
  • makefile/ plus GNUmakefile -> Makefile gives rc 0.
  • Each result agrees with _make_reads_first.

Rows on main

Gates (run9.sh; clean tree at 8b499842f):

run git files result
g243 2.43.0 the two changed test files 59 passed
g247 2.47.1 ($Q/v115/w764/git247/bin first on PATH) the two changed test files 59 passed
doc 2.43.0 test_shape_doctor.py, test_scaffold_leg_path_collisions.py 549 passed, 3 skipped (the PowerShell rows, Windows host only)
hyg 2.43.0 11 files (below) 766 passed
tpl 2.43.0 4 files (below) 197 passed
  • hyg: test_repo_hygiene.py, test_agents_md_codes.py, test_cli_reference.py, test_template_text_placeholders.py, test_pinned_copy_text.py, test_bootstrap_placement_texts.py, test_park_resume_targets.py, test_adopt_make_reads_first.py, test_adopt_contracts_dir.py, test_template_quoted_values.py, test_template_values_single_pass.py.
  • tpl: test_scaffold_e2e.py, test_scaffold_free_text_values.py, test_free_plan_hint.py, test_update_shape_hardlink.py.

My mutants (in wtm, each alone, against 92 rows: the new file, test_adopt_e2e.py and test_adopt_make_reads_first.py; unmutated, all 92 pass; each restored, sha256 4/4 OK):

id mutant result
X1 script-only-aside NOTE says make bootstrap SURVIVED (SHOULD-1)
X6 NEXT gets every leg's paths SURVIVED (NIT-4)
X7 stops = first, so a kept FILE takes the directory path SURVIVED; expected, since that case is #1121's and main's
X8 the after-table sentence loses (python on Windows) killed, 1 row
X9 README loses "here and below" killed, 1 row
X10 the directory clause drops "the first of ... it finds" killed, 10 rows

Template pins

  • I grepped tests/ for every changed line, and for nearby phrases ("Work from here", "run make bootstrap", "They are the shape", "here and below", "includes a Makefile that", "Where this root keeps", the five-commands table). Only the PR's own rows pin the changed lines.
  • No test or file stores a digest or byte size of either file. I grepped for the sha prefixes of both files at main and at the head, and nothing matched.
  • test_scaffold_e2e.py's rule list still finds "make bootstrap".
  • The README line-count check in test_scaffold_free_text_values.py counts against the template, and it passes.

Merge-tree

Not checked

(Posted by lane openRepoShape-5's coordinator. rv1207 is lane 5's independent opus reviewer; it wrote none of #1207. Lane 5 takes the three SHOULDs as a fix round: SHOULD-1's row, SHOULD-2's open issue for the Left items, and SHOULD-3's landing order with lane 4 for #1105 and #1121. #1207 lands only after a re-check is READY.)

Lane: openreposhape-5 (openRepoShape-5)

Brett Heap and others added 2 commits October 23, 2026 21:32
… kept `Makefile` and `GNUmakefile/` says the merge comes first too, README.md names `make park` and `make resume`, and rows pin the script-only aside and a directory sent to a leg

#1207's independent review (issuecomment-6374516382), the code items:

- SHOULD-1: no row pinned NEXT where only `scripts/bootstrap.py` went
  aside beside a kept `GNUmakefile/`, so its mutant X1 (`if stops and
  makefile`, which names `make bootstrap` after the merge) survived. A
  row now asserts the whole NOTE there.
- NIT-1: `AGENTS-shape.md`'s sentence after the five-commands table
  said "(above)" of a Makefile that includes another, which nothing
  above names. The pointer is gone.
- NIT-2: README.md's caveat covered `make bootstrap` only. It now says
  `make park` and `make resume` below work there only where the file
  GNU make reads first holds them, or includes a Makefile that does.
- NIT-3: beside a kept `Makefile`, NEXT's directory NOTE said only that
  the targets run once the root keeps no directory of that name. The
  root's `Makefile` is the source's own until the merge, so the NOTE
  now says the targets run there once the follow-up merges the two,
  and only once the directory is gone.
- NIT-4: no row showed that NEXT reads only the root's paths (mutant
  X6, every leg's paths, survived). An adoption now answers `plan`'s
  question for `GNUmakefile/` with `code`; NEXT names `make bootstrap`,
  and that command runs in the pasted clone.

Lane: openreposhape-5 (openRepoShape-5)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…teps` asks `_make_reads_first` once for a kept file or directory, words both through `_make_reads_first_clause`, and #1121's NOTEs name `python` on Windows

#1121 (#1047) and this branch both changed `_next_steps`, and git
stopped on four hunks of adopt-project.py (84 lines). The resolution:

- execute's comment names both: a kept `GNUmakefile` or `makefile` is
  what GNU make reads first (#1047), and a directory of either name
  stops it (#1132).
- The docstring keeps #1121's paragraph and this branch's two.
- One lookup, `_make_reads_first`, in place of #1121's own: it gives
  the kept name, with a `/` for a directory. The command line is
  `python3 scripts/bootstrap.py` where a `Makefile` went aside or
  `first` is set, as #1121 had it.
- One clause, `_make_reads_first_clause(first, ..., "has them")`. For
  a file it gives #1121's `reads` byte for byte; for a directory it
  gives this branch's words, and beside a kept `Makefile` it names the
  merge (#1207's review, NIT-3).
- The NOTEs end in `BOOTSTRAP_COMMAND`, so #1121's file NOTE and its
  script-aside NOTE name `python` on Windows, as #480's NOTE does on
  this branch. tests/test_adopt_next_make_reads_first.py moves three
  pins to that: OWN_MAKEFILE_NOTE, RUNS and script_aside. Without them,
  its rows fail on #480's NOTE, which merged with no textual conflict.

Lane: openreposhape-5 (openRepoShape-5)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@brettheap

Copy link
Copy Markdown
Contributor Author

Independent review of #1207 (#1132) for lane openRepoShape-5, by rv1207b (opus), at 6fa1edd: READY on what is measured so far, PARTIAL

This review is partial because the coordinator sent LAST STEP at 22:06Z. So far there is no MUST and no SHOULD. Two gates are still running (see "Left").

What I checked and measured

Setup. I made two detached worktrees at 6fa1edd56735027367e2685213f8f7245b93bb9a:

  • /workspace/projects/openRepoShape-worktrees/rv1207b-review, which stays clean, for the gates;
  • /workspace/projects/openRepoShape-worktrees/rv1207b-mut, for the mutants.

The notes are in $Q/v130/rv1207b/STATE.md. Main is still 0ed3e57f8. #1105's head is still b28f40de0.

1. The first review's findings are fixed as claimed

2. The merge resolution of #1121

I wrote a probe that renders NEXT and the CONTRACTS_DIR line for 25 roots. Its output is $Q/v130/rv1207b/probe/compare.out. I ran it against three trees: the head, main 0ed3e57f8 (a git archive copy), and 75c90b6db (also a copy).

3. Mutants at 6fa1edd56

The log is $Q/v130/rv1207b/mut/summary.txt. Each mutant was applied alone and then restored, and every restore checked 4/4 sha256 OK with a clean tree.

  • Fast set: 61 rows from the two NEXT files, without the adoption rows. Unmutated, all 61 pass.
  • Adoption set: 5 rows, all passing unmutated.
id mutant rows failing
X1a script-only NOTE names make bootstrap unless a Makefile went aside 5 of 61
X1b the same, for a directory only 1 (SHOULD-1's row)
N1 merged = "" 2
N2 merge named even where the script went aside 1
N3 merge named without a kept Makefile 2
K1 merge named for a kept FILE too (75c's condition) 3
K2 clause given only for a directory (75c's gate) 18
K3 command test makefile or stops (75c's) 3
K4 the elif first NOTE back to plain python3 10
K5 the script-aside NOTE back to plain python3 5
K6 M18: the file clause says does 9
K7 _make_reads_first drops the / of a directory 14
T1 "(above)" put back 1
T2 the README's park/resume sentence removed 1
X6 NEXT is handed every leg's paths 2 of 5

All 15 mutants are killed. M18 (K6) is no longer equivalent, which confirms #1240's item J.

4. Gates, all through run9.sh

5. Template pins

I grepped tests/ for 18 phrases from the changed lines. Only tests/test_adopt_make_bootstrap_followups.py pins the changed text. The other hits are unrelated:

  • a sentence in the doctor test;
  • a comment in test_ruleset_hint_tracking_branch.py;
  • park/resume text in the family-root tests;
  • os.name text in test_setup_project_py.py.

6. The body

7. Merge-trees

Findings

MUST: none so far.

SHOULD: none so far.

NIT-A: a stale comment.

NIT-B: a contrived corner, inherited from 75c90b6db and not new in the merge. I did not measure it with make; it follows from GNU make's order.

Left (not done because of LAST STEP)

  • Two gates still running (the gates.sh chain, PID 4193513):
    • rv1207b-g243rest: run9 PID 415764, pytest PID 415799, logs/rv1207b-g243rest.log;
    • then rv1207b-g247rest: logs/rv1207b-g247rest.log.
    • Together they cover test_shape_doctor.py, test_scaffold_leg_path_collisions.py and the hygiene five, on 2.43 and on 2.47.1.
    • Results land in $Q/v130/rv1207b/logs/gates.summary. READY depends on both ending with rc=0.
  • The template's scripts/bootstrap.py wording, checked against BOOTSTRAP_COMMAND.
  • The body's 26 clean merges with other open PRs, re-run against today's heads.
  • A grep of tests/ for a stored digest of either template file.
  • NIT-B measured with GNU make.

All paths above are under $Q/v130/rv1207b/, where $Q is /tmp/claude-1000/-workspace-projects-openRepoShape/50cede29-ab82-4ca3-9f67-d98e1a58c8d2/scratchpad.

Lane: openreposhape-5 (openRepoShape-5)

@brettheap

Copy link
Copy Markdown
Contributor Author

Independent review of #1207 (#1132) for lane openRepoShape-5, by rv1207b and rv1207c (opus), at 6fa1edd: READY on everything measured, PARTIAL (the two rest gates are still running)

This review is PARTIAL because the coordinator sent LAST STEP at 71%. No MUST and no SHOULD were found. One gate pair is still running (see "Left"). The verdict becomes READY outright if both of those gates end with rc=0.

Head and setup

rv1207b's findings (issuecomment-6375894847), kept as measured

  1. The first review's findings are fixed:
  2. The merge of adopt-project.py: execute's NEXT prints python3 scripts/bootstrap.py where GNU make at the root reads a kept GNUmakefile or makefile, and its NOTE names that file and what it takes for make bootstrap to run there (#1047) #1121 is right:
  3. Mutants: all 15 are killed: X1a, X1b, N1-N3, K1-K7, T1, T2 and X6. Every restore was checked 4/4 sha256 OK. M18 (K6) is killed by 9 rows, which confirms adopt-project.py, shape_materialize.py and the assembly-root template: after #1121 and #1105, one GNU_MAKE_READS and one lookup, their NEXT NOTE and collision line as true as #1207's, and the NITs 1, 3b, 3c, 4 and 5 of #1105's second review #1240's item J.
  4. Gates: the PR's test files plus test_adopt_make_reads_first.py, 135 rows, all passed on git 2.43.0 and on 2.47.1.
  5. Template pins: of 18 phrases grepped from the changed lines, only tests/test_adopt_make_bootstrap_followups.py pins changed text.
  6. The body: the counts (35 and 40 rows, 34/1 and 19/21) are true. Closes #1132 is the only closing keyword, body-guard.py exits 0, and sq.sh's cut keeps the Closes and Lane lines.
  7. shape_materialize.py and the pinned AGENTS-shape.md: where the root keeps a GNUmakefile or a makefile, which GNU make reads first, the Makefile collision follow-up no longer says the shape's make targets arrive with the merge, and the pinned file says make reads the first of the three and the block reaches it only once that file holds it (#1048) #1105 still conflicts: in scripts/shape_materialize.py only, in both orders, 3 hunks of 65 lines.

rv1207c's checks (the items rv1207b left)

  1. The template's scripts/bootstrap.py wording matches BOOTSTRAP_COMMAND.
    • templates/assembly-root/scripts/bootstrap.py:7 reads make bootstrap # or: python3 scripts/bootstrap.py (python on Windows). templates/family-root's line 7 is the same.
    • BOOTSTRAP_COMMAND is "python3 scripts/bootstrap.py (python on Windows)": the same pair of names.
    • The PR does not change bootstrap.py or the template Makefile.
    • GNU make 4.3, run with -n on the template Makefile, prints python3 scripts/bootstrap.py for bootstrap, with or without OS=Windows_NT. It prints python ... only with PYTHON=python. See NIT-C.
  2. The body's "26 clean merges" still hold.
  3. No test stores a digest of either template file.
    • I took the sha256 and blob id of templates/assembly-root/AGENTS-shape.md and README.md at 0ed3e57f8 (the same blobs as main 50869f384) and at 6fa1edd56. None appears anywhere in the tree, in full or as a 12-character prefix.
    • AGENTS-shape.md is COPIED_VERBATIM: each project's shape pin digests it when the project is scaffolded. README.md is TEMPLATED and has no pin.
    • Other tests that read these files still hold:
      • test_adopt_contracts_dir.py asserts ADOPTED_SENTENCES, which comes from test_pinned_copy_text.py, and that file is in the running gate.
      • test_scaffold_free_text_values.py:318 compares line counts with the live template.
      • test_scaffold_e2e.py:781 checks for rules such as "make bootstrap", which are still present.
    • Hygiene of the added lines: every added template line is at most 77 columns. The one non-ASCII byte in the diff is an em dash carried over from main's own docstring line. No assert joins conditions with and/or (S9073).
  4. NIT-B is measured with GNU make 4.3 and confirmed. See below.
  5. The head has not moved since 6fa1edd56.

Findings

MUST: none.

SHOULD: none.

NIT-A (rv1207b): a stale comment. tests/test_adopt_next_make_reads_first.py:64 says "#480's NOTE, for a root that keeps only its own Makefile: unchanged." The merge moved that pin to add (python on Windows), so "unchanged" is no longer true. Drop the word, or say the text changed for #1132.

NIT-B (rv1207b, measured by rv1207c): a second name hidden behind the directory. This comes from the PR's own directory clause and is byte-identical at 75c90b6db. #1240 does not list it.

  • The roots: G/+m (a GNUmakefile/ directory and a makefile file) and M+G/+m (the same, plus a kept Makefile).
  • What the head prints:
    • For G/+m, NEXT's NOTE says the targets "run there only once the root keeps no directory of that name".
    • For M+G/+m, it says they run "once the follow-up merges the two, and only once the root keeps no directory of that name".
    • Both CONTRACTS_DIR lines say "only once the root keeps no directory of that name".
  • What GNU make 4.3 does:
    • With GNUmakefile/ present, make bootstrap and make contracts-dir exit 2 with "GNUmakefile: Is a directory. Stop."
    • After GNUmakefile/ is moved aside, with the merged Makefile in M+G/+m, both still exit 2: "No rule to make target 'bootstrap'", because make now reads the source's makefile.
    • Only once makefile is moved aside as well do they work: SHAPE-BOOT, rc 0.
  • In short: the condition the text names is necessary but not enough, and the text never names makefile. These roots are contrived. Add it to adopt-project.py, shape_materialize.py and the assembly-root template: after #1121 and #1105, one GNU_MAKE_READS and one lookup, their NEXT NOTE and collision line as true as #1207's, and the NITs 1, 3b, 3c, 4 and 5 of #1105's second review #1240 as a new item; it does not need a fix round.
  • Files: probe $Q/v130/rv1207c/probe-make/nitb.sh, output nitb.out.

NIT-C (rv1207c, optional): "is what make bootstrap runs".

Left (not done because of LAST STEP)

  • The rest gates at 6fa1edd56. They cover test_shape_doctor.py, test_scaffold_leg_path_collisions.py and the hygiene five, on git 2.43.0 and then 2.47.1, about 1035 rows each.
    • They are still running and were not killed: chain $Q/v130/rv1207c/gates.sh, PID 2089124.
    • 2.43 run rv1207c-g243rest: run9 PID 2089168, pytest PID 2089283, log $Q/v130/rv1207c/logs/rv1207c-g243rest.log. At 14:40:55Z it was at about 48% with no failure and 3 skips.
    • 2.47.1 run rv1207c-g247rest follows, with log $Q/v130/rv1207c/logs/rv1207c-g247rest.log.
    • Results land in $Q/v130/rv1207c/logs/gates.summary as <name> rc=N <time>. READY outright depends on both lines reading rc=0.
  • Everything else in R2's list is done.

Notes are in $Q/v130/rv1207c/STATE.md, with a CHECKPOINT dated 2026-10-24T14:41Z. Other files in that folder: mt.txt (merge-trees), prlist.json, digests.txt, probe-make/ and rv1207b-posted-review.md. $Q = /tmp/claude-1000/-workspace-projects-openRepoShape/50cede29-ab82-4ca3-9f67-d98e1a58c8d2/scratchpad.

Lane: openreposhape-5 (openRepoShape-5)

This branch has not been deployed

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

Labels

None yet

Projects

None yet

1 participant