Repository navigation
adopt-project.py: plan and check take an empty flag or a bare plan value as typed, and the id refusals name only what the person typed (#902) - #1016
Conversation
Uncommitted work of W902 (lane openRepoShape-3), checkpointed after the reboot of 2026-10-19. Not for review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…'s flag refusals #963 moved the refusals of a passed flag's own value into `_checked_flag_inputs`, before the source is read. #902's changes are resolved onto it: there `--org ''` and `--elected-on ''` are values passed and refused by name, and a passed `--id` that is no id is refused only where the naming policy holds the names `--project` makes (`_names_held`); beside a name it refuses, `_refuse_bad_id` asks `_check_names` first where the source is known, before the clone of an `org/repo` source. #963's control test of `--org ''` reaching the clone now expects the refusal by name. Also AGENTS.md's adoption step says the `--org` default (L1-49), within its line cap, and the tests of the rename-first remedy expect the rule said before it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ords Checkpoint of W902 (lane openRepoShape-3) at the 2026-10-19 account swap. Not for review. - test_adopt_manifest_values.py: `_refuse_bad_id` now takes the naming policy and the declared products. - test_adopt_plan_yaml_quoting.py: the test that pinned L1-K3's residual (`check` passing `tracking_branch: 007` and `id: 007`) now expects the refusal by what was read; the misread rule's test leaves out the id, which `plan-bad-id` refuses first. - test_adopt_values_as_typed.py: #294's date finding says the date chooses the reference only where the plan sets none. Neither changed file has been run since the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThe PR tightens adopt-project input handling so values are validated as explicitly typed, improves identifier and naming diagnostics with consistent remediation and precedence, and verifies that these refusals occur before source access or adoption side effects. Sequence diagram for adopt-project early input refusalssequenceDiagram
participant User
participant Plan as adopt-project.py plan
participant Naming as NamingPolicy
participant Source as Source access
participant Git as Git or GitHub
User->>Plan: invoke plan with flags
Plan->>Naming: load NAMING_POLICY
Plan->>Naming: _names_held(naming, project, pins)
alt naming refusal
Naming-->>Plan: naming-unclassified or naming-role-mismatch
Plan-->>User: refusal with name rule and remediation
else names held
Plan->>Plan: _checked_flag_inputs(args, naming)
alt empty or invalid flag
Plan-->>User: refusal before source access
else invalid passed id
Plan-->>User: adopt-bad-id with PASSED_ID_REMEDIATION
else flags valid
Plan->>Source: read or resolve --source
Source->>Git: clone or inspect source when needed
Git-->>Source: source metadata
end
end
Flow diagram for typed plan value validationflowchart TD
A[Read plan value] --> B{Number or boolean?}
B -->|Yes| C[Refuse under the value's key code]
C --> D[Explain what was read]
D --> E[Quote the value or re-run plan]
B -->|No| F[Apply the key's normal validation]
F --> G{Valid?}
G -->|No| H[Report key-specific refusal]
G -->|Yes| I[Continue check or execute]
File-Level Changes
Assessment against linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-3, 2026-10-19T22:53:59Z) RV1016 (lane 3's independent review of #1016 at 8e04a6d): NOT READY, one MUST. MUST-1 adopt-project.py:8742 and :12994-12996. A hand-edited SHOULD-1 adopt-project.py:15442: the role-mismatch rule in Holds: each of #902's repros at main and head gives what the body quotes. plan with YAML-like values (007, yes, no, ~, null, true, 1.50, 0x1F, -007, #, :, quotes, a leading -, non-ASCII) gives the same plan and check on main and head. Tests (head, stub gh, one at a time): values_as_typed 38 passed; manifest_values 79 passed; plan_yaml_quoting 517 passed; adopt_plan 66 passed, 1 skipped; flag_refusals_first 89 passed; scaffold_leg_path_collisions 104 passed; readme_windows_quoting_sentence 44 passed (no shell_quoted added); four docs guards 176 passed; no_auto_maintenance 60 passed. Predicted main+head+#1015: values_as_typed + plan_value_order 56 passed. Skipped test_shape_doctor.py (the doctor uses no function this PR changes) and the rest of F902b's list. Mutants: M8 survives the empty-date test and is killed by flag_refusals_first[elected-on-empty-org-repo]; M13 killed (id 007 test); M16 killed (org-repo uncloned test); mine R1 killed (refused_name_speaks_before_a_passed_id[another-repository]); mine R2 survives (SHOULD-1). Merges: clean with origin/main 2d6f928 and 3969409. The predicted squash's patch-id equals the branch's diff against 3864b18 (9 files, +835/-124). Clean with #1015, #1012, #1011, #1010, #1002, #986, #983, #976, #975, #973 and #958. #954 conflicts in templates/family-root/scripts/bootstrap.py, and main alone conflicts with #954 the same way. Lane: openreposhape-3 (openRepoShape-3) |
…r or a boolean, `tracking_branch: -007` among them, says what `check` says, `plan-bad-tracking-branch` or `plan-bad-adopt-branch` and what was read, and not `unsafe-value` of the `-7` nobody typed (#902) RV1016's MUST-1. `_checked_plan_values` asked `checked_value` before the number-or-boolean question that `check` asks first (`_branch_name_refusal`), so a branch hand-edited to a negative number was refused in other words, under another code, by `execute` than by `check`. It asks each branch in `BRANCH_KEYS`' order, the number-or-boolean question first (`_checked_branch_value`). Tests: `-007` for `tracking_branch` and `adopt_branch` in `test_adopt_values_as_typed.py`, and RV1016's SHOULD-1, a source `acme/Tools-Install` with `--project ToolsInstall`, in `test_adopt_project_is_the_source.py`, which pins the role-mismatch rule that `_own_name_remediation` gives. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-3, 2026-10-19T23:07:53Z) RV1016 delta re-check of #1016, fix round 1, head 9261ff2: READY. MUST-1 holds. With a local source, local remotes and the stub gh:
Mutants at 9261ff2:
Tests at 9261ff2, one at a time, under the stub gh: test_adopt_values_as_typed.py 40 passed in 28.70s; test_adopt_branch_names.py 29 passed in 23.99s; test_adopt_project_is_the_source.py 23 passed in 18.81s; test_no_auto_maintenance.py 60 passed in 8.29s. Merges: clean with origin/main dbf12e0 (tree a394c9cd, the same 9 files) and with #1015 2b04aec (tree 82fc90f2). Body: true, with one NIT. The L2-90 bullet says that in place the name refusal "speaks in the words it has when no 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 20 hours by commenting |
…lict, so #1025 is tested against main as it stands, with #983's change to validate-family.py and #1026's fix of main's two red rows (#936) Main took twelve squashes since e076e3e: #1026, #958, #986, #1002, #1016, #1011, #1021, #1022, #973, #975, #1015 and #983. Only #983 touches validate-family.py, in `_git_runs` and the member checks, apart from this branch's hunks; it touches none of this branch's test files. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eviews and the Known limits continuation, with lanes 2, 4 and 5's items: eleven sites left to #1110, the doctor's 000 and 444 mount rows, the #1082 upgrade warning and the #891 and #1103 limits said as main's code does, #1112 and lane 2's two doctor limits named, and the shape copies measured (#921) The fix round on RV1155a, RV1155b, RV1155c and RV1155d, the continuations RV1155d2 and RV1155a2, and the comments lanes openRepoShape-4 and openRepoShape-5 left on #1155: - RV1155c's two MUSTs: `spec/` at 000 is `cannot be listed` and at 444 `spec/.git: cannot be read`; `scripts/` at 444 is `INVALID (manifest, pins)` on 3.14 as on 3.12. - Every SHOULD (a 3, b 2, c 3, d 13) and every NIT but RV1155a's NIT 7, RV1155a2's eight among them. - Where lane 5 and a reviewer word one line, one text covers both: #891 (d SHOULD 6, lane 5 SHOULD 7), #1103 (d SHOULD 7, lane 5 SHOULD 4) and #1090 (c's first NIT, lane 5 NIT 14). - KNOWN_OPEN in tests/test_path_guard_hygiene.py holds eleven sites, so both lines say eleven. - #1112 is a known limit; lane 2's two doctor limits (#975) and its test-only gaps (#975, #1050) are listed, and the "INVALID, not DRIFTED" sentence holds for a mount this user cannot list or read. - The shape-copy measurement fills its placeholder; the "Before tagging" pointer is gone. - The pipx claim carries RV1155d's qualifier everywhere it is made. - v1.0.0's tab-in-a-pin line is closed (#1015); its bare number or boolean in a plan's text field is narrowed (#1016). - `update-shape.py check` is named with `--at <release-commit>`, and the measurement says main `038dafb` was the upstream. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Closes #902.
#902 groups seven lines of #457: L1-K3 (with its duplicate L1-15), L2-86 to L2-90, and L1-49. Each one was probed on main
c7a3ff16, which already holds #963, and onc4dd19dcbefore it. None was fixed on main, and each one is fixed here. Main is3864b18cas merged in here (it has moved on since, to2d6f928b: see Merges). Of the landings sincec7a3ff16(#984, #966, #971, #972, #957, #960, #945, #946, #951, #955, #995, #970 and #956), only #995 and #970 touchadopt-project.py, and only #970 touchesdocs/cli.md; none touchesAGENTS.md. #995 changes how aneutral_product_pins:entry is spelled. #970 makes a--sourceor a recorded source this user cannot reachsource-unreadable. Neither is one of the seven lines.This branch has seven commits, meant to be squashed: two WIP commits,
d046905d(the change) and23ce1201(three test files), four normal merges of main, and fix round 1's commit,9261ff2f(see "Fix round 1"). The merges:37c43debmergesc7a3ff16. It resolves the conflict with adopt-project.py:planasks the leg-path, pin, date and value refusals of its flags, and anorg/reposource's own name and derived id, before the work directory, git's reads of the source andgh repo clone, and says why it cannot clone into a--work-dirnobody can write in (#903) #963's_checked_flag_inputs, keeping both: see "How it fits adopt-project.py:planasks the leg-path, pin, date and value refusals of its flags, and anorg/reposource's own name and derived id, before the work directory, git's reads of the source andgh repo clone, and says why it cannot clone into a--work-dirnobody can write in (#903) #963" below.8016f0b3merges6f27432c. It was clean.abf87558merges04923f10(scaffold-project.py, adopt-project.py and shape_materialize.py: one function both tools call spells aneutral_product_pins:entry, so a--pinof0x1F,1_000,1e3,3dGlassor-xis double-quoted from the scaffold as from adopt (#924) #995). It was clean. scaffold-project.py, adopt-project.py and shape_materialize.py: one function both tools call spells aneutral_product_pins:entry, so a--pinof0x1F,1_000,1e3,3dGlassor-xis double-quoted from the scaffold as from adopt (#924) #995 moves_neutral_pin_entryintoscripts/shape_materialize.py, and_PLAIN_RE's pattern with it, unchanged. This branch touches neither, so both changes stand as each made them.8e04a6d1merges3864b18c(adopt-project.py: a--sourceor recorded source this user cannot reach issource-unreadable, never read as absent or sent togh, and an unreadable leg or link place is named: no 3.12 traceback, no 3.14plan-source-gone(part of #430) #970 and validate-family.py: a member's checkout, a shape copy, the shape pin orfamily.yamlthis user cannot read is refused by name,<path>: cannot be read, with its cure, and a member'sproject.yamlismember-manifest-unreadable, so no Python 3.12 traceback and no 3.14member-uninitialized,pinned but absent,does not existor update of a checkout that is there (#430) #956). It was clean, and its tree,bb4d4989, is the onegit merge-treepredicted. adopt-project.py: a--sourceor recorded source this user cannot reach issource-unreadable, never read as absent or sent togh, and an unreadable leg or link place is named: no 3.12 traceback, no 3.14plan-source-gone(part of #430) #970's hunks inadopt-project.pyare new helpers (_is_a,_is_there,source-unreadable) and their calls where the source is read; adopt-project.py: a--sourceor recorded source this user cannot reach issource-unreadable, never read as absent or sent togh, and an unreadable leg or link place is named: no 3.12 traceback, no 3.14plan-source-gone(part of #430) #970 also adds thesource-unreadablesentences todocs/cli.md, which this branch does not change; validate-family.py: a member's checkout, a shape copy, the shape pin orfamily.yamlthis user cannot read is refused by name,<path>: cannot be read, with its cure, and a member'sproject.yamlismember-manifest-unreadable, so no Python 3.12 traceback and no 3.14member-uninitialized,pinned but absent,does not existor update of a checkout that is there (#430) #956 isvalidate-family.pyalone. No hunk of this branch's overlaps theirs, so both stand as each made them. adopt-project.py: a--sourceor recorded source this user cannot reach issource-unreadable, never read as absent or sent togh, and an unreadable leg or link place is named: no 3.12 traceback, no 3.14plan-source-gone(part of #430) #970's newtests/test_adopt_unsearchable_source.pyis run below.Every probe used a stub
ghfirst on PATH (it exits 97),GH_TOKEN=bogus, an emptyGH_CONFIG_DIR,GIT_SSH_COMMAND=false, local sources, andexecute --local-remote-dir.What changed, line by line
L1-K3 and L1-15: a bare plan value that the plan's reader reads as a number or a boolean
On main, a hand-edited plan had these results:
id: 007gavecheckexit 0 (plan ok), thenexecuteexit 0 (adoption verified), andproject.yamlsaidid: "7".tracking_branch: 007,trueand1.50, with the source holding a branch7,Trueor1.5, was adopted onto that branch.adopt_branch: 007andtruepushed the split to7and toTrue.Now:
id:read as a number or a boolean isREFUSED plan-bad-id: <plan>:id:was read out of the plan as the number 7, not as text, soexecutewould take it as the text7``.checkexits 2 and `execute` refuses it before any leg exists.tracking_branch:oradopt_branch:read that way is a finding under the key's own code,plan-bad-tracking-branchorplan-bad-adopt-branch, in the same words.checkexits 1.executerefuses it itself, under the same code and in the same words, before any leg exists: it asks the number-or-boolean question first, ascheckdoes, so atracking_branch: -007isplan-bad-tracking-branchin both, and notunsafe-valueof the-7nobody typed (fix round 1). Git is not asked about it."007","true"), or re-runplan, which writes every value so that it reads back as typed.L2-86:
--org ''On main,
flags_givencounted--org ''as passed, but_planned_org,org_givenandadopt-not-the-sourceread it as not given. Three things followed:--org is '~x', a value nobody typed.adopt-not-the-sourcesaid "(--orgnot given)".origin, the result wasadopt-no-org.Now
--orgis read byis not Noneeverywhere: in_checked_flag_inputs,_planned_org,org_givenand_refuse_a_clone_of_another_repository.--org ''is nowREFUSED unsafe-value: --org is '', which is not a value this tool will put on agitorghcommand line. It is refused with the other flags, before the source is read or cloned, as--tracking-branch ''is.L2-87:
--elected-on ''and--reference ''On main both exited 0. The plan recorded today's date, or the default reference.
Now:
--elected-on ''is refused with the flags:REFUSED election-date-malformed: --elected-on '' is not a YYYY-MM-DD date ....--reference ''is refused in the validator's words, as scaffold-project.py and family.py init: a flag passed empty is refused by name, a passed --id is told what an id may hold once the name passes, and an empty folder this user cannot list is told the permission (#911, #930) #972 refuses the scaffold's:REFUSED adopt-manifest-invalid: --reference is '', which the project's own validate-manifest.py refuses: manifest-reference: reference is ''; drop the key or name the document the election followed.The validator saw the default and not
'', because_template_tablereadreference:by its truth. It now reads it byis not None. This changes two other things:reference: "", whichexecuteused to write as the default reference without a word, is now a finding ofcheck(plan-manifest-invalid, in the same words), andexecuterefuses it before any leg exists.checknever reads a plan'selected_on:when the plan has noreference:, soplan okis followed by anexecutethat creates both leg repositories and only then refuseselection-date-malformed, naming--elected-on, a flag neither command takes #294's finding for a badelected_on:says that the date chooses the reference only where the plan sets none (_elected_on_findings).L2-88:
plan-bad-idfor a plan with noid:The refusal now reads:
REFUSED plan-bad-id: <plan>: id (not set in the plan, so legs.assembly lowercased) is 'north_wind', which must match ^[a-z0-9][a-z0-9-]*$Remediation: changelegs.assemblyin the plan to one CamelCase token with no hyphen, underscore, dot or space, or re-runplanwith a --project of that form. The id is legs.assembly lowercased, and anid:would leavelegs.assembly, the assembly root's name, as it is. The GitHub topic is derived from the id.The label is
DERIVED_ID's, and the remedy is worded likeplan's derived case (L2-54).L2-89: the naming rule, in place
When the source is adopted in place,
_check_namesnow says the rule for the new name before_rename_first's way out:Remediation: --project is the source's own name, which must be one CamelCase token with no hyphen, underscore, dot or space. testorg/North_Wind cannot be adopted in place under that name: ...naming-role-mismatchgives its own rule in the same place, the one it gives a name that is not the source's.NAME_RULES,_name_remedy).checkuses the same table to decide which remedy it prints once (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).adopt-not-the-sourceandplan-not-the-sourcenow say: "The source's own name is no remedy either: 'North_Wind' matches no family in the naming policy. The name must be one CamelCase token with no hyphen, underscore, dot or space. testorg/North_Wind cannot be adopted in place under that name: ..."L2-90, and adopt's half of L2-82 (#459, U159): the name before a passed id, and what an id may hold
--idthat is no id is refused only where the naming policy holds the names--projectmakes. Beside a name the policy refuses,naming-unclassifiedornaming-role-mismatchspeaks first. Where the source is the one--projectnames, it speaks in the words it has when no--idis passed: in place, the rule and then "rename first". Where the source names another repository (origintestorg/Atlas,--project North_Wind), it isnaming-unclassified's own words, and the--projectits remedy asks for is then refused byadopt-not-the-source: the same two steps a derived id takes on main (RV1016's NIT-1).Remediation: pass an --id of lowercase letters, digits and hyphens, starting with a letter or digit.This is scaffold-project.py and family.py init: a flag passed empty is refused by name, a passed --id is told what an id may hold once the name passes, and an empty folder this user cannot list is told the permission (#911, #930) #972's sentence at4e44df40, word for word, in the order scaffold-project.py and family.py init: a flag passed empty is refused by name, a passed --id is told what an id may hold once the name passes, and an empty folder this user cannot list is told the permission (#911, #930) #972 gives it: a refused name first, then the id.id:.checkandexecutesay the same of a plan'sid:that the pattern refuses: "setid:in the plan to lowercase letters, digits and hyphens, starting with a letter or digit, or re-runplanwith an --id of that form." They used to say "an explicit lowercase --id".L1-49: AGENTS.md
AGENTS.md's adoption step now says that
--orgdefaults to the organisation of anorg/reposource or of a urlorigin, and that a pathorigin, or none, needs--org. The file is still 494 lines.To make room, the paragraph's other clauses were tightened. These were dropped:
"Step 4 is the one you must not skip" is now "never skip step 4".
Decisions the Direction left open
checkrefuses; it does not write what was typed. The plan's reader has already lost what was typed, since007and7read alike, soexecutecannot write the original text. Existing codes are reused (plan-bad-id,plan-bad-tracking-branch,plan-bad-adopt-branch), so there is no new code to document. Only a number or a boolean is refused this way. Any other kind is held to its key's rules as before._check_namesof all three names with the products--pindeclares. scaffold-project.py and family.py init: a flag passed empty is refused by name, a passed --id is told what an id may hold once the name passes, and an empty folder this user cannot list is told the permission (#911, #930) #972 limits the scaffold's early check tonaming-unclassifiedand the unambiguous forms, because a scaffold descendant's role turns on--referent-chainandgh. Adopt's naming question reads only the declared products, which--pingives before any digest is read. So the early answer is the same as the later one.planasks the leg-path, pin, date and value refusals of its flags, and anorg/reposource's own name and derived id, before the work directory, git's reads of the source andgh repo clone, and says why it cannot clone into a--work-dirnobody can write in (#903) #963 (landed as2771b380). adopt-project.py:planasks the leg-path, pin, date and value refusals of its flags, and anorg/reposource's own name and derived id, before the work directory, git's reads of the source andgh repo clone, and says why it cannot clone into a--work-dirnobody can write in (#903) #963 refuses a passed--idwith the flags, before the source is read. The naming refusal comes after the source, because its in-place remedy names the source. Here, a passed id that is no id is refused with the flags only where the names hold (_names_held). Otherwise it is left to the place where the source is known:org/reposource (_refuse_a_clone_of_another_repositorynow asks a passed id too);originof a local one.planasks the leg-path, pin, date and value refusals of its flags, and anorg/reposource's own name and derived id, before the work directory, git's reads of the source andgh repo clone, and says why it cannot clone into a--work-dirnobody can write in (#903) #963's control test changed. adopt-project.py:planasks the leg-path, pin, date and value refusals of its flags, and anorg/reposource's own name and derived id, before the work directory, git's reads of the source andgh repo clone, and says why it cannot clone into a--work-dirnobody can write in (#903) #963'stest_control_an_empty_org_is_none_given_and_reaches_the_cloneasserted L2-86's old reading. It now asserts the refusal by name, with noghcall.reference: ""in a plan is a value. This is L2-87's rule applied to the plan file: the validator says "drop the key or name the document".Out of scope
planandcheck: an empty flag or a bare plan value is taken without a word, and the id refusals name what the person did not type #902 has no section headed "Needs a ruling" or "Documented limit".elected_by:,reference:andorg:read as a number or a boolean. These are the same class as L1-K3, but adopt-project.pyplanandcheck: an empty flag or a bare plan value is taken without a word, and the id refusals name what the person did not type #902's State and L1-K3 do not name them. Measured onc4dd19dc:elected_by: 007,elected_by: true,reference: 1.50andorg: 007each givecheckexit 0,plan ok.visibility: trueis already the validator's finding, and anelected_on:read as a number is adopt-project.py:checknever reads a plan'selected_on:when the plan has noreference:, soplan okis followed by anexecutethat creates both leg repositories and only then refuseselection-date-malformed, naming--elected-on, a flag neither command takes #294's.org:is read asstron purpose for the not-the-source comparison (adopt-project.py:--organd--projectmust name the repository the source names, compared without case, soplanrefuses--project BillingServiceforacme/billing-serviceasadopt-not-the-sourceinstead of writing a plan whoseexecuteacts onacme/BillingService,checkfinds andexecuterefuses a plan edited to name another asplan-not-the-source, and a source whose own name the naming policy refuses is told to rename it first, never to retype--project(#499) #513's review).planandcheck: an empty flag or a bare plan value is taken without a word, and the id refusals name what the person did not type #902 closes.--id's help text is unchanged ("lowercase project id (default: --project lowercased)"), sodocs/cli.mdis unchanged.Tests
Every run used:
timeout 900for a file run alone, andtimeout 2400for a batch;ghfirst on PATH (it exits 97),GH_TOKEN=bogus, an emptyGH_CONFIG_DIR,GIT_SSH_COMMAND=falseandEMAIL=tests@openreposhape.invalid;/usr/bin/python3 -m pytest -q -p no:cacheprovider --basetemp=<scratch>/bt-<name> -o tmp_path_retention_policy=failed <files>, one process at a time.They ran on
8e04a6d1, the head before fix round 1, unless marked otherwise; fix round 1's runs are in its own section. The files changed here were each run alone, with-x --durations=10.New and changed files, with the other test files for the code they reach:
tests/test_adopt_values_as_typed.py(new): 38 passed in 27.08stests/test_adopt_manifest_values.py(changed): 79 passed in 88.48s (0:01:28)tests/test_adopt_plan_yaml_quoting.py(changed): 517 passed in 183.71s (0:03:03)tests/test_adopt_plan.py(changed): 66 passed, 1 skipped in 23.82stests/test_adopt_plan_flag_refusals_first.py(changed): 89 passed in 32.62stests/test_adopt_project_is_the_source.py(changed): 22 passed in 18.40stests/test_adopt_check_plan_name.py(changed): 45 passed in 51.57stests/test_adopt_origin_repository.py: 30 passed in 10.61stests/test_adopt_split_branch.py: 47 passed in 68.69s (0:01:08)tests/test_adopt_tracking_branch.py: 17 passed in 18.58stests/test_adopt_unsearchable_source.py(adopt-project.py: a--sourceor recorded source this user cannot reach issource-unreadable, never read as absent or sent togh, and an unreadable leg or link place is named: no 3.12 traceback, no 3.14plan-source-gone(part of #430) #970's, merged in): 27 passed in 8.74sTwo existing tests were changed to #902's words:
tests/test_adopt_manifest_values.py::test_the_derived_ids_words_are_the_tablescalls_refuse_bad_iddirectly. It now passes the naming policy and the declared products, which that function now takes.tests/test_adopt_plan_yaml_quoting.py::test_check_passes_a_plan_value_read_as_not_text_as_main_doespinned L1-K3's behaviour,checkpassingtracking_branch: 007andid: 007. It is nowtest_check_refuses_a_plan_value_read_as_not_text_by_what_was_read, and it expects the refusal by what was read. The misread rule's test beside it (test_the_misread_rule_names_a_plan_value_that_is_not_text_by_what_was_read) no longer takes theidrow, becauseplan-bad-idnow refuses that value before the misread rule is asked.The
tests/test_adopt_*.pyfiles (55), in four batches, on37c43deb(before the three later merges of main; the same files were run again on this head, above):test_adopt_manifest_values.py::test_the_derived_ids_words_are_the_tables, which23ce1201changes; that file passes above.test_adopt_plan_yaml_quoting.py::test_check_passes_a_plan_value_read_as_not_text_as_main_doesand theid='007'case oftest_the_misread_rule_names_a_plan_value_that_is_not_text_by_what_was_read, which23ce1201changes; that file passes above.Other tests of these files:
tests/test_derived_id_refusals.py,tests/test_github_name_limits.py,tests/test_github_limits_init_check_org.py,tests/test_reference_default.py,tests/test_doctor_recorded_answers.pyandtests/test_answered_paths_per_leg.py, in one run: 284 passed in 160.85s (0:02:40)tests/test_scaffold_leg_path_collisions.py: 104 passed in 4.07stests/test_shape_doctor.py, once: 445 passed, 3 skipped in 654.95s (0:10:54)The docs guards, in one run:
tests/test_agents_md_codes.py,tests/test_cli_reference.py,tests/test_update_add_codes_documented.py,tests/test_repo_hygiene.py(AGENTS.md's 494-line cap among them) andtests/test_no_auto_maintenance.py: 236 passed in 43.29s. On37c43deb, each on its own: 47, 23, 6, 100 and 60 passed.Mutants
Each mutant is applied to
adopt-project.pyasHEADholds it, the named test is run with-x, and the file is restored. Each mutant counted as killed was killed by a failed assertion.Tistests/test_adopt_values_as_typed.py.T::test_a_refused_name_speaks_before_a_passed_id[no-origin]T::test_a_refused_name_speaks_before_a_passed_id[in-place]T::test_a_passed_id_is_told_what_an_id_may_hold[foo_bar]T::test_in_place_the_naming_refusal_says_the_rule_before_renamingT::test_not_the_source_says_the_rule_its_own_name_breakstests/test_adopt_plan_flag_refusals_first.py::test_a_refusal_the_flags_alone_decide_is_asked_before_anything_exists[org-empty-local]T::test_an_empty_org_is_refused_as_passed[origin-org-the-validator-refuses]tests/test_adopt_plan_flag_refusals_first.py::test_a_refusal_the_flags_alone_decide_is_asked_before_anything_exists[elected-on-empty-org-repo]T::test_an_empty_reference_is_refused_in_the_validators_wordsT::test_check_finds_an_empty_reference_in_the_planT::test_a_plan_with_no_id_names_its_id_as_derived_from_legs_assemblyT::test_check_refuses_an_id_read_as_a_number_or_a_boolean[007]T::test_check_finds_a_branch_read_as_a_number_or_a_boolean[tracking-007]T::test_a_plans_id_the_pattern_refuses_is_told_what_an_id_may_hold[foo_bar]T::test_an_org_repo_sources_own_name_speaks_before_a_passed_id_unclonedtests/test_adopt_check_plan_name.py::test_check_and_execute_say_the_same_remedy_of_the_sources_own_name[no-family]T::test_a_bad_date_says_it_chooses_the_reference_only_where_none_is_set[empty-reference]T::test_check_finds_a_branch_read_as_a_number_or_a_boolean[tracking-1.50]M1 to M19 and E1 to E3 ran on
8016f0b3. The merges of04923f10and3864b18cchange none of their lines: each mutant's text is inadopt-project.pyonce onabf87558and once on8e04a6d1, as it was on8016f0b3, and the merges' hunks are #995's and #970's alone. M8 survived its first test,T::test_an_empty_election_date_is_refused_by_name, because the empty date is refused later anyway, in the same words. It was run again on8e04a6d1with the test that asks the order,tests/test_adopt_plan_flag_refusals_first.py::test_a_refusal_the_flags_alone_decide_is_asked_before_anything_exists(its fourelected-on-emptycases), after a control run with no mutant: 5 passed in 1.85s. Mutant: 1 failed, 1 passed in 0.95s. E1 to E3 survive as equivalent: an empty--orgor--elected-onis now refused before the line E1 or E2 changes, and the shipped naming policy gives the same answer with or without the declared products (E3).Fix round 1
RV1016's review of
8e04a6d1found one MUST and one SHOULD. Commit9261ff2f, on8e04a6d1. Main was not merged in again: what it changed since (2d6f928b) is none of this branch's files.checkandexecuterefused a branch hand-edited to a negative number in different words. Withtracking_branch: -007(oradopt_branch: -007),checksaidplan-bad-tracking-branch, "read out of the plan as the number -7", andexecute, which askedchecked_valuefirst, saidunsafe-valueof a-7nobody typed. That broke adopt-project.pycheck: refuse the branch namesexecutefails on after both legs exist (case,hideRefs, git's own rules) #520's rule that the two refuse a branch name in the same words, and the sentence above thatexecuterefuses it ascheckdoes._checked_plan_valuesnow asks each branch inBRANCH_KEYS' order, the number-or-boolean question first (_checked_branch_value), ascheckdoes (_branch_name_refusal). Both sayplan-bad-*-branch, with the same sentence and the same remedy, andexecuterefuses before any leg exists. (RV1016 proposed one loop over both keys before eitherchecked_value; asking each key in turn keepscheck's order: whereadopt_branchis-xandtracking_branchis007, the first refusal ofexecuteischeck's first finding.) Test:tests/test_adopt_values_as_typed.py::test_execute_refuses_a_negative_number_in_checks_words, a-007row for each key. It assertscheck's finding andexecute's refusal carry the same sentence, thatexecutesayscheck's remedy, and that neither saysunsafe-value._own_name_remediationwas untested.tests/test_adopt_project_is_the_source.py::test_not_the_source_gives_the_rule_of_the_names_own_refusal: originacme/Tools-Installwith--project ToolsInstallmust saynaming-role-mismatch's rule (NAME_RULES), and notnaming-unclassified's "no hyphen, underscore, dot or space".--idbullet under L2-90, and "Merges".PASSED_ID_REMEDIATIONrepeatsscaffold-project.py's sentence. Sharing it would put it inscripts/repo_shape.py, a pinned copy that this branch avoids (scripts/repo_shape.py: the YAML reader refuses a key written twice in one mapping, block or flow, by the line of the second, and a block scalar line indented less than its first by its line, where it kept the last key and cut the line's first characters; such a line opening#ends the block as a comment (#917) #974 and validate-manifest.py, validate-repository-naming.py, repo_shape.py and scaffold-project.py: a chain source that is there and cannot answer is named with why instead of offered, aSHAPE_PIN_SOURCE_*two products of one run share answers for no chain, and a first chain link that is a--pinis recorded in the pin's spelling (#909) #981 edit it).Tests on
9261ff2f, each file alone, undertimeout 2400, a stubghfirst on PATH (it exits 97),GH_TOKEN=bogus, an emptyGH_CONFIG_DIR,GIT_SSH_COMMAND=falseandEMAIL=tests@openreposhape.invalid:tests/test_adopt_values_as_typed.py: 40 passed in 27.75stests/test_adopt_branch_names.py: 29 passed in 25.29stests/test_adopt_project_is_the_source.py: 23 passed in 19.79stests/test_adopt_check_plan_name.py: 45 passed in 57.62stests/test_adopt_plan_flag_refusals_first.py: 89 passed in 34.17stests/test_scaffold_leg_path_collisions.py: 104 passed in 4.38stests/test_agents_md_codes.py,tests/test_cli_reference.py,tests/test_update_add_codes_documented.pyandtests/test_repo_hygiene.py: 176 passed in 36.50stests/test_no_auto_maintenance.py: 60 passed in 8.54stests/test_shape_doctor.py: not run again in this round. It passed on8e04a6d1(445 passed, 3 skipped). The fix changes_checked_plan_values, which onlycmd_executecalls.Mutants of fix round 1, each applied to
adopt-project.pyas9261ff2fholds it, the new test run with-x, the file restored:_checked_branch_valueaskschecked_valueonly, asexecutedid (the MUST-1 fix removed)tests/test_adopt_values_as_typed.py::test_execute_refuses_a_negative_number_in_checks_words[tracking-branch]_own_name_remediationalways givesnaming-unclassified's ruletests/test_adopt_project_is_the_source.py::test_not_the_source_gives_the_rule_of_the_names_own_refusalMerges
git merge-tree --write-treeof this head,9261ff2f, which holds main3864b18c, with each:origin/main2d6f928b: clean, treec7dc7857. Main has changedscripts/repo_shape.py(scripts/repo_shape.py: the YAML reader refuses a key written twice in one mapping, block or flow, by the line of the second, and a block scalar line indented less than its first by its line, where it kept the last key and cut the line's first characters; such a line opening#ends the block as a comment (#917) #974),update-shape.py,scripts/bump-leg.py,scripts/family.py,setup-project.py, a templatebootstrap.pyand their tests since3864b18c: none of this branch's files (AGENTS.md,adopt-project.pyand thetests/test_adopt_*.pyit lists), so it is not merged in.checkandexecuteask a plan'sorg:andid:before its leg paths and branch names, apins:entry_read_pinsnames is that one finding, and adigest_source:finding says how the digest was read (#901) #1015 (fix/901-plan-value-order,2b04aec2), the one open PR that changes files this branch changes (adopt-project.pyandtests/test_adopt_plan_yaml_quoting.py): clean with this head alone (tree82fc90f2) and with this head and main (treefdcc6ffc). As a pretest, that predicted result (two scratch commits,1b09db29, never pushed) was run in a scratch worktree, in one process:tests/test_adopt_values_as_typed.py,tests/test_adopt_plan_value_order.py(adopt-project.py:checkandexecuteask a plan'sorg:andid:before its leg paths and branch names, apins:entry_read_pinsnames is that one finding, and adigest_source:finding says how the digest was read (#901) #1015's),tests/test_adopt_branch_names.pyandtests/test_adopt_project_is_the_source.py: 110 passed in 91.36s.Authentication failed), or whose ssh key it refuses (Permission denied (publickey)), names how to replace it and the--reuse-empty-repore-run before the ruleset hint, in any locale (#993) #1012 (d87263e4), validate-repository-naming.py and validate-manifest.py: a setSHAPE_PIN_SOURCE_<LINK>the run guard passes over is named once by alink-source-collidesnote,--link-remedy=openXdox=a/openYdox=bgivesopenXdoxits words, and a row runs setup step (4) from another folder (#990, #991, part of #999) #1011 (f5749b7b), scaffold-project.py, family.py and shape_materialize.py: the scaffold refuses a --project no Windows disk can make a folder of, by the one question init asks of --family, and the five folder flags' --help say what an empty value and . mean (#989, part of #999) #1010 (37b0fc43), family.py and bump-leg.py: every remedy that namesfamily.py bumpnames--family-root, so each runs as printed (#927) #1002 (3df537a4), openRepoShape, setup.sh and install.sh: a URL, path or ref with a letter past ASCII is no control character underCeither, as #760's byte rule decides in all seven checks that used[![:print:]], and every control character is still refused #986 (d4bba685), validate-family.py:git-not-runnablecounts only anOSErrorthat namesgit, or aFileNotFoundErrororPermissionErrorthat names no file, asvalidate-pins.pycounts it, and only around the git calls, so a member's checkout git could not enter, or a member check's own step, is never told to put git on PATH (#928) #983 (840d2a07), update-shape.py: a path in the root or the upstream this user cannot read isupdate-unreadableon every Python, so ascripts/it can list but not search is no longer fivecopy-missingand a validator it cannot stat is never SKIPPED #976 (c61e6a70), shape-doctor.py and shape_advisory.py: a path in the root this user cannot reach is named on every Python, never read as absent or stood in for by the template, a root it cannot search is refused first, and a row that could not ask is never DRIFTED #975 (3fd80cbe), bootstrap.py: the paste words it prints quote a leading=, are made of the bytes git is handed (UTF-8 on Windows), and the NOT INITIALIZED command names a leg's path as one word after a--, with./before a:(#961) #973 (b2887cf5), the assembly root's validate-manifest.py and bootstrap.py: a path this user cannot read is named, never read as absent, so ascripts/nobody may search no longer SKIPs every validator intobootstrap ok(#430, slice D) #958 (5aaeac64) and the family root's bootstrap.py: a gitlink a member's own index gives no url, or a member's commit that a fetch reaching its remote did not bring, fails the run by name with its cure and no credential advice, where it saidfamily bootstrap okbeside git's exit 128 (#892) #954 (9b557d97). Each also merges with main alone.Lane: openreposhape-3 (openRepoShape-3)
🤖 Generated with Claude Code
Summary by Sourcery
Make adoption planning and validation preserve typed inputs, report refusals against the values users actually supplied, and provide consistent naming and ID guidance.
Bug Fixes:
Enhancements:
Documentation:
Tests: