Repository navigation
update-shape.py: apply's rollback line says to make a shut directory writable first, committed on <branch>: names what the commit holds, a branch named like a tag is pushed and read by its full name, and a quoted files: or shape: header is found as the reader finds it (#674) - #810
Conversation
…py: `apply`'s rollback line says to make a shut directory writable first, `committed on <branch>:` names what the commit holds, a branch named like a tag is pushed and read by its full name, and a quoted `files:` or `shape:` header is found as the reader finds it (#674) Four cases, each measured on main 4ce9045 with a scaffolded project and local bare remotes, git 2.43.0. The rollback line. A validator that deleted `contracts/shape-pin.yaml` and left `contracts/` at 0555 got `... kept as blob <id> in this repository, then: git -C <root> cat-file blob <id> > <root>/contracts/shape-pin.yaml`, and pasted, that failed `Permission denied`. `Rollback._restore` now asks the directory the command writes into, `_nearest_directory`'s answer, and where the file is absent and that directory will not take it, the line says `so make <that directory> writable, then:` first. The directory is named by its full path, since it is the root itself where `contracts/` went too, and need not be inside the root at all. A present file keeps "so make it writable", and an absent file under a writable directory keeps the bare command. `committed on <branch>:`. `_land` printed `paths`, and `cmd_apply` always puts the pin and the manifest in it, so a run that only drops a merged-away row, or keeps a copy with `--accept-local` at the pinned commit, said `contracts/shape-pin.yaml, project.yaml` of a commit holding the pin alone: the manifest was rewritten with the bytes it had. The line now names what `git diff-tree --root --no-commit-id -r --name-only --no-renames -z HEAD` lists, through the tool's own `_git_answer`, and keeps `paths` only should git not answer. A branch named like a tag. In a root tagged `v1.0.0`, `apply --yes --branch v1.0.0 --push` committed and then refused `pushing v1.0.0`, exit 2, `error: src refspec v1.0.0 matches more than one`, and the NEXT line it printed failed the same way pasted, as did `--branch`'s own NEXT line. `push_branch` and both NEXT lines now push `refs/heads/<b>:refs/heads/<b>` (`_full_refspec`), the form #689 and #672 ask of `family.py`'s and `bump-leg.py`'s push lines; it pushes the branch and not the tag, and `-u` still sets the upstream. `current_branch` read `git symbolic-ref --quiet --short HEAD`, which prints `heads/v1.0.0` there, so a root ON `v1.0.0` was refused `update-branch-exists` "('heads/v1.0.0' is checked out)" by `commit_on_branch`, and a `--pr` re-run there was not recognised by `_awaits_its_pull_request`, said "nothing to do" and opened nothing. It reads the ref whole now and takes `refs/heads/` off, as `Rollback.new_branch` does; a HEAD attached anywhere else is on no branch. A quoted header. `_is_block_header` asked for the exact text `<key>:` once a comment was stripped, so `"files":`, `'files':` and `files :`, which the reader and both validators read as the key `files`, passed `check`, which sent the person to `apply`, and `apply` refused `update-pin-unrecognised` ("0 `files` line(s)"); `"shape":` likewise got `update-manifest-no-shape`, "has no `shape:` block", which is false. It now asks the reader's own question, `_split_key(_strip_comment(line.strip())) == (key, "")`, at the reader's column (no space in front, and no tab among the blanks the reader refuses), and a line `_split_key` refuses is no header rather than a traceback. The header is written back as the line was, quotes included. So `"shape":` beside `shape:` is one key written twice, as the reader reads it, and is refused by name as #558 refuses a doubled bare `shape:`. The two docstrings that said such a header "is not found here" say it is. Left as they were, by the issue's own direction: AGENTS.md:283's placeholder `git -C <path> push -u origin shape/update-<sha>` (true of the branch it names; AGENTS.md is at its 492-line cap), `run_validator`'s environment (a rider that changes no verdict), and `landing_failed`'s `--` before `origin`, which its line always carried. Tests: `tests/test_update_shape_apply_lines_and_headers.py`, one or more per case, on the neighbours' harnesses. The tests that held the bare push form move with it (`tests/test_update_shape_landing.py`, `tests/test_update_shape_next_quoting.py`), and `tests/test_update_shape_header_comments.py`'s docstring no longer says a quoted header is not found. State at this checkpoint: the new file passed alone (38 passed) and all seven mutants were killed; the targeted run was stopped by PID at 48% for the account swap, with no failure before it. Closes #674 Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main moved from 4ce9045 to bf1f099. The only change to update-shape.py on main in that span is #742 (`Upstream` and `_classify_row`, the partial-clone currency row); none of #674's functions. No conflicts, so nothing was resolved by hand, and no pinned copy changed under the files #674 touches. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reviewer's GuideThe PR fixes four Sequence diagram for unambiguous branch push and detectionsequenceDiagram
participant User
participant Apply as update-shape.py
participant Git
participant Remote
User->>Apply: apply --branch v1.0.0 --push
Apply->>Git: symbolic-ref --quiet HEAD
Git-->>Apply: refs/heads/v1.0.0
Apply->>Apply: current_branch
Apply->>Git: push -q -u -- origin refs/heads/v1.0.0:refs/heads/v1.0.0
Git->>Remote: Push branch ref
Remote-->>Git: Push result
Git-->>Apply: Push result
Apply-->>User: NEXT push line uses full refspec
Sequence diagram for committed-file reportingsequenceDiagram
participant Apply as update-shape.py
participant Git
participant User
Apply->>Git: commit_on_branch
Git-->>Apply: Commit created
Apply->>Git: diff-tree --root --name-only --no-renames -z HEAD
alt Git returns file list
Git-->>Apply: Files held by HEAD
Apply-->>User: committed on branch: actual files
else Git does not answer
Git-->>Apply: No answer
Apply-->>User: committed on branch: requested paths
end
Flow diagram for update-shape apply correctionsflowchart TD
Apply[update-shape.py apply] --> Rollback[Rollback restore]
Apply --> Land[Commit and landing]
Apply --> Headers[Header discovery]
Rollback --> Directory[_nearest_directory]
Directory --> Writable{Directory writable?}
Writable -->|No| Instruction[Print make directory writable]
Writable -->|Yes| Restore[Print cat-file restore command]
Land --> CommitFiles[diff-tree lists committed files]
Land --> Push[_full_refspec]
Push --> BranchRef[refs/heads/branch to refs/heads/branch]
Headers --> ReaderRules[_split_key and _strip_comment]
ReaderRules --> Consistent[check and apply agree]
Flow diagram for reader-compatible header discoveryflowchart TD
Line[Header line] --> Strip[_strip_comment and strip whitespace]
Strip --> Indent{Top-level indentation valid?}
Indent -->|No| NotHeader[Not a header]
Indent -->|Yes| Split[_split_key]
Split --> Parsed{Reader accepts key?}
Parsed -->|No| NotHeader
Parsed -->|Yes| Match{Key matches files or shape?}
Match -->|Yes| Found[Find and preserve header exactly]
Match -->|No| NotHeader
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-1, 2026-10-17T11:23:25Z) Lane 1's independent review of #810 at 31c2ca0: CHANGES NEEDED All four fixes are correct, and every line of #674 is fixed, measured on main and at the head. Two things must change before this lands: the PR adds two Sonar code smells of kinds the review rules forbid (S1192 and S5778). Both fixes are small and mechanical, and I applied and tested them below. With those two changes and nothing else, I would call this PR-READY. Basis: the head already contains main The issue's lines
The new test file run against main's Findings, ranked1. MUST: S1192 (CRITICAL),
|
…anch off its full ref through one `_branch_of`, and the header test builds its rows before `pytest.raises`, so SonarCloud's S1192 and S5778 on #674's lines go (#674) Lane 1's review of #810 at 31c2ca0 (CHANGES NEEDED, issuecomment-6233308231) found two code smells the PR added, and nothing wrong with the four fixes. S1192: `"refs/heads/"` was written four times in update-shape.py, two of them `current_branch`'s new reads and two of them `Rollback.new_branch`'s existing ones. `_branch_of(ref)` is now the one place that takes `refs/heads/` off a full ref, returning None for a detached HEAD or one attached outside `refs/heads/`, and both callers use it. The literal is written once. S5778: `test_a_line_that_is_no_files_header_to_the_reader_is_still_counted_none` built its `Row` list inside the `pytest.raises` block beside the call under test, so a failure of either could satisfy it. The rows are built first. The `_land` comment now says the line keeps `paths` where git does not answer or lists nothing, as `named or paths` does. Left for #458's checklist, not changed here: the directory in `so make <dir> writable` is printed raw, as the tool's other prose prints a path; the command half of the line is quoted. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-1, 2026-10-17T13:06:58Z) Lane 1's re-verification of #810 at a84f445: PR-READY Both required fixes are in exactly as proposed, and nothing else in the code changed. The merge of main is clean, the tests pass on a local merge with current main, and SonarCloud reports 0 issues on this head. The Linux
New since the first review:
Lane: openreposhape-1 (openRepoShape-1) |
|
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 7 hours and 30 minutes by commenting |
|
main gained #748 (#662, `431c8cf8`), #731 (#692, `d66a9054`), #810 (#674, `d17564d9`), #809 (#688, `42aed788`), #816 (refs #684, `fa04d2cd`), #813 (#689, `f6d17bc5`) and #723 (#663, `bad05d21`) since 64ec47c. No conflict, and none of them touches README.md, the handbook, AGENTS.md, docs/ or the README cap's ledger, so every handbook marker reads as before. README.md is 1474 lines, its cap; AGENTS.md 494, its cap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#813 and #723, v1.0.0's quoted-header and plain-path-clone limits closed and the counts current to main bad05d2 (#563) Each entry is the one RV630 corrected in its pre-review of the B2 stage, put at its anchor, and checked against its squash on main: the `git merge-tree --write-tree` of main before the squash with the head the entry was written from equals the squash's tree for all seven. - #810 (#674, `d17564d9`): a new bullet, "`apply`'s lines, its branch and its headers.", after "Headers, branches and messages.". It closes v1.0.0's quoted `files:`/`shape:` header (v1.0.0.md:439) and `apply`'s half of the paths listed as staged (:433); `bump-leg.py`'s half waits for #746. - #731 (#692, `d66a9054`): appended to "Folders, asked before they are made.". - #813 (#689, `f6d17bc5`): appended to "`--tracking-branch`.". - #809 (#688, `42aed788`): a new bullet, "What `family.py init` records and commits as.", after "Names that are not UTF-8.". - #816 (part of #684, `fa04d2cd`): a new bullet, "More holder states, named.", after "family.py refuses before it writes.". #816 closes no issue; #684 keeps its reftable case and is on v1.1.1. - #748 (#662, `431c8cf8`), then #723 (#663, `bad05d21`): in the CI bullet, after #713's sentence. With #554 and #578, #748 closes v1.0.0's limit on fixtures that clone a just-pushed remote by plain path (:530). The counts, in the header comment, the counts paragraph, "What changed" and the open list: main `bad05d2`; 120 pull requests since `bfed143`; at 18:02Z, 3 of the `v1.1.0` milestone's 133 issues open and 130 closed. The open list is #563 (#630), #672 (#746) and #686 (#795); #684 and #695 (#738) are on v1.1.1. #746 and #795 have not landed, so neither has an entry yet. Nothing since 2000488 touches pyproject.toml, src/openreposhape/, install.sh or .github/workflows/. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…it closed as one clause, the list's and the known limits' markers made final, the triage lines named by their issues, and the counts current to main fc86720 (#563) - #746 (#672, `fc867207`): a new bullet, "`bump-leg.py`'s branch and its lines.", after "`bump-leg.py --dry-run` gives the real run's answer.". `git merge-tree --write-tree bad05d2 a5139a3`, the head #746 was gated and landed on, equals the squash's tree `bb920ac1`. The head the entry was written from, `4a014a79`, sat on #723's head before its squash and does not merge clean with main; `scripts/bump-leg.py` is one blob (`226e7b0d`) at `4a014a79`, `a5d473ee`, `a5139a32` and the squash, and the squash's own lines in each of its five files equal that head's, so the entry stands as written. - v1.0.0's limit on the paths listed as staged (v1.0.0.md:433) is one clause, in #746's entry: with #810 it closes. #810's half-clause is cut. `bump-leg.py --dry-run` over a pin its validators refuse (:440) stays a limit, which the dry run now says. - "What changed" runs to main `fc86720`, and says #795 is added if it lands next; Known limits' re-read marker and "as read so far" are cut. The counts marker stays as the release-body tool reads it. - Known limits name each triage by its issue: "the checklist triage of the L4 lines on #457 and #459" and "of the L1 lines on #457", where they named lanes 4 and 1 and D9, and each id reads "#457's L4-05", "#459's L4-24" and so on. - Counts in the four places: main `fc86720`; 121 pull requests since `bfed143`; at 19:08Z, 131 of the `v1.1.0` milestone's 133 issues closed, the open two #563 (#630) and #686 (#795). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Lane: openreposhape-4 (openRepoShape-4)
Claim: #674 (comment)
What changed and why
Four cases from #674, each measured on main 4ce9045 (git 2.43.0, a scaffolded project with local bare remotes) before the change and again after it.
1. The rollback line over a read-only directory (#458's 6192496098, L4-65). A validator deleted
contracts/shape-pin.yamland leftcontracts/at 0555. The tool printed...its bytes before this run are kept as blob 738777eca8e6 in this repository, then: git -C <root> cat-file blob 738777ec... > <root>/contracts/shape-pin.yaml, and pasted, that failed withcannot create ...: Permission denied(exit 2 under dash; the issue says exit 1).Rollback._restoreasks the directory the command writes into, which is_nearest_directory's answer. When the file is absent and that directory will not take it, the line reads..., so make <root>/contracts writable, then: git -C <root> cat-file blob <id> > <root>/contracts/shape-pin.yaml.contracts/was deleted too, and it need not be inside the root at all.2.
committed on <branch>:(AT-scaffold N9, 6172776355)._landprintedpaths, andcmd_applyalways puts the pin and the manifest in it. So an--accept-localrun at the pinned commit printedcommitted on shape/update-x: contracts/shape-pin.yaml, project.yaml, whilegit show --name-only HEADlisted only the pin. The same happens with the issue's merged-away row.git diff-tree --root --no-commit-id -r --name-only --no-renames -z HEADlists. It goes through the tool's own_git_answer, and falls back topathsonly if git does not answer or lists nothing.3. A branch named like a tag (6214576643,
update-shape.pyhalf). In a root taggedv1.0.0,apply --yes --branch v1.0.0 --pushmade its commit, then failed withREFUSED pushing v1.0.0, exit 2:git push -q -u -- origin v1.0.0,error: src refspec v1.0.0 matches more than one. The NEXT line it printed failed the same way when pasted, and so did the plain--branchNEXT line.push_branchand both NEXT lines now pushrefs/heads/<b>:refs/heads/<b>(_full_refspec). That is the form family.py: the NEXT push line is a forced refspec for a branch beginning with+and fails for a branch named like a tag, and a tracking branch of 251-255 characters passes--dry-run, dies at the first commit once every remote exists #689 and bump-leg.py: a root on+xis toldpush -u origin +x(forced), a branch named like a tag passesbump-leg-on-tracking-branch,bump-leg-root-dirtyprints C1 controls raw, andcommitted <sha>:lists paths the commit lacks #672 ask offamily.pyandbump-leg.py. It pushes the branch and not the tag, and-ustill setsbranch.v1.0.0.merge.remote.origin.pushmapping no longer redirects the branch. Withrefs/heads/*:refs/heads/review/*set, a baregit push -u -- origin b1lands onrefs/heads/review/b1(upstreamrefs/heads/review/b1); the full refspec lands onrefs/heads/b1, the branch the printedgh pr create --head b1names. Measured with git 2.43.0 in a scratch repository.current_branchreadsymbolic-ref --short, which printsheads/v1.0.0in that state. So onv1.0.0,apply --branch v1.0.0was refused withupdate-branch-exists ... ('heads/v1.0.0' is checked out). A--prre-run there was also not recognised:_awaits_its_pull_requestcomparedheads/v1.0.0with the bare name.current_branchnow reads the full ref and takesrefs/heads/off through_branch_of, the one spelling it shares withRollback.new_branch.+half stays refuted for this tool.BRANCH_REstill refuses+x.4. A quoted header (6175499929, L4-57). The reader and both validators read
"files":,'files':andfiles :as the keyfiles.checkpassed such a pin and sent the person toapply, which then refused withupdate-pin-unrecognised("has 0filesline(s)")."shape":was refused withupdate-manifest-no-shape("has noshape:block"), which is false._is_block_headerasks the reader's own question,_split_key(_strip_comment(line.strip())) == (key, ""), at the reader's column. A line that_split_keyrefuses is not a header (no traceback). The header is written back exactly as it was, quotes included."shape":besideshape:is therefore one key written twice, as the reader reads it, and is refused by name as update-shape.py: afiles:orshape:header with a trailing comment is rewritten and keeps it,--branchis judged the same from any directory, andapply's messages say what is true (#437, #431, #535) #558 refuses a doubledshape:.checkandapplyagree by the reader's house rule (repo_shape.py: a flow collection with an empty entry (k: [,a],k: [a,,b],k: {,}) or an entry that opens with:,#,-or?(k: [:],k: {:":},k: [#a],k: [- a]) is read, where PyYAML refuses, and a single pair in a flow sequence (k: [a: b]) reads as the string'a: b', where PyYAML reads[{'a': 'b'}]#392/repo_shape.py:_scalarreturns a BLOCK plain scalar that opens with]or}as text, sok: ],k: }and- ]read as the strings']'and'}', where YAML forbids a plain scalar to open with a flow indicator and PyYAML refuses, and the same fall-through readsk: ,,k: @a, `k:aandk: %a` #393, repo_shape.py: a plain scalar opening with an indicator, an empty flow entry and a single pair in a flow sequence are refused, sok: ]is not the text],pins: [,a]not['a']andk: [a: b]not the string'a: b'(#392, #393) #635).Left as the issue directs:
run_validator's environment (a rider; no verdict changes).git -C <path> push -u origin shape/update-<sha>. It is still true of the branch it names, and AGENTS.md is at its 492-line cap.landing_failed's--beforeorigin, which its line always carried. The plain--branchNEXT line keeps having none.docs/handbook.htmlanddocs/cli.md: unchanged. No--helptext moved, sodocs/cli.mdneeds no regeneration.Tests
New file:
tests/test_update_shape_apply_lines_and_headers.py, 38 tests. It uses the neighbours' harnesses: the partial-rollback upstream and ledger, the scaffold-rendered pins oftest_pin_rewrite_layout.py, theghstub oftest_update_shape_landing.py, and the merged-away adoption._split_keyrefuses; andcheckthenapplyend to end, with the validators green.--accept-localcase, and the issue's own merged-away row.--push; both printed push lines, run as printed (without a shell); the current-branch posture;current_branchin process; and the--prre-run, through theghstub.contracts/at 0555 over a deleted pin, and a deletedcontracts/under a 0555 root. Each line is followed: its command fails while the directory is shut and restores the pin once it is opened. Both areWINDOWS_SKIP, as their neighbours are.Tests that held the bare push form move with it:
tests/test_update_shape_landing.py(3 places) andtests/test_update_shape_next_quoting.py(3 places and 2 docstrings).tests/test_update_shape_header_comments.py's docstring no longer says a quoted header is not found.Targeted run, on the merged tree (tree
b89da034: mainbf1f099bmerged into the pushed WIP; the run started on the unpushed merge commitf99feada, whose message was then amended with the trailers, tree unchanged): 33 test files,2026 passed, 6 skipped, 1 xfailed in 27m41s, exit 0, noGIT_ALLOW_PROTOCOLexported.update-shape.py(found withgit grep -l -E "update-shape|update_shape" tests, keeping those that runapplyor call its readers), among themtest_update_shape.py,test_update_shape_landing.py,test_update_shape_partial_rollback.py,test_shape_doctor.pyandtest_family.py;test_doctor_partial_clone.py(shape-doctor.py and update-shape.py: run from a partial clone of the standard, theshape currencyrow asks without fetching whether the clone holds what the comparison reads, and is CANNOT ANSWER with a fetch for a person to run where it does not, and a read that fails is refused, never a copy calledboth(#683) #742's file, which runs the mergedupdate-shape.py) andtest_commit_trailers.py; andtest_repo_hygiene.py,test_cli_reference.pyandtest_agents_md_codes.py.test_shape_doctor.py:10429(the PowerShell line is printed, andpowershell.exeexists, only on a Windows host) and 3 intest_update_shape_add_symlink.py:832(NTFS junctions).test_shape_doctor.py:4154's strict xfail for siblings.py (make siblings): a FILE, or a directory it cannot list, at../<Project>ends in a traceback (NotADirectoryErrororPermissionErroratsiblings.py:558), exit 1, where the doctor calls the slot NOT A CLONE #735, not this change.Fix round 1 (head
088a2267, on lane 1's review of31c2ca02, which found the four fixes correct and two SonarCloud smells):_branch_ofis now the one place that takesrefs/heads/off a full ref, forcurrent_branchandRollback.new_branch(S1192, the literal written four times), and the header test builds itsRowlist beforepytest.raises(S5778). Nothing else inupdate-shape.pymoved but the wording of one comment in_land("or list nothing"). Rerun on those bytes: the new file,test_update_shape_partial_rollback.py,test_update_shape_landing.py,test_update_shape.py,test_update_shape_next_quoting.py,test_update_shape_pr_base.py,test_repo_hygiene.py,test_cli_reference.pyandtest_agents_md_codes.py:446 passed in 6m31s, no skips, exit 0.Mutants, each reverting one part of the fix and run against the new file. All seven were killed:
path.parentinstead of_nearest_directorycurrent_branchback to--short_full_refspecreturns the bare name_is_block_headerback to the exact textYamlErrorcatchcommitted onprintspathsThe seven mutants ran before main was merged. The fix code they mutated differs from this head's only in one comment of
_land(checked withdiff), and the merge changed none of the functions they revert: main's only change toupdate-shape.pyin that span is #742'sUpstreamand_classify_row.What it cannot tell
so make <dir> writableis printed raw, as the tool's other prose prints a path (make {README} writable). The command half of the line is quoted; a person who copies the directory intochmodfrom a root whose path holds a space or a$quotes it. Left for Tracking: update-shape.py and bump-leg.py, keeping a project's shape copies and leg pins current #458's checklist.os.accesson Windows ignores ACLs and answers by the read-only attribute alone. So on Windows the "make writable" words appear only where that attribute is set, as with the existing checkout-form check. The chmod tests skip there.shape:. Thefiles:block is still found at its first header and written to the end of the file, as before.Shared files
Main
bf1f099bwas merged into the branch (git merge --no-ff, no conflicts; the WIP commit on the branch is untouched).git merge-tree --write-tree, each head read withls-remotefirst, on 2026-10-17:bf1f099bb89da034(this head's own tree: the head already holds main)bb439a29(its one commit since touchestests/test_pins_child_kind_and_siblings_root_spelling.pyonly)c774b9b3l3-755-merged-away-tests,tests/test_update_shape_merged_away.py), the only open PR in the update-shape tests837920c278b508acfix/672-bump-leg-branch-and-push-line), thebump-leg.pytwin of the push-line casea5d473ee2c36e404None of the 36 open pull requests (read at 10:28Z) touches
update-shape.pyor the four test files this PR changes, so there is nothing else to compare.update-shape.pyis not a pinned copy: no project re-pins.Closes #674
🤖 Generated with Claude Code
Summary by Sourcery
Fix
update-shape.py applyreporting, branch handling, rollback guidance, and header detection so its actions and printed commands remain accurate and executable.New Features:
update-shape.py applyrecognize quoted and spacedfiles:andshape:headers consistently with the YAML reader.Bug Fixes:
Documentation:
Tests: