Skip to content

shape-doctor.py: in a root that lost scripts/repo_shape.py, the agent-files row's WHY puts the file back before it names validate-pins.py to confirm the digest (or says why no command does), and names the standard's copy where the root has none (#776) - #780

Merged
brettheap merged 11 commits into
mainfrom
l3-776-agent-files-why
Oct 18, 2026

Conversation

@brettheap

@brettheap brettheap commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Lane: openreposhape-3 (openRepoShape-3)
Claim: #776 (comment)

Closes #776.

Stacked on #771; lands after it. Lands after the v1.1.0 tag. (Its base is #771's branch l3-750-lockstep-next, so this diff is the #776 commits and the merge of #771's latest head; #771 is at f1234bc.)

What changed and why. In a root that lost scripts/repo_shape.py, the agent-files row for a deleted pinned copy (AGENTS-shape.md) ends its WHY with "then python3 <root>/scripts/validate-pins.py --root <root> to confirm the digest". That line exits 1 with ModuleNotFoundError: No module named 'repo_shape' there, on main as on #771 (found by RV-a as S1 of its review of #771; the row's NEXT, git -C <root> checkout -- AGENTS-shape.md, runs). The same sentence also named the root's own validate-pins.py where the root has none.

Tests (stub gh first on PATH that exits 97, GH_TOKEN=bogus, empty GH_CONFIG_DIR; /usr/bin/python3 -m pytest -q -p no:cacheprovider --basetemp=<scratch>/bt -o tmp_path_retention_policy=failed <files>):

Round 2 (RV-a's review of 676f80a, one MUST and three NITs; head c4c96b1, after a plain merge of #771's f1234bc).

Round 3 (RV-a's optional NIT, tests only; head below, doctor unchanged). The N2 sentence had a surviving mutant: without its pinned_paths check (shape-doctor.py:7669) no test failed. tests/test_doctor_validator_rows.py now builds a root with no gate of its own whose contracts/shape-pin.yaml has no files: row for it (without_its_own_gate(..., pin_row=False), the row removed as bytes), for an assembly root and a holder: test_the_agent_files_why_says_nothing_of_a_gate_the_pin_has_no_row_for asserts the WHY is the template line named and no "Until" sentence, and test_the_template_line_for_a_gate_the_pin_has_no_row_for_passes pastes the NEXT and then that line from / and asserts exit 0 with no shape-copy-missing. With the check dropped, the first fails for both kinds (2 failed, 14 passed of -k "agent_files or template_line"); on the head all 16 pass. tests/test_doctor_validator_rows.py tests/test_repo_hygiene.py tests/test_cli_reference.py: 170 passed.

Docs. None state the agent-files WHY, so none changed. Pinned copies: none touched; shape-doctor.py is in no copy list.

🤖 Generated with Claude Code

Summary by Sourcery

Make agent-files remediation guidance restore missing validator dependencies before confirming pinned-file digests and select only an appropriate runnable gate.

New Features:

  • Improve the agent-files guidance for pinned copies by naming a runnable digest-validation command appropriate to the root type and available validator copy.

Bug Fixes:

  • Prevent the guidance from directing users to a validator that fails when the root is missing scripts/repo_shape.py by restoring that file first or explaining when no cure is available.
  • Use the family validator for family holders and avoid naming a missing standard validator when the pin has no corresponding file row.

Enhancements:

  • Share gate-command selection between lockstep and agent-file guidance so both rows consistently identify runnable validator commands.

Tests:

  • Add coverage for lost and committed repo_shape.py, family holders, standard-template validators, unavailable gates, and pin rows without validator files.
  • Extend command-property tests to cover the updated pinned agent-files guidance for assembly roots and holders.

Brett Heap and others added 3 commits October 17, 2026 00:53
…gs` and `members` lockstep NEXT is the `pins` and `family` rows' `git -C <root> checkout HEAD -- scripts/repo_shape.py` (none where HEAD no longer holds it) and its WHY names the gate to run after, where it named the root's own gate, which dies on the import (#750)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…his root committed", and the lockstep line of `legs` and `members`, which the reader never ran, says "run" and not "run again"; the test module's header says the tests read a why for the command and the file it names (#750)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ent-files` row's WHY puts the file back before it names `validate-pins.py` to confirm the digest (or says why no command does), and names the standard's copy where the root has none, where it named the root's own gate, which dies on the import (#776)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Oct 17, 2026

Copy link
Copy Markdown

Reviewer's Guide

The PR fixes misleading agent-files guidance by sharing validator command selection and dynamically describing how to restore a missing repo_shape.py before confirming a pin digest, with comprehensive regression coverage for root, template, absent, and committed-deletion cases.

Flow diagram for agent-files digest confirmation guidance

flowchart TD
    A[check_agent_files] --> B[confirm_digest]
    B --> C[gate_line]
    C --> D{Named validator command?}
    D -->|No| E[Return no gate guidance]
    D -->|Yes| F{Root validator imports repo_shape?}
    F -->|Yes| G[Name validate-pins.py command]
    F -->|No| H{HEAD contains scripts/repo_shape.py?}
    H -->|Yes| I[lost_repo_shape_cure and restore_from_head]
    I --> J[Name restore command first]
    J --> G
    H -->|No| K[Explain why no cure is available]
    K --> G
Loading

File-Level Changes

Change Details Files
Centralize gate selection and command generation so validator rows consistently identify a runnable root or standard-template copy.
  • Added gate_line to share copy selection, PYTHONPATH handling, and omission behavior.
  • Updated lockstep_next to use the shared helper without changing its output behavior.
shape-doctor.py
Make the pinned agent-files explanation prescribe a valid digest-confirmation sequence when the root’s validator depends on a missing repo_shape.py.
  • Added confirm_digest to prepend the existing restore-from-HEAD cure when applicable.
  • Explains the no-cure case when HEAD no longer contains the file.
  • Names the standard template validator when the root lacks its own copy, and omits the gate when no runnable line can be formed.
  • Integrated the helper into the pinned agent-files WHY while preserving NEXT, status, and detail.
shape-doctor.py
Add regression and command-property coverage for all validator-source and missing-module scenarios.
  • Verify cure-before-gate ordering and successful execution after restoration.
  • Cover committed deletion, unchanged healthy roots, standard-template fallback, and unreadable validator copies.
  • Ensure the generated NEXT remains a valid command.
tests/test_doctor_validator_rows.py
tests/test_doctor_next_is_a_command.py

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

@brettheap

Copy link
Copy Markdown
Contributor Author

(lane openRepoShape-3, 2026-10-17T04:14:33Z) Independent review of #780 by RV-a (opus), on head 676f80a7 (base l3-750-lockstep-next): NOT READY. There is one MUST, and it is small. Three NITs. It lands only after the v1.1.0 tag, after #771.

What works (probe on main and main + #780, every line the WHY names pasted):

case main #780
P1 repo_shape.py intact, AGENTS-shape.md deleted WHY byte-identical to main; the gate prints pins ok
P2 repo_shape.py lost in the tree the named gate dies, ModuleNotFoundError cure, then gate. Pasted in order: NEXT, cure and gate all exit 0
P3 committed deletion no cure; names the parent commit and the gate
P4 no own validate-pins.py named a missing file, can't open file, exit 2 the template line with its PYTHONPATH

gate_line leaves lockstep_next as it was. The tests bite: against the base's doctor they give 5 failed (all new) and 4 passed.

M1: in a family holder, the WHY names the assembly-root gate.

NITs:

Mutants (-k agent_files; the head passes 9):

mutant result
no cure 3 killed
always the root's own path 1 killed
drop the no-line branch 1 killed
holder gate untested (M1)

Tests:

Sonar passes. No doc describes this WHY, and no pinned copy is touched.

Base l3-750-lockstep-next: fine for review and for landing after #771.

Lane: openreposhape-3 (openRepoShape-3)

Brett Heap and others added 3 commits October 17, 2026 04:20
…root committed" as the other rows that restore `scripts/repo_shape.py` do, so a holder whose `family.yaml` does not load is not told "this project" (#750)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…alidate-family.py` and not the assembly root's `validate-pins.py` (and the holder gets #737's cure for a lost `repo_shape.py`), says to run the gate once the file is back "to confirm the digest" where HEAD lacks it, says the standard's copy also reports the root's missing gate, and the tests parse the lines they run with `shlex` (#776)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@brettheap

Copy link
Copy Markdown
Contributor Author

(lane openRepoShape-3, 2026-10-17T04:43:01Z) RV-a's re-verification of #780 round 2 (c4c96b1d): READY. M1 and N1-N3 are fixed. One new optional NIT. It lands only after the v1.1.0 tag, AFTER #771, and never into l3-750-lockstep-next. Linux tests was pending at 04:42Z.

Every line pasted through sh from /, on main + #780:

case the WHY says pasted
H1 holder, AGENTS-shape.md deleted python3 <holder>/scripts/validate-family.py --root <holder> NEXT exits 0, then family ok, exit 0
H2 holder that also lost repo_shape.py ". First git -C <holder> checkout HEAD -- scripts/repo_shape.py: … Then run …validate-family.py… once the file is back, to confirm the digest" NEXT, cure and family ok all exit 0
H3 holder without its own validate-family.py the family template line + ". Until scripts/validate-family.py is back that exits 1, reporting the file as pinned but absent" exits 1, as stated
H4 holder, committed deletion no cure; ". HEAD does not hold … Then run … once the file is back, to confirm the digest" the gate dies until the file is back, as stated
P1-P4 (assembly root) unchanged from round 1 (P1 byte-identical to main) as stated

N1 and N2 cannot both apply. The purpose wording is built only on the own-copy branch, and N2 requires not own_copy (:7669).

Mutants (-k agent_files; the head passes 17):

mutant result
holder gate back to PINS_GATE 6 killed
drop the purpose 4 killed
N2 sentence without the pinned_paths check survives

New NIT (tests/test_doctor_validator_rows.py:1114): RV-a measured the surviving mutant's case.

  • The case: a root without its own validate-pins.py whose pin has no row for it.
  • The head is right there: no "Until …" sentence, and the template line exits 0 with pins ok. The mutant would print a false "exits 1".
  • Fix: add that case.

Tests:

Merges: clean with bf1f099b and every open lane-3 v1.1.1 PR at its current head.

#771's NIT round (f1234bc6) is READY too.

  • unloaded_manifest_row (:2111) says "this root committed", and its CASES opening matches. "this project committed" no longer appears anywhere.
  • The body's :1814-1857 span is right.
  • 114 passed; clean merges.

Lane: openreposhape-3 (openRepoShape-3)

…hose shape pin has no row for it gets the standard's line named with no "Until ... exits 1" sentence, and that line, pasted after the NEXT, exits 0, so the `pinned_paths` check in the `agent-files` WHY is held (#776)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@brettheap

Copy link
Copy Markdown
Contributor Author

(lane openRepoShape-3, 2026-10-17T04:47:37Z) RV-a's re-verification of #780 round 3 (68f27877): READY. Tests only; shape-doctor.py is byte-identical to round 2.

Lane: openreposhape-3 (openRepoShape-3)

This was referenced Oct 17, 2026
brettheap added a commit that referenced this pull request Oct 18, 2026
…gs` and `members` lockstep NEXT is the `pins` and `family` rows' `git -C <root> checkout HEAD -- scripts/repo_shape.py` (none where HEAD no longer holds it), where it named the root's own gate, which dies on the import (#750) (#771)

Closes #750.

In a root that lost `scripts/repo_shape.py`, with a gitlink off its pin, the
`legs` and `members` rows' lockstep NEXT was the root's own `validate-pins.py`
or `validate-family.py` line. That gate dies on `import repo_shape` and exits
1, so the line could not do what its WHY said of it, while the `pins` and
`family` rows beside it already printed the `checkout HEAD` cure (#737).

Now `lockstep_next` in `shape-doctor.py` returns that cure where the root's
own copy cannot import `repo_shape`:
`git -C <root> checkout HEAD -- scripts/repo_shape.py`, or no NEXT where HEAD
no longer holds the file. Its WHY names the gate to run once the file is back.
Every other lockstep line, the verdict and the exit code are unchanged. The
cure's WHY, which the `pins`, `family` and `manifest` crash rows and the
unloaded-manifest row share, now says "the copy this root committed" where it
said "this project"; in `legs` and `members` it says "run" where the crash
rows say "run again". `tests/test_doctor_validator_rows.py` pastes the cure
and then the gate from `/`.

Lane 3's independent reviewer RV-a (opus) found it READY on `c90a137c` (#771
issuecomment-6227877943) and again on `d4ddac85` (-6229504137). Its one
SHOULD, the `agent-files` row's WHY, which still names a gate that dies, is
#776, in #780. The last commit, `f1234bc6`, takes round 2's one new NIT, the
unloaded-manifest row's "this project"; RV771n found it READY (#771
issuecomment-6244822339).

It lands on Brett Heap's word "merge lane 3's v1.1.1 drafts after the v1.1.0
tag" (2026-10-17T13:00:57Z).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Base automatically changed from l3-750-lockstep-next to main October 18, 2026 05:17
Brett Heap and others added 2 commits October 18, 2026 05:18
…step 1 of 3 after #771 squash-landed as 3bf2d3c)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… 2 of 3; the squash's tree equals the final head's, so #780's own lines are kept)

shape-doctor.py, tests/test_doctor_next_is_a_command.py and tests/test_doctor_validator_rows.py conflicted and were taken ours: F (28b13d6) and S (3bf2d3c) have the same tree, so this merge changes no file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@brettheap

Copy link
Copy Markdown
Contributor Author

(lane openRepoShape-3, 2026-10-18T05:31:26Z) #780 is restacked after #771's squash (3bf2d3c), as 40167950, by lane 3's three-merge recipe (writer R780, opus-xhigh):

  1. bf2e62e5 merges shape-doctor.py: in a root that lost scripts/repo_shape.py, the legs and members lockstep NEXT is the pins and family rows' git -C <root> checkout HEAD -- scripts/repo_shape.py (none where HEAD no longer holds it), where it named the root's own gate, which dies on the import (#750) #771's final head 28b13d67. It is clean.
  2. 40167950 merges the squash 3bf2d3c9, taking ours only in shape-doctor.py: in a root that lost scripts/repo_shape.py, the legs and members lockstep NEXT is the pins and family rows' git -C <root> checkout HEAD -- scripts/repo_shape.py (none where HEAD no longer holds it), where it named the root's own gate, which dies on the import (#750) #771's three files, which conflicted: shape-doctor.py, tests/test_doctor_next_is_a_command.py and tests/test_doctor_validator_rows.py. The squash and the final head have the same tree, 5dee1c95, and this merge changed nothing.
  3. Main was 3bf2d3c9 itself, so the third merge was "Already up to date".

Lane: openreposhape-3 (openRepoShape-3)

@sonarqubecloud

Copy link
Copy Markdown

@brettheap
brettheap marked this pull request as ready for review October 18, 2026 09:24
@sourcery-ai

sourcery-ai Bot commented Oct 18, 2026

Copy link
Copy Markdown

Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 2 days and 6 hours by commenting @sourcery-ai review. Upgrade to get a review now.

@brettheap
brettheap merged commit 85aa8a1 into main Oct 18, 2026
5 checks passed
@brettheap
brettheap deleted the l3-776-agent-files-why branch October 18, 2026 09:24
brettheap pushed a commit that referenced this pull request Oct 18, 2026
…and its round 2, and run to main 33c5128 (#883)

Round 2b of #883. It answers RV883's full NIT list on ede678c (lane 3,
2026-10-18T09:29:28Z) and RV883's round 2 on 86baec5 (#883
issuecomment-6248362246). Each finding was checked against the code at
main 33c5128 before the text changed:

- The #843 limit is two: what `adopt-work-dir-shared` leaves open (group
  write, Windows, root as the other user, this user's own processes, the
  default work directory), and what it now refuses that v1.1.0 used
  (another user's directory, a run as root included, a 0777 directory
  without the sticky bit, a drive mounted without ownership), with the
  way out. "What a script sees" names `adopt-work-dir-shared` and
  `adopt-work-dir-occupied`.
- The doctor's NEXT lines, not its `members` row, read no url from an
  unmerged `.gitmodules`; the `make siblings` limit names that row.
  `siblings.py` has no unmerged rule; the `validate.yml` templates do.
- `validate-family.py`'s `family ok` is scoped to `--no-members` and to a
  holder whose members are checked out.
- The wording points: `update-pr-no-repository` under `--pr`, family.py's
  `git rm -r --cached` line, `MSYSTEM` set and not empty, `..` and
  `naming-unclassified`, the keys git refuses with no value, the url the
  root's config keeps, `contracts: cannot be listed`, install.sh's newest
  tag and redirect location, the scan test's name,
  `update-rewrite-symlink`, `os.access` for root, `LC_ALL=C`,
  `update-validators-red`, `heads/main` in both keys, where
  `plan written to` came, junctions on Python 3.12, the approval click and
  the run's approvals, and the back-port route's one run.
- #457's L1-17 is narrowed by #825; #853 and #854 join #834 as test
  limits.

The refresh past c5359df adds #870 (#864), #876 (#839), #791 (#787),
#859 (#838), #780 (#776), #873 (#857) and #875 (#874): 57 pull requests.
It drops the limits for #776, #839 and #874's ref rule, rewrites the #838
clauses, and adds the limits those squashes name (#863, #879, #884, an
older git without `GIT_NO_LAZY_FETCH`, family.py's link guard). The open
list is the 14 that the 09:42Z milestone read leaves after #875: #675,
#707, #739, #755, #764, #834, #840, #853, #854, #856, #863, #878, #879
and #884.

The six-and-six shape-copy lists stand: from c5359df to 33c5128 the
templates and repo_shape.py changed only in the family root's
bootstrap.py and validate-family.py, both listed. No other file changes.

Lane: openreposhape-1 (openRepoShape-1)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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