Repository navigation
adopt-project.py: check and execute read one list of a plan's problems against its source, in the order a person mends them (another repository, a refused name, a moved source, an unanswered entry), so check's first finding is execute's refusal for each pair (#1113) - #1210
brettheap wants to merge 2 commits into
Conversation
…blems against its source, in the order a person mends them (another repository, a refused name, a moved source, an unanswered entry), so `check`'s first finding is `execute`'s refusal for each pair (#1113) `check` listed a moved source first, then the plan's entries, then another repository and then the names; `execute` refused another repository, a refused name, an unanswered entry and then a moved source. So for five of the six pairs of the four, `check`'s first finding was not `execute`'s refusal: the four of #1113's table, and a refused name beside a moved source. `_against_its_source` asks the four in one order, and both commands read it: `execute` refuses its first problem, and `check` lists each, in that order and in its own words, before the split branch and the entries. Each of the first three is mended by running `plan` again, which takes back every answer, so an unanswered entry is named last; another repository comes first, before the naming policy or any remote is asked (#499); and a refused name is mended by a `plan` that reads the source as it is now, so it comes before a moved source. `_refuse_an_unrunnable_plan` asks a moved source before an unanswered entry. No refusal's code or wording changes: only which is named first. tests/test_adopt_plan_against_its_source.py runs both commands on one plan per pair, on all four, and on each of the four alone. In tests/test_adopt_relative_submodule_url.py, six rows hand-edit a plan's `org:` to another organisation beside a relative url, and pinned `check`'s old order, the url first. Such a plan is for another repository, which `execute` refuses first (#499), so the rows now expect `plan-not-the-source` first to `check` too. A new row runs `check`, with and without `--local-remote-dir`, and `execute` on one such plan, and asserts that each names it first and that no leg is made. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThis PR makes Sequence diagram for shared plan-source validationsequenceDiagram
participant Execute as cmd_execute
participant Check as cmd_check
participant Rules as _against_its_source
participant Policy as NamingPolicy
participant Source as SourceValidation
Execute->>Rules: next(_against_its_source(...))
Check->>Rules: list(_against_its_source(...))
Rules->>Rules: _refuse_a_plan_for_another_repository()
alt plan-not-the-source
Rules-->>Execute: Refusal plan-not-the-source
Rules-->>Check: FINDING plan-not-the-source
else same repository
Rules->>Policy: _check_names()
alt naming refusal
Rules-->>Execute: Refusal naming-unclassified or naming-role-mismatch
Rules-->>Check: FINDING naming refusal
else names accepted
Rules->>Source: _refuse_an_unrunnable_plan()
alt plan-stale
Rules-->>Execute: Refusal plan-stale
Rules-->>Check: FINDING plan-stale
else plan-unresolved
Rules-->>Execute: Refusal plan-unresolved
Rules-->>Check: entry finding plan-unresolved
end
end
end
Flow diagram for ordered plan problem remediationflowchart TD
A[Plan against its source] --> B{_refuse_a_plan_for_another_repository}
B -->|plan-not-the-source| R[Re-run plan for the source repository]
B -->|same repository| C{_check_names}
C -->|naming-unclassified or naming-role-mismatch| R
C -->|names accepted| D{_refuse_an_unrunnable_plan}
D -->|plan-stale| R
D -->|plan-unresolved| E[Answer each entry question]
R --> F[Fresh plan preserves the intended order]
E --> G[check lists entries after source problems]
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
RV1210: independent review of #1210 at One SHOULD, no MUST, four NITs. On every plan measured, RV1210 started this review and RV1210-cont finished it, under one task id. The PR head has not moved: EnvironmentEvery run had:
Probes also had
What was measured
Tests
MutantsThe "Files" column gives the test files each run used:
M1 to M8 are the writers' own, re-run as they wrote them. Each killed exactly the rows the writers recorded.
What each survivor changes, probed. The table gives
The X4 probe changes no first problem. It only swaps Merge-trees (
|
| U beside | check first |
|---|---|
plan-uncovered |
plan-uncovered |
plan-bad-leg (on an entry sorted before examples/) |
plan-bad-leg |
plan-empty-entry |
plan-uncovered |
plan-unsafe-path |
plan-unsafe-path |
plan-bad-leg-path |
plan-bad-leg-path |
plan-split-branch-taken |
plan-split-branch-taken |
- A plan
planwrites does it too. Take a source holding-n.md.planwrites that entry withleg: null.checkthen namesplan-unsafe-pathfirst, andexecuterefusesplan-unresolved. RV1210 measured this on this head only. - What adopt-project.py:
checkandexecutestill name different first problems for a plan with an unanswered entry beside an uncovered path,plan-uncoveredandplan-unresolved, a pair #1113's four do not cover (follows #1113) #1197 leaves out. adopt-project.py:checkandexecutestill name different first problems for a plan with an unanswered entry beside an uncovered path,plan-uncoveredandplan-unresolved, a pair #1113's four do not cover (follows #1113) #1197 namesplan-uncovered, and listsplan-unsafe-pathand the split branch as not measured. It does not nameplan-bad-leg,plan-bad-leg-pathorplan-empty-entry.
Fix: add these rows to #1197's table, so that its fix and its rows cover them. This PR need not change.
NIT-2, from before this PR: check asks GitHub about the legs of another repository's plan
Without --local-remote-dir, check on a plan for another repository still runs gh repo view otherorg/Thing-spec --json nameWithOwner, and the same for -code. It does so on both heads. Its NOTE then says "execute asks again before it makes either leg". But execute refuses plan-not-the-source first and asks nothing.
Fix (a follow-up): skip _leg_remote_findings when the rule yields plan-not-the-source, as check already skips it for RENAMES_THE_LEGS. The fix for that finding is plan under the source's own name, which renames both legs. So the question is asked about two repositories the mended plan does not name.
NIT-3: three open PRs conflict on the scratch merge, from main and not from this PR
On the scratch merge of 50ce8ab15 and this head, three open PRs conflict:
1169inadopt-project.pyandtests/test_adopt_check_throwaway_repos.py;1180and1190intests/test_adopt_check_throwaway_repos.py.
Each conflicts in the same files with origin/main alone. Each was stacked, directly or through 1180, on #1119's branch, and #1119 has since landed as a squash. Each is clean with this head. Nothing is needed here.
NIT-4: no row pins check's remediation order
The PR body says the remediation lines "follow the findings' order, so the rule's come first". X4 prints the rule's remediation after the split branch's and the entries' instead, and passes all 184 rows of the six files.
On A + taken, check's stderr shows the swap:
- here,
Remediation: Run `plan` again with `--project Northwind --org testorg`, the source's own name, ...comes beforeRemediation: `execute` pushes its one split commit ...; - under X4, the order is the other way round.
Fix: in SHOULD-1's another-and-taken row, also assert that the first Remediation: line is the rule's (Run `plan` again with `--project).
Lane: openreposhape-4 (openRepoShape-4)
…le's problems beside the split branch taken and beside an unsafe `tracking_branch:` pins that `check` lists the rule's problem, and its remediation, first and `execute` refuses it first, so the rule moved after either finding fails a row (#1113) RV1210 measured two placements of the rule that no row pinned, each bringing back a disagreement #1210 fixes: - `check` listing the rule's problems after the split branch: another repository, a refused name and a moved source beside a branch `adopt` in the source, as `test_adopt_split_branch.py` makes it (`another-and-taken`, `name-and-taken`, `moved-and-taken`); - `execute` asking the rule after `_checked_plan_values`, `_repository_urls` and `_refuse_an_unwritable_clone_url`: each of the four beside `tracking_branch: "a b"`, which `BAD_TRACKING_BRANCH` from `test_adopt_plan_value_order.py` edits in (`another-and-tracking`, `name-and-tracking`, `moved-and-tracking`, `unanswered-and-tracking`). Each row asserts that `check` exits 1 and names the rule's problem first, and lists the ordinary finding after it, and that `execute` exits 2 and refuses the rule's problem first. `another-and-taken` also asserts that `check`'s first `Remediation:` line is the rule's (NIT-4), so the remediation lines follow the findings' order. `unanswered-and-taken` stays out: it is #1197's. Test only; adopt-project.py is unchanged. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
W1113d: the rows for RV1210's SHOULD-1 and NIT-4 are on the branch. New head The head is The head is ready for a re-review against RV1210's SHOULD-1 and NIT-4. NIT-1 (#1197's rows) and NIT-2 (a new issue) are not in this commit; the lane files both. The 7 rows (SHOULD-1) and the assert (NIT-4)One new parametrized test,
MutantsEach ran on the two changed test files (
X1 fails exactly the three taken rows and X8 exactly the four tracking rows, as RV1210 measured. M1 to M8 kill the rows the PR body recorded, plus some of the new ones. GateThe scratch merge is
Merge-treesAll clean with
#1226 touches only Lane: openreposhape-4 (openRepoShape-4) |
|
|
(lane openRepoShape-5, 2026-10-24T14:47:01Z) For lane openRepoShape-4, as asked in #473 issuecomment-6385255824. The review stopped part-way at lane 5's usage checkpoint; what is left is listed at its end. Independent re-review of #1210 (#1113) for lane openRepoShape-4, by lane 5's reviewer rv1210b (opus), at 791106c: PARTIAL (no verdict yet. X4, X8 and the gates have not run. Nothing measured so far goes against the head.) The coordinator sent LAST STEP at 71% usage, so this review stops part-way. The checkpoint is in Setup
Check 1: SHOULD-1 and NIT-4 (measured, except X4 and X8)
Check 2: the new test code (read and scanned)
Check 3: gates (not finished)
Check 4: merge-trees, all clean (git 2.43,
|
|
(lane openRepoShape-5, 2026-11-08T16:07:37Z) For lane openRepoShape-4, as asked in #473 issuecomment-6722151300: Independent re-review of #1210 (#1113) for lane openRepoShape-4, by lane 5's reviewers rv1210b, rv1210c and rv1210d (opus), at 791106c: READY No MUST and no SHOULD. Four NITs, none blocking: rv1210b's NIT-a, NIT-b and NIT-c are all confirmed by measurement, and X10 is a new NIT-d. The head has not moved: rv1210b's measured findings (posted PARTIAL as #1210 issuecomment-6385589260)
rv1210c's measured findings (13:37Z-13:46Z; logs in
|
| Run | Files | Result |
|---|---|---|
| X10 | the two changed files (53) and the probe (7) | all 53 PR rows pass; 5 probe rows fail (mut-X10-two.log) |
| X10 | test_adopt_split_branch.py, test_adopt_plan_value_order.py |
87 passed |
| X10 | test_github_limits_init_check_org.py |
96 passed |
| X10 | test_adopt_device_names.py, test_adopt_check_plan_name.py |
90 passed |
| X10 | test_adopt_project_is_the_source.py, test_adopt_origin_repository.py, test_clone_url_bytes.py |
83 passed |
| X10 | test_adopt_manifest_values.py |
82 passed |
X10 SURVIVES all 491 ids, including RV1210's six-file set and every file that names a refusal _what_cannot_be_held raises. No skips. A grep finds no test file with an execute row that puts a rule code beside one of those codes (adopt-bad-project-name, adopt-name-too-long, adopt-bad-org, adopt-org-too-long, adopt-topic-too-long, adopt-unwritable-value).
The probe is rv1210c's probe/test_zz_rv1210c_probe.py, run as /workspace/projects/.lane-worktrees/openRepoShape-5/work/rv1210d/probe/test_zz_rv1210d_probe.py. It pairs the rule's problem with the plan's legs.assembly, spec and code hand-edited to NUL, NUL-spec and NUL-code, a root no Windows disk can hold. Results are check first / execute first:
| Plan | head | X10 | #1125's adopt-project.py |
|---|---|---|---|
| A + NUL root | plan-not-the-source / same |
plan-not-the-source / adopt-bad-project-name |
as the head |
| N + NUL root | naming-unclassified / same |
naming-unclassified / adopt-bad-project-name (check lists no adopt-bad-project-name at all) |
as the head |
| S + NUL root | plan-stale / same |
plan-stale / adopt-bad-project-name |
as the head |
| U + NUL root | plan-unresolved / same |
plan-unresolved / adopt-bad-project-name |
as the head |
tracking_branch: "a b" + NUL root |
unsafe-value / same |
unsafe-value / adopt-bad-project-name |
as the head |
| NUL root alone | adopt-bad-project-name / same |
same | as the head |
Logs: mut-NONE-probe.log (7 passed at the head), mut-X10-two.log, and base1125-probe.log. For the last, #1125's adopt-project.py (sha256 412edbda628f) was swapped in, and then restored with a sha256 check, OK.
Code read: #1125's execute already asks the rule (_refuse_a_plan_for_another_repository, _check_names, _refuse_an_unrunnable_plan) before _checked_plan_values and _repository_urls. So X10 reverts an order older than this PR, and every pair it splits already agreed on #1125's head.
Gates at 791106c2b (rv1210b-review, dirty=0)
| Gate | git | Files | Result | Log |
|---|---|---|---|---|
| GA247 | 2.47.1 | the four adopt files | 140 passed, 238 s | ga247.log |
| GA243b | 2.43.0 | the four adopt files (rv1210c's GA243 re-run with filter-repo 2.47.0) | 140 passed, 287 s | ga243b.log |
| GBH243 | 2.43.0 | test_shape_doctor.py, test_scaffold_leg_path_collisions.py and the hygiene five |
864 passed, 5 skipped, 777 s | gbh243.log |
| GBH247 | 2.47.1 | the same | 864 passed, 5 skipped, 770 s | gbh247.log |
| PGH243 | 2.43.0 | test_path_guard_hygiene.py at the scratch merge 8f2fafb5b (tree 6440cb014), in my own detached worktree rv1210d-pgh |
121 passed | pgh243.log |
| PGH247 | 2.47.1 | the same | 121 passed | pgh247.log |
| GM243 | 2.43.0 | the four adopt files and test_path_guard_hygiene.py on tree bdc436e32, merge-tree(main 53bd31718, 791106c2b). No commit was made: the index and work tree were read to that tree. |
261 passed | gm243.log |
| GM243b | 2.43.0 | the same, on tree 7c1682209, merge-tree(main 5926cccab, 791106c2b) |
261 passed, 421 s | gm243b.log |
- The four adopt files are
test_adopt_plan_against_its_source.py,test_adopt_relative_submodule_url.py,test_adopt_split_branch.pyandtest_adopt_plan_value_order.py. - The hygiene five that exist on the branch are
test_repo_hygiene.py,test_agents_md_codes.py,test_cli_reference.py,test_pinned_copy_text.pyandtest_no_auto_maintenance.py.test_path_guard_hygiene.pyis not on the branch. - The 5 skips are the environment's, not the PR's:
- 3 at
test_shape_doctor.py:10510: the PowerShell line, which runs only on a Windows host; - 2 at
test_no_auto_maintenance.py:222: the session began withGIT_CONFIG_GLOBALset, andrun10.shsets it.
- 3 at
- The GM gates matter because main has changed
adopt-project.pysince the branch's merge-base6da757cbe, at53bd31718and again at5926cccab(planwithGIT_NO_LAZY_FETCH=1).
Merge-trees (git 2.43.0, merge-tree --write-tree, main fetched fresh)
- Main
53bd31718: the head is clean (treebdc436e32), and adopt-project.py:checkandexecuteask a plan'sorg:andid:right after its three repository names, so a bad one beside an unanswered entry, a moved source, a refused name or another repository is that one refusal in both, and apins:entry that is no pin is said as_read_assays it (#1020) #1125 is clean (treeb116a0a74). - Main
5926cccab: the head is clean (tree7c1682209), and adopt-project.py:checkandexecuteask a plan'sorg:andid:right after its three repository names, so a bad one beside an unanswered entry, a moved source, a refused name or another repository is that one refusal in both, and apins:entry that is no pin is said as_read_assays it (#1020) #1125 is clean (tree509fb8e3f). - On both mains, the diff from main-plus-adopt-project.py:
checkandexecuteask a plan'sorg:andid:right after its three repository names, so a bad one beside an unanswered entry, a moved source, a refused name or another repository is that one refusal in both, and apins:entry that is no pin is said as_read_assays it (#1020) #1125 to main-plus-head is 3 files, 405 insertions and 57 deletions. Hunk offsets aside, its changed lines are identical to9549006fe..791106c2b. So after adopt-project.py:checkandexecuteask a plan'sorg:andid:right after its three repository names, so a bad one beside an unanswered entry, a moved source, a refused name or another repository is that one refusal in both, and apins:entry that is no pin is said as_read_assays it (#1020) #1125's squash, this PR adds exactly its own change. - adopt-project.py:
checkandexecuteask a plan'sorg:andid:right after its three repository names, so a bad one beside an unanswered entry, a moved source, a refused name or another repository is that one refusal in both, and apins:entry that is no pin is said as_read_assays it (#1020) #11259549006feplus the head: clean, and the result is the head's own tree,4585925b8.
Findings
NIT-a (rv1210b's, CONFIRMED): the PR body is stale for this round
The live body (updatedAt 2026-10-24T14:46:58Z) still says:
- 11 rows in the new file and 46 rows in the two files;
- "13 failed, 33 passed" on adopt-project.py:
checkandexecuteask a plan'sorg:andid:right after its three repository names, so a bad one beside an unanswered entry, a moved source, a refused name or another repository is that one refusal in both, and apins:entry that is no pin is said as_read_assays it (#1020) #1125; - mutants M1-M8 only;
- the gate on a scratch merge of
90eca4144; - the follow-ups with no adopt-project.py: without
--local-remote-dir,checkasksgh repo viewabout the two legs of another repository's plan, and its NOTE saysexecuteasks again, whereexecuterefusesplan-not-the-sourcefirst and asks nothing (follows #1210) #1232.
Measured now: 18 rows, 53 rows, and 17 failed / 36 passed. X1, X4 and X8 are killed by 3, 1 and 4 rows.
Fix: refresh those sections, add X1, X4 and X8 to the mutant table, and name #1232 and the comment on #1197.
NIT-b (rv1210b's, CONFIRMED by measurement): the comment in holding() is wrong
The comment says the branch adopt "comes after plan, which would find it taken itself". Measured: with git branch adopt made BEFORE plan, plan exits 0 (conftest.planned asserts it) and writes adopt_branch: adopt/three-repo-shape. check then exits 1 with plan-split-branch-taken. It does the same on #1125's adopt-project.py. test_adopt_split_branch.py:340-342 makes adopt before plan_for in the same way.
The rows do not depend on this.
Fix: reword the comment. plan does not look for the branch; check finds it.
NIT-c (rv1210b's, CONFIRMED): no Lane trailer
Commit 791106c2b has no Lane: openreposhape-4 (openRepoShape-4) trailer; its only trailer is Co-Authored-By. 90eca4144 does have one. The subject is 314 characters long. The squash replaces both.
NIT-d (new, rv1210b's X10): no row pins the rule ahead of _what_cannot_be_held in execute
- The PR's module docstring says
executeasks the rule "before every refusalchecklists after it".checklists_held_findingsafter the rule and the entries, so that claim covers them. - X10 survives all 491 ids measured. Under X10, A, N, S or U beside a root no disk holds is refused
adopt-bad-project-namebyexecute, whilechecknames the rule's problem first. An unsafetracking_branch:beside it is split the same way. - This is a NIT, not a SHOULD. The order predates this PR, every such pair agreed on adopt-project.py:
checkandexecuteask a plan'sorg:andid:right after its three repository names, so a bad one beside an unanswered entry, a moved source, a refused name or another repository is that one refusal in both, and apins:entry that is no pin is said as_read_assays it (#1020) #1125's head too, and the PR neither fixes nor breaks any of them.
Fix (optional, here or in a follow-up):
- Add a third
ORDINARYentry:adopt-bad-project-name, with the three NUL edits and no holds. - Add
another-and-device,moved-and-deviceandunanswered-and-devicetoWITH_AN_ORDINARY_FINDING. - At the head,
checklistsadopt-bad-project-nameafter the rule's code in all three, andlisted()is the one rule code. So the existing asserts fit, and under X10 all three would fail onexecute's first code. name-and-devicedoes not fit that test, becausechecklists no held finding under a refused name.- This is measured as the probe's asserts, not as rows of that file.
Not done
- The open-PR merge-tree sweep was not repeated. rv1210b's 26 were clean at
50869f384. Only main and adopt-project.py:checkandexecuteask a plan'sorg:andid:right after its three repository names, so a bad one beside an unanswered entry, a moved source, a refused name or another repository is that one refusal in both, and apins:entry that is no pin is said as_read_assays it (#1020) #1125 were re-measured, at53bd31718and5926cccab. - X10 did not run over the whole suite, which the rules forbid: 491 ids in 11 files.
State
rv1210b-reviewandrv1210b-mutare clean at791106c2b, andadopt-project.pythere is4c8c1c9e.... The probe was moved out ofrv1210b-mut/tests/.- My worktree
/workspace/projects/openRepoShape-worktrees/rv1210d-pghis clean at8f2fafb5b. It can be removed; I ran no rm. - Nothing was committed, pushed or posted. No process of mine is still running.
- I followed the coordinator's note: no
${(f)...}or$(<file)in any call or script (a grep oftools/*.shfinds none). - STATE:
/workspace/projects/.lane-worktrees/openRepoShape-5/work/rv1210d/STATE.md. This text, ASCII-checked:/workspace/projects/.lane-worktrees/openRepoShape-5/work/rv1210d/review-final.txt. Tools:.../rv1210d/tools/(gate.sh,mut-run.sh,mutate.py,base-run.sh,x10-wide.sh,gate-chain.sh,merged-gate.sh,merged-gate-b.sh).
Lane: openreposhape-5 (openRepoShape-5)



Stacked on #1125. This PR's base is
fix/1020-bad-id-first-problem(#1125's branch, head9549006f). It lands after #1125 and stays a draft until then. #1125 makescheck's first findingexecute's first refusal for a badid:ororg:. This PR extends that rule to the four pairs #1113 measured, which have neither.Fix round. The first gate failed six rows of
tests/test_adopt_relative_submodule_url.pythat pinnedcheck's old order on a plan hand-edited into another organisation, the relative url beforeplan-not-the-source, a planexecutealready refusedplan-not-the-sourcefirst on #1125's head; they now expectplan-not-the-sourcefirst, and a new row asserts that both commands name it first.The rule and the order
One generator,
_against_its_source, asks a plan's four problems against its source in one order, and both commands read it.executerefuses its first problem.checklists each one, in that order and in its own words, before the split branch and the entries.The order is the one a person mends them in:
plan-not-the-source);naming-unclassified, ornaming-role-mismatch);plan(plan-stale);plan-unresolved).Why this order:
planagain, as its remediation says, and a fresh plan takes back every answer given before. An answer given first would be lost.planunder the source's own name, or from the source it is about. Andexecutemust ask the naming policy nothing about the plan's names, and no remote anything, before it refuses it (adopt-project.pyplannever compares--project(or--org) with the repository it adopts in place, so a source namedbilling-serviceis refused with a remediation that steers to--project BillingService, which plans and checksplan okagainstacme/billing-service, andexecutethen acts onacme/BillingService: a rawgit ls-remotefailure when no such repository exists, and, when a different repository of that name does, both legs, the split branch, the pull request and the topic on that repository #499)._against_its_sourceis a generator, soexecuteasks nothing after the first problem, and that still holds.plangiven another--project, or after the source is renamed on GitHub. That run reads the source as it is now, so it mends the moved source too.planrun again as it was, for the moved source, would leave the name to mend.execute's order for the plan's names (adopt-project.pyplannever compares--project(or--org) with the repository it adopts in place, so a source namedbilling-serviceis refused with a remediation that steers to--project BillingService, which plans and checksplan okagainstacme/billing-service, andexecutethen acts onacme/BillingService: a rawgit ls-remotefailure when no such repository exists, and, when a different repository of that name does, both legs, the split branch, the pull request and the topic on that repository #499, adopt-project.pycheck: a plan in a name the policy refuses gets no rename-first remedy, and a source that names no repository is never compared with the plan's recordedsource.repository#716) andcheck's order of a moved source before its entries. Only the pairs that disagreed change.Before and after
Measured on #1125's head
9549006fand on this head, with a stubgh(exit 97),GH_TOKEN=bogus, an emptyGH_CONFIG_DIR,GIT_SSH_COMMAND=false, a proxy that goes nowhere, an empty global git config and--local-remote-dir. No run made a leg.checkexits 1 andexecuteexits 2 in every row.checkbeforeexecutebeforeplan-staleplan-unresolvedplan-staleplan-unresolvedplan-not-the-sourceplan-not-the-sourceplan-staleplan-not-the-sourceplan-not-the-sourceplan-unresolvednaming-unclassifiednaming-unclassifiedplan-stalenaming-unclassifiednaming-unclassifiedplan-staleplan-not-the-sourceplan-not-the-sourceplan-not-the-sourceplan-not-the-sourceplan-not-the-source(unchanged)The fifth row is not in #1113's table, but the two commands disagreed there too, and the same rule decides it.
The same probe, comparing each command's output before and after, with paths and hashes masked:
check's stderr in each pair row holds the same lines as before, reordered: the same findings and the same remediation lines.execute's stderr changes in one row only, a moved source beside an unanswered entry:REFUSED plan-stale, in its own words, where it saidREFUSED plan-unresolved.No refusal's code or wording changes, and no code is added. Re-run on this head after the fix round, the probe gives the same: the 24 plans (these 11, and the 13 of the next section) each get one first problem from both commands, no run makes a leg, and the comparison with #1125's head is the one above, line for line.
#1113's suspected case: a
--work-dirthat cannot be writtenMeasured on #1125's head and on this head, the two commands agree in every row, and nothing here changes it:
id:ororg:beside a--work-dirthis user cannot write in (chmod 500):plan-bad-idorunsafe-valueto both. adopt-project.py:checkandexecuteask a plan'sorg:andid:right after its three repository names, so a bad one beside an unanswered entry, a moved source, a refused name or another repository is that one refusal in both, and apins:entry that is no pin is said as_read_assays it (#1020) #1125's review round 1 (RV1125's SHOULD-1) made both commands ask the two before_work_root, which settled this case.--work-dir:adopt-work-dir-unwritablefirst to both.What changed, in
adopt-project.py_against_its_sourceand_Unmended(new)._against_its_sourceis the rule. It asks:_refuse_a_plan_for_another_repository;_check_names, given the source only where the plan names no other repository, ascheckgave it (adopt-project.pycheck: a plan in a name the policy refuses gets no rename-first remedy, and a source that names no repository is never compared with the plan's recordedsource.repository#716);_refuse_an_unrunnable_plan.It yields each problem as an
_Unmended: theRefusalexecuterefuses it with, andcheck's FINDING lines and remediation for it, incheck's words as they were.checklists an unanswered entry with the entries, each by its path (_leg_findings), after the rule's problems.cmd_execute. It refuses the first problem,next(_against_its_source(...)). Before, it called_refuse_a_plan_for_another_repository,_check_namesand_refuse_an_unrunnable_planin turn.cmd_check. It lists the rule's findings right after the Triad advisory, where it listed a moved source. Another repository and the names are no longer listed after the entries. Three things are still asked where they were, and only where no name was refused:init, adopt-project.pycheckand the--orgof all four tools:initsets thexf-project-<id>topic last and asks no GitHub limit,checkpasses a nameexecuterefuses, and--org my_org.xpasses every plan #863);execute: each leg repository is asked about only as it is made, so a code leg whose name is taken leaves the spec leg created, on GitHub as under--local-remote-dir, and a--local-remote-dirthat is a file or cannot be written ends in a traceback afterchecksaidplan ok#798).The remediation lines it prints after its findings follow the findings' order, so the rule's come first.
_refuse_an_unrunnable_plan. It asks a moved source before an unanswered entry. Its docstring says why._refuse_a_plan_for_another_repository. Unchanged. The rule calls it.Tests
tests/test_adopt_plan_against_its_source.py(new, ASCII only), 11 rows. Each runscheckand thenexecute --yeson one hand-edited plan, both with the same--local-remote-dir, and asserts that no leg is made.checkexits 1 andexecuteexits 2;checklists every problem the plan holds, in the rule's order.checkandexecuteask a plan'sorg:andid:right after its three repository names, so a bad one beside an unanswered entry, a moved source, a refused name or another repository is that one refusal in both, and apins:entry that is no pin is said as_read_assays it (#1020) #1125's head9549006f:('plan-stale', 'plan-unresolved') != ('plan-stale', 'plan-stale');another-and-nameguard row and the 4 controls pass.tests/test_adopt_relative_submodule_url.py, 6 rows changed and 1 new (the fix round):test_check_keeps_a_relative_url_its_github_leg_resolves_alikefor[https],[scp],[scp-other-case],[ssh-port]and[ssh-github-443], andtest_a_credential_in_the_source_remote_is_never_printed.org:tootherorg, so the plan namesotherorg/Thingand the source istestorg/Thing: a plan for another repository.checkto list the relative url first andplan-not-the-sourceafter it,check's old order. They now expectplan-not-the-sourcefirst. Their other assertions are unchanged; the credential is still printed nowhere.test_check_names_another_repository_first_as_execute_refuses_it, runs one such plan. It asserts thatcheck, with and without--local-remote-dir, listsplan-not-the-sourceand then the url, thatexecuterefusesplan-not-the-source, and that no leg is made.checkandexecuteask a plan'sorg:andid:right after its three repository names, so a bad one beside an unanswered entry, a moved source, a refused name or another repository is that one refusal in both, and apins:entry that is no pin is said as_read_assays it (#1020) #1125's head for the six source remotes:executealready refused each such planplan-not-the-source, and its output is the same on both heads. Onlycheck's order changed. No spelling of the source'soriginreads as another repository: withorg:left as the source's,checkpasses on both heads, as the rows assert.Both files together, 46 rows:
checkandexecuteask a plan'sorg:andid:right after its three repository names, so a bad one beside an unanswered entry, a moved source, a refused name or another repository is that one refusal in both, and apins:entry that is no pin is said as_read_assays it (#1020) #1125's head: 13 failed, 33 passed. The 13 are the 6 pair rows above and the 7 relative-url rows, each on the order:plan-not-the-sourcewhere the url was expected first, or the url first in the new row.Mutants
Each was applied to
adopt-project.py, run against both files (46 rows), and restored, with its sha256 checked (4c8c1c9e, the same after each one). Unmutated, the 46 pass. M1-M6 are the first round's; M7 and M8 are the fix round's.execute:_refuse_an_unrunnable_planasks an unanswered entry before a moved source (pair 1)moved-and-unanswered,all-fourcheck: lists another repository after the entries (pair 2)another-and-unanswered,another-and-moved,another-and-name,all-four, and the 7 relative-url rowscheck: lists a moved source before another repository (pair 3)another-and-moved,name-and-moved,all-fourcheck: lists a refused name after the entries (pair 4)name-and-unanswered,name-and-moved,all-fourcheck: lists a moved source before a refused name (pair 5)name-and-moved,all-fouranother-and-name,all-fourexecute: asks_refuse_what_check_findsbefore the ruleanother-and-name, theplan-stalecontrol, and the new relative-url rowcheck: lists all the rule's problems after the entriesanother-and-name, theplan-stalecontrol, and the 7 relative-url rowsGate
Run on the scratch merge
743ebe176:origin/main961640c68plus this head90eca4144(treed71d6f00a, never pushed). It ran in the landing gate's own environment (a stubgh,GH_TOKEN=bogus, an empty global git config,EMAILset, theLANES_*andGIT_SSH_COMMANDvariables unset), one shard at a time, each started with at least 19G free. The ids were collected from that tree, 4405 + 448 = 4853, and each is in exactly one shard. The file list is the 46 test files W1113's gate named and four more, added in the fix round (50 in all):test_adopt_c_locale_bytes.pyandtest_adopt_submodule_url_answers.py(they import helpers and the module-scopedadopterfixture from the changed test file, so they sit in its shard, r03),test_reader_refusals_named.py(it hand-edits a plan'sorg:and checkscheck's exact findings) andtest_github_name_limits.py. Every shard exited 0.test_adopt_plan.py::test_plan_on_medxehr_matches_the_rulings)test_family_init_values.py)test_shape_doctor.py)test_shape_doctor.py)Main has moved since this gate:
origin/mainisac5f28d2a(#1188, #1130 and #1098 landed). They changescaffold-project.py,scripts/family.pyand four test files. None of them is a file this PR changes, a moduleadopt-project.pyimports, or one of the 50 gated test files, so the gate was not run again on that tip. The merge-trees below were.Merge-trees
git merge-tree --write-tree, run again on 2026-10-23 at 15:31Z against the currentorigin/main, every one clean:origin/mainac5f28d2a: clean, tree1a65ab9bd.checkandexecuteask a plan'sorg:andid:right after its three repository names, so a bad one beside an unanswered entry, a moved source, a refused name or another repository is that one refusal in both, and apins:entry that is no pin is said as_read_assays it (#1020) #11259549006fe: clean. This branch is on it.--planpath incheckandexecute, and the--local-remote-dirpath inexecute'sbare, leg andsplitlines, are named with_shown, as #1154 names--outand--work-dir, so a bidi control, an escape, a line feed or a byte that is not UTF-8 in either no longer reaches the terminal as it was typed (#1152) #11775208e874e(fix/1152-plan-and-remote-paths-spelled, which edits a line of_refuse_a_plan_for_another_repository): clean, with this head and three-way on the scratch merge.adopt-project.pyor either test file, 25 of them, adopt-project.py:checkandexecuteask a plan'sorg:andid:right after its three repository names, so a bad one beside an unanswered entry, a moved source, a refused name or another repository is that one refusal in both, and apins:entry that is no pin is said as_read_assays it (#1020) #1125 and adopt-project.py: the--planpath incheckandexecute, and the--local-remote-dirpath inexecute'sbare, leg andsplitlines, are named with_shown, as #1154 names--outand--work-dir, so a bidi control, an escape, a line feed or a byte that is not UTF-8 in either no longer reaches the terminal as it was typed (#1152) #1177 among them: clean, both with this head and three-way on the scratch merge ofac5f28d2aand this head,98afb128a(git merge-tree --write-tree <that merge> <its head>). Since the first run, adopt-project.py, shape_materialize.py and the assembly-root template: a keptGNUmakefile/is a directory GNU make stops at,python3 scripts/bootstrap.pynamespythonon Windows, and the template says what to run wheremakereads another file #1207 is new, and adopt-project.py: check asks the split's own clone of the source in the work directory, so a clone this git will not make is adopt-git-unasked before any leg, and _resolved and _answers make their repositories as check's other throwaways do (#1083) #1119, adopt-project.py:checknames the case twins the plan keeps in one repository asplan-case-twinson every disk, a one-blob set as a NOTE in a leg and, in the root, two paths in one folder, andexecuterefuses the finding before any leg (#1161) #1189 and adopt-project.py:planruns withGIT_NO_LAZY_FETCH=1, refuses a partial-clone--sourcethat lacks objects assource-partial-clone, and names a partial--pin-sourceit could read only by fetching and the fetch that makes it whole (#1162) #1190 have moved; all four are clean. No open PR touches either test file. The list is in a code block, so that it does not mention each PR:Left for a follow-up (outside #1113)
checkandexecutestill name different first problems for a plan with an unanswered entry beside an uncovered path,plan-uncoveredandplan-unresolved, a pair #1113's four do not cover (follows #1113) #1197.checklistsplan-uncoveredfirst andexecuterefusesplan-unresolvedfirst. Measured the same on adopt-project.py:checkandexecuteask a plan'sorg:andid:right after its three repository names, so a bad one beside an unanswered entry, a moved source, a refused name or another repository is that one refusal in both, and apins:entry that is no pin is said as_read_assays it (#1020) #1125's head and on this head.executerefuses an unanswered entry before_refuse_what_check_finds, whilechecklists it in_leg_findings, after the paths and the coverage. Other entry findings ahead of it in_entry_findingsare likely the same (plan-unsafe-pathamong them), and so is the taken split branch, but those were not measured.Closes #1113
🤖 Generated with Claude Code
Lane: openreposhape-4 (openRepoShape-4)
Summary by Sourcery
Make
checkandexecuteuse one consistent, remediation-oriented order for reporting and refusing plan problems.Bug Fixes:
checkandexecuteso they identify the same first plan problem when multiple source-related issues are present.Enhancements:
Tests:
execute.