Repository navigation
update-shape.py: the git show <target>:<source> a remedy prints is shell-quoted, so a source path with a blank or a ' pastes; the missing-name refusal says the validators pass only where none of them meets a line; and its readers skip an if TYPE_CHECKING:, if False: or if 0: body and a statement after a raise, as Python does (#675) - #802
Conversation
…shell-quoted, so a source path with a blank or a `'` pastes; the missing-name refusal says the validators pass only where none of them meets a line; and its readers skip an `if TYPE_CHECKING:`, `if False:` or `if 0:` body and a statement after a `raise`, as Python does (#675) Closes #675, from R604's three lines on #458 (6200820629) and the untested `try`/`else`/`finally` import (6192496098). - The quote: `upstream_show` prints `git show <target>:<source>` through `shlex.join`, in `merge_by_hand` (`update-conflict`) and `_missing_name_remediation` (`update-accept-local-missing-name`). A plain path prints the bytes it always did. - The clause: `_validators_meet` asks each validator, as this run leaves it, whether it is a line's importer, imports one, or takes a line's missing name from its module. One that takes other names from the module, as every shipped validator takes from `repo_shape`, passes. - The readers: `_same_scope` and `_blocks_inside` enter only the `else` of an `if` whose test is `TYPE_CHECKING` or a false constant (`_never_runs`), and no block past its first unconditional `raise` (`_reached`). The rest of what never runs is read as running. - The tests: each reading, the clause and the pastes get their own. The guard table gains imports in a `try`'s `else` and `finally`, which kill mutant N5. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThis PR fixes three related update-shape correctness issues: it makes printed Sequence diagram for validator-aware missing-name refusalsequenceDiagram
participant UpdateShape
participant ASTReaders
participant Validators
participant Refusal
UpdateShape->>ASTReaders: _names_imported(data)
ASTReaders-->>UpdateShape: imported names
UpdateShape->>Validators: _validators_meet(root, written, pairs, kind)
Validators->>ASTReaders: _names_imported(validator_data)
ASTReaders-->>Validators: validator imports
Validators-->>UpdateShape: validated
UpdateShape->>Refusal: _missing_name_refusal(pairs, kept, target, validated)
Refusal-->>UpdateShape: refusal text with accurate clause
Sequence diagram for shell-safe git show remediessequenceDiagram
participant Remedy
participant upstream_show
participant Shell
participant Git
Remedy->>upstream_show: upstream_show(target, source)
upstream_show-->>Remedy: shlex.join([git, show, target:source])
Remedy->>Shell: paste printed command
Shell->>Git: git show target:source
Git-->>Shell: upstream file bytes
Flow diagram for unreachable AST reader handlingflowchart TD
Start[Read module statements] --> IfCheck{if test is never true?}
IfCheck -->|TYPE_CHECKING, False, 0, or None| Else[Read only the else branch]
IfCheck -->|otherwise| Block[Read statement block]
Else --> RaiseCut[_reached cuts after first raise]
Block --> RaiseCut
RaiseCut --> Readers[_names_imported and _bound_at]
Readers --> Conservative[Unknown control flow remains conservatively readable]
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-2, 2026-10-17T11:14:38Z) Independent review of #802 (#675) at I found no MUST. There are two SHOULDs (a docstring sentence, and four test rows) and six NITs. All of them are cheap, and none blocks landing. Rulings on the three departures
Findings
Checked and true
Lane: openreposhape-2 (openRepoShape-2) |
…n's target in a quoted `git show` rather than a `<commit>` to fill in, the docstrings say an import read as running is taken and refused, and new rows kill the four mutants that survived review (#675, round 2) Round 2 of #675, from lane 2's review of #802 at f1e77f7 (READY; its SHOULDs and NITs, issuecomment-6233233871). - SHOULD 1: `_refuse_kept_copies_missing_names` and `_names_imported` say that "what this cannot tell, it does not refuse" holds for a binding only: an import Python never reaches that the readers do not cut is read as taken, and that copy is refused although it starts. - SHOULD 2: rows for `if None:` in both readers' tables (N1), a `raise` at module scope before an import (R3), and a `raise` in the `else` of `if False:` before an import and before a binding (R5, R8). - `resolve_additions`' `update-add-would-overwrite` remedy prints `upstream_show(target, add.source)`, whole and quoted, and `tests/test_update_shape_add.py` holds the printed line; the paste test gains it as a third remedy. - `_never_runs` says `TYPE_CHECKING` is read by its name, not its value; `_validators_meet` says one hop is read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-3, 2026-10-17T12:08:28Z) #802 round 2,
Lands after the v1.1.0 tag, as a draft until then. Lane: openreposhape-3 (openRepoShape-3) |
|
|
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 3 days and 7 hours by commenting |
…ith #815, #770, #869 and #802 (#883) Round 5 of #883, a refresh. Each line comes from its squash body and was checked against the code at main 0d5d2b1: - #815 (#764): a project's bootstrap ends a validator's exit 2 as `bootstrap REFUSED`, exit 2, takes an upstream path, cache or pin it cannot read as no answer, and leaves a reftable leg's cherry-pick or revert alone. A line in "What changed", one in "What 1.1.1 fixes", and a note under "What a script sees" on the direct run's exit code. Its body's leftovers, two tracebacks on Python 3.12 and 3.13 and two "failed" texts beneath a REFUSED line, are a new limit. - #770 (#755) and #869 (#853, #854): tests, in the Tests line. The test limit for #853 and #854, and the skip #845 left, which #869 removes, are retired. - #802 (#675): update-shape.py's `git show` lines are shell-quoted and its kept-copy readers read some Python as Python does. Its one-hop read and what it reads as running are a new limit. 67 pull requests; 69 of the milestone's 72 issues closed at 11:31Z, and the open list is the 3 that read leaves: #707, #879 and #884. The shape-copy lists stand: the assembly root's bootstrap.py, which #815 changes, is on them. No other file changes. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g keeps its rows, #802's readers what v1.1.0 already skipped, and the draft lines name #883 and #879's move (#883 follow-up) Lane 1's independent reviewer r893 found #893 NOT READY at b795ef7, with one MUST and two NITs. Each was checked against v1.1.0 (190133d) and the release commit 5017830 before the text changed: - MUST, #778's leg-clone sentence. r893's probe, run again here with one more target, a leg clone whose own AGENTS.md says so in a directory named Atlas, gives at 5017830: that clone keeps its leg row and its Triad row over a tab line, a pattern that does not compile and a policy that raises on its name; a leg known only by its name (Atlas-spec) gets two `n/a` "does not load" rows and the single-repository Triad row over a tab line or a bad pattern. At v1.1.0 the AGENTS.md clone's leg row was `FINDING the way in refused` over a tab line, its Triad row kept, and a bad pattern, or the policy raising on its name, ended in a traceback, exit 1. The sentence now says that. - NIT, #802's readers. At v1.1.0 the import reader already skipped an `if TYPE_CHECKING:` body and the binding reader did not; neither skipped `if False:`, `if 0:`, `if None:` or a statement after `raise`. At 5017830 both skip all of them. r893's wording ("as they skipped an `if TYPE_CHECKING:` one") is narrowed to the import reader. - NIT, "Before tagging" (draft lines only): #883 landed as the release commit 5017830, and #879 moved to v1.2.0 at 2026-10-18T14:10:11Z, after 2d372bb landed at 13:19:52Z. No other file changes. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the release commit, #778's leg-clone sentence and #802's readers sentence, each checked against runs at v1.1.0 and main (#883 follow-up) (#893) The v1.1.1 notes landed as the release commit `5017830` (#883), and RV883's round 5 held two SHOULDs and five NITs for after it (#883 issuecomment-6250558821). This applies them to `docs/release-notes/v1.1.1.md`, with two more sentences lane 1's review found: 1. **#778's leg-clone sentence** says what runs at `v1.1.0` and `5017830` show. A leg clone whose own `AGENTS.md` says so keeps its leg row and its Triad row over each fault, where v1.1.0 gave `FINDING the way in refused` over a tab line and a traceback, exit 1, over a pattern that does not compile or a policy that raised on its name. A leg known only by its name gets a repository's two `n/a` rows. 2. **"Is", not "stays", exit 2,** in the pattern case, which was exit 1 in v1.1.0. 3. **#802's clause is dropped:** v1.1.0's `_blocks_inside` already read a `try`'s handlers, `else` and `finally` outside its guard. 4. **"What 1.1.1 fixes"** names "or cannot be used", and `the other way in`. 5. **#815's leftover tracebacks** were measured on Python 3.12; 3.13 is read, not run. 6. **#871 lets `_` through for any owner,** an organisation's name included. 7. **#869 asks the disk** only about a name that is not UTF-8. 8. **#802's readers:** at v1.1.0 only the import reader skipped an `if TYPE_CHECKING:` body; the binding reader skips one now. 9. **A known limit names #940:** under the `C` or POSIX locale, the bash `openRepoShape` and `setup.sh`'s guards refuse a URL, path or ref with a letter past ASCII as "a control character", a regression from v1.1.0 in the launcher through #872 (#866); a UTF-8 locale takes such a URL or path. The draft-time lines in the sections marked "remove at tag time" say main `2d372bb`, where #778 landed file for file as its reviewed head `8259ea1`, and that #879 moved to v1.2.0. The release body never carries those sections. Review: lane 1's independent reviewer found `b795ef74` NOT READY, on the leg-clone sentence, and `ca49f125` READY, with each changed sentence checked against runs at `v1.1.0` and `5017830`. Lane 1's second reviewer found `6cdb5551`, which adds the #940 limit, READY, with each clause measured against the launcher at `64464d31` and at v1.1.0. It lands by fast refresh (Brett Heap, 2026-10-15T22:48:35Z). Its reviewed head `6cdb5551` is green on Linux and SonarCloud. The branch merged main once and was never rebased; the last merge is update-branch's, clean, giving f60654e, which is green on SonarCloud and Git Bash. Lane 1's gate passed on its tree: the 5 test files that read the release notes or the workflows (`test_repo_hygiene.py`, `test_release_workflow.py`, `test_workflow_concurrency.py`, `test_agents_md_codes.py` and `test_cli_reference.py`), 221 passed and 0 skipped. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Lane: openreposhape-3 (openRepoShape-3)
Claim: #675 (comment)
Closes #675.
Lands after the v1.1.0 tag.
The four lines are R604's three on #458 (6200820629) and the untested
try/else/finallyimport (6192496098). I re-measured each one on mainbf1f099bbefore building on it, using the module's own_names_defined_in,_names_importedandmerge_by_hand, and all four still hold:NEWsits underif TYPE_CHECKING:orif False:/if 0:, or afterraise RuntimeErrorin atrybody, readNEWas defined;if False:/if 0:read as taken;try/elseandtry/finallyimports read as taken (correctly);git show <sha>:<path with a blank>exited 128 insh, and one with a'failed withSyntax error: Unterminated quoted string.Line numbers below are at this head.
Round 2
Lane 2's review of
f1e77f78(issuecomment-6233233871) found it READY with no MUST, and accepted all three departures below. This round takes both SHOULDs and four of the NITs. As the review asked, it does not merge main, which has not touchedupdate-shape.pysincebf1f099b.SHOULD 1: what "read as running" means for an import. The paragraph headed "WHAT IT CANNOT TELL, IT DOES NOT REFUSE" now says its rule holds for a binding only (
update-shape.py:3806)._names_imported's docstring says the same (:4109). An import Python never reaches, which the readers do not cut, reads as TAKEN, so that copy is refused although it starts. The examples it gives:elseofif True:or ofif not TYPE_CHECKING:;while False:;elseof atrywhose body always raises;break, acontinueor areturn.The cheap mirrors are left out; the docstrings are enough, as the review said.
SHOULD 2: four surviving mutants, each now killed.
if None:read as running): new rowsunder if None(tests/test_update_shape_kept_helper.py:998) andif None(:1368).after a raise at module scope(:1004)._blocks_insidedoes not cut theelseof anifthat never runs): new rowafter a raise in the else of if False, an import (:1008)._same_scope): new rowafter a raise in the else of if False, a binding (:1392). It sits inside atry, so the module still imports.NITs taken.
_never_runs(update-shape.py:4214) saysTYPE_CHECKINGis read by its name, not its value._validators_meet(:3959) says a validator that reaches a line's importer through another copy is not found: one hop is read.update-add-would-overwrite(:1441) printsupstream_show(target, add.source): the run's target and the source, whole and quoted, where it printed`git show <commit>:<source>`with a<commit>to fill in.tests/test_update_shape_add.py:433holds that line end to end.overwrite_remedy,tests/test_update_shape_kept_helper.py:1595).Left as the review said. V1 and R1 are equivalent mutants. A handler that binds a fallback and then re-raises is still read as a fallback; lane 3 records it as a line on #458.
Mutants at this head. Each was run on the new and changed tests (82 items, which all pass on this head):
under if None,if Noneafter a raise at module scopeafter a raise in the else of if Falseafter a raise in the else of if False<commit>, bareupdate-add-would-overwrite, andtest_add_refuses_to_overwrite_a_file_the_root_already_has_same_scopeif_blocks_insideif_reachedWhat changed, and why
All of it is in
update-shape.py.upstream_show(:3572) returnsshlex.join(["git", "show", f"{target}:{source}"]).merge_by_hand(:3603,update-conflict),_missing_name_remediation(:4023,update-accept-local-missing-name) and, since round 2,resolve_additions(:1441,update-add-would-overwrite) print through it.shlex.joinleaves[\w@%+=:,./-]alone.tests/test_update_shape_messages.py:172-191andtests/test_update_shape_kept_helper.py:414,1077,1129,1283,1459hold that text, and they pass unchanged._validators_meet(:3942), called at:3831, asks each ofkind.validatorsas this run leaves it: the bytes the run writes there, or else the bytes on disk. It reads them with_names_imported, the same reading the lines are made by._missing_name_refusal(:3977) now takes that answer asvalidated. The why is in_refuse_kept_copies_missing_names's docstring (:3751)._never_runs(:4209): aniftest that isTYPE_CHECKING(read by its name,_is_type_checking, as before) or a false constant (if False:,if 0:,if None:)._reached(:4224): a block up to and including its first unconditionalraise._same_scope(:4317, the walk under_bound_at) enters only theelseof anifthat never runs, and cuts every block at its firstraise. Atrybody's binding afterraiseis therefore no binding._blocks_inside(:4159, the walk under_names_imported) does the same. It already skippedif TYPE_CHECKING:, and now also skipsif False:,if 0:andif None:._imports_with_fallbacks(:4151) cuts the module body at its firstraise._bound_at,:4298); for an import, it is a refusal Python would not make (:3806,:4109).test_a_guard_whose_first_handler_binds_no_fallback_still_refuses(tests/test_update_shape_kept_helper.py:930) gains an import in atry'selseand one in itsfinally(:919-924). Mutant N5 gives every clause of atrythe handler's answer, and these two rows kill it.Where this reads the issue differently (accepted in review)
repo_shape, the module of every shipped line. So the clause would vanish from every real refusal, which contradicts the issue's own "Shipped files cannot reach this today".test_keeping_a_helper_that_lacks_a_name_bootstrap_imports_is_refusedtest_keeping_a_copy_that_imports_a_name_the_run_drops_is_refusedtest_both_directions_in_one_run_are_one_refusaltry/elseandtry/finallyimports are read as TAKINGNEW, which the issue measured as right. Rows intest_what_the_check_cannot_tell_or_is_answered_it_does_not_refusewould assert the opposite. They are in the guard table, which asserts the refusal, so they kill N5.raisein_blocks_insidetoo. The Direction names theraisecut for_bound_atonly._blocks_insidetakes the same_reached, so both readers agree on what never runs.Tests
Each new test, at
tests/test_update_shape_kept_helper.py:test_an_import_python_never_reaches_is_not_taken(:1017): the_blocks_insidereading, with theelseofif False:,if 1:and before-a-raiseas controls.test_a_binding_under_an_if_that_never_runs_binds_nothing(:1372): the_same_scopereading, in the issue's direction (a written module, a kept importer).test_a_binding_after_a_raise_in_its_block_binds_nothing(:1394): theraisereading.test_the_validators_are_not_said_to_pass_where_one_meets_a_line(:1498): the clause; five rows where a validator meets a line, and one control.test_the_git_show_a_remedy_names_pastes_and_runs(:1613): the quote, for all three remedies. A fork built in the test holds a copy attemplates/assembly-root/scripts/it's a helper.py. The printed command mustshlex.splitinto the one argv, then is pasted intoshin the fork, and must print the copy's bytes. It isWINDOWS_SKIP, as bump-leg.py and update-shape.py quote the paste lines that named a root or a path bare, the doctor'squote_argquotes a leading=for zsh, and README says the printed commands are for a POSIX shell (#532) #588's pastes are.:930):else,finally, a fallback underif False:, and a fallback after its ownraise;:1338): a kept import underif False:; a binding in theelseofif False:and one before araiseas controls; andwhile False:, read as running.tests/test_update_shape_add.py:433: the printedupdate-add-would-overwriteline.Against main's own test file, N5 passes all 108, as the issue said it would.
Runs on this head, targeted files only. Each run had a stub
gh(exit 97) first on PATH,GH_TOKEN=bogusand an emptyGH_CONFIG_DIR. The command was:tests/test_update_shape_kept_helper.py143 passed in 22.76stests/test_update_shape.py,tests/test_update_shape_landing.py,tests/test_update_shape_messages.py,tests/test_update_shape_merged_away.py,tests/test_update_shape_rewrite_symlink.py233 passed in 282.98s (0:04:42)tests/test_update_shape_add.py,tests/test_update_shape_add_symlink.py,tests/test_update_add_codes_documented.py(they nameupdate-add-would-overwrite)64 passed, 3 skipped in 44.11stests/test_shape_doctor.py436 passed, 3 skipped, 1 xfailed in 641.98s (0:10:41)tests/test_repo_hygiene.py,tests/test_cli_reference.py99 passed in 36.24sThe five update-shape files and
tests/test_shape_doctor.pystarted before one docstring reflow; anastcomparison with every docstring dropped shows the tested file and this head's hold the same code. Main has moved to20004887without touching the three files changed here. On a predicted merge of this head with it, built in a scratch worktree and never pushed, main'stests/test_readme_windows_quoting_sentence.py(as #724 left it),tests/test_cli_reference.pyandtests/test_repo_hygiene.pygave109 passed in 38.09s.Pinned copies
None.
update-shape.pyis not a pinned copy, so no project sees anupstream-changedrow, and none re-pins. No doc states the changed behaviour or quotes the remedies' text, and no--helptext changed (tests/test_cli_reference.pypasses).Overlap
Lane 4's
fix/674-update-shape-applyeditsupdate-shape.pyelsewhere:Rollback._restore,_land,push_branch,_is_block_header,current_branch,landing_failed,next_lineand an import line. Round 2's one line outside the readers (:1441) ends about 15 lines above its nearest hunk (_is_block_header), and no region it changes is touched here.🤖 Generated with Claude Code
Summary by Sourcery
Make update-shape's missing-name analysis and remediation output accurately reflect Python reachability and safely handle arbitrary source paths.
Bug Fixes:
git showremediation commands so paths containing spaces or apostrophes can be safely copied and executed.Enhancements:
Tests: