Skip to content

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

Merged
brettheap merged 8 commits into
mainfrom
l3-902-adopt-plan-values
Oct 20, 2026
Merged

brettheap merged 8 commits into
mainfrom
l3-902-adopt-plan-values

Conversation

@brettheap

@brettheap brettheap commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

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 on c4dd19dc before it. None was fixed on main, and each one is fixed here. Main is 3864b18c as merged in here (it has moved on since, to 2d6f928b: see Merges). Of the landings since c7a3ff16 (#984, #966, #971, #972, #957, #960, #945, #946, #951, #955, #995, #970 and #956), only #995 and #970 touch adopt-project.py, and only #970 touches docs/cli.md; none touches AGENTS.md. #995 changes how a neutral_product_pins: entry is spelled. #970 makes a --source or a recorded source this user cannot reach source-unreadable. Neither is one of the seven lines.

This branch has seven commits, meant to be squashed: two WIP commits, d046905d (the change) and 23ce1201 (three test files), four normal merges of main, and fix round 1's commit, 9261ff2f (see "Fix round 1"). The merges:

Every probe used a stub gh first on PATH (it exits 97), GH_TOKEN=bogus, an empty GH_CONFIG_DIR, GIT_SSH_COMMAND=false, local sources, and execute --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: 007 gave check exit 0 (plan ok), then execute exit 0 (adoption verified), and project.yaml said id: "7".
  • tracking_branch: 007, true and 1.50, with the source holding a branch 7, True or 1.5, was adopted onto that branch.
  • adopt_branch: 007 and true pushed the split to 7 and to True.

Now:

  • An id: read as a number or a boolean is REFUSED plan-bad-id: <plan>: id:was read out of the plan as the number 7, not as text, soexecutewould take it as the text7``. check exits 2 and `execute` refuses it before any leg exists.
  • A tracking_branch: or adopt_branch: read that way is a finding under the key's own code, plan-bad-tracking-branch or plan-bad-adopt-branch, in the same words. check exits 1. execute refuses it itself, under the same code and in the same words, before any leg exists: it asks the number-or-boolean question first, as check does, so a tracking_branch: -007 is plan-bad-tracking-branch in both, and not unsafe-value of the -7 nobody typed (fix round 1). Git is not asked about it.
  • The remedy is said once: write each value between double quotes in the plan, as typed ("007", "true"), or re-run plan, which writes every value so that it reads back as typed.

L2-86: --org ''

On main, flags_given counted --org '' as passed, but _planned_org, org_given and adopt-not-the-source read it as not given. Three things followed:

  • A refusal named --org is '~x', a value nobody typed.
  • adopt-not-the-source said "(--org not given)".
  • With no origin, the result was adopt-no-org.

Now --org is read by is not None everywhere: in _checked_flag_inputs, _planned_org, org_given and _refuse_a_clone_of_another_repository. --org '' is now REFUSED unsafe-value: --org is '', which is not a value this tool will put on a gitorgh command 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:

The validator saw the default and not '', because _template_table read reference: by its truth. It now reads it by is not None. This changes two other things:

L2-88: plan-bad-id for a plan with no id:

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: change legs.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 like plan's derived case (L2-54).

L2-89: the naming rule, in place

When the source is adopted in place, _check_names now 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: ...

L2-90, and adopt's half of L2-82 (#459, U159): the name before a passed id, and what an id may hold

L1-49: AGENTS.md

AGENTS.md's adoption step now says that --org defaults to the organisation of an org/repo source or of a url origin, and that a path origin, or none, needs --org. The file is still 494 lines.

To make room, the paragraph's other clauses were tightened. These were dropped:

  • "a hard dependency:", which the refusal says already;
  • ", printing the install command";
  • "in order".

"Step 4 is the one you must not skip" is now "never skip step 4".

Decisions the Direction left open

Out of scope

Tests

Every run used:

  • timeout 900 for a file run alone, and timeout 2400 for a batch;
  • a stub gh first on PATH (it exits 97), GH_TOKEN=bogus, an empty GH_CONFIG_DIR, GIT_SSH_COMMAND=false and EMAIL=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:

Two existing tests were changed to #902's words:

  • tests/test_adopt_manifest_values.py::test_the_derived_ids_words_are_the_tables calls _refuse_bad_id directly. 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_does pinned L1-K3's behaviour, check passing tracking_branch: 007 and id: 007. It is now test_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 the id row, because plan-bad-id now refuses that value before the misread rule is asked.

The tests/test_adopt_*.py files (55), in four batches, on 37c43deb (before the three later merges of main; the same files were run again on this head, above):

  • adopt0 (14 files): 710 passed, 2 skipped
  • adopt1 (14 files): 1 failed, 1107 passed. The failure was test_adopt_manifest_values.py::test_the_derived_ids_words_are_the_tables, which 23ce1201 changes; that file passes above.
  • adopt2 (14 files): 5 failed, 1390 passed. The failures were four cases of test_adopt_plan_yaml_quoting.py::test_check_passes_a_plan_value_read_as_not_text_as_main_does and the id='007' case of test_the_misread_rule_names_a_plan_value_that_is_not_text_by_what_was_read, which 23ce1201 changes; that file passes above.
  • adopt3 (13 files): 795 passed

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.py and tests/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.07s
  • tests/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) and tests/test_no_auto_maintenance.py: 236 passed in 43.29s. On 37c43deb, each on its own: 47, 23, 6, 100 and 60 passed.

Mutants

Each mutant is applied to adopt-project.py as HEAD holds it, the named test is run with -x, and the file is restored. Each mutant counted as killed was killed by a failed assertion. T is tests/test_adopt_values_as_typed.py.

Mutant What it breaks Result Killed by
M1 no name check before a passed id's refusal killed T::test_a_refused_name_speaks_before_a_passed_id[no-origin]
M2 flags refuse a passed id whatever the name killed T::test_a_refused_name_speaks_before_a_passed_id[in-place]
M3 the old passed-id remedy killed T::test_a_passed_id_is_told_what_an_id_may_hold[foo_bar]
M4 in place, no rule before renaming killed T::test_in_place_the_naming_refusal_says_the_rule_before_renaming
M5 not-the-source says no rule killed T::test_not_the_source_says_the_rule_its_own_name_breaks
M6 flags read --org by truth killed tests/test_adopt_plan_flag_refusals_first.py::test_a_refusal_the_flags_alone_decide_is_asked_before_anything_exists[org-empty-local]
M6b flags and _planned_org read --org by truth killed T::test_an_empty_org_is_refused_as_passed[origin-org-the-validator-refuses]
M8 flags read --elected-on by truth killed 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]
M10 --reference read by truth killed T::test_an_empty_reference_is_refused_in_the_validators_words
M11 the table reads reference: by truth killed T::test_check_finds_an_empty_reference_in_the_plan
M12 a plan with no id: named as id: killed T::test_a_plan_with_no_id_names_its_id_as_derived_from_legs_assembly
M13 id: read as a number taken as text killed T::test_check_refuses_an_id_read_as_a_number_or_a_boolean[007]
M14 a branch read as a number taken as text killed T::test_check_finds_a_branch_read_as_a_number_or_a_boolean[tracking-007]
M15 the old plan-bad-id remedy for id: killed T::test_a_plans_id_the_pattern_refuses_is_told_what_an_id_may_hold[foo_bar]
M16 pre-clone check asks only a derived id killed T::test_an_org_repo_sources_own_name_speaks_before_a_passed_id_uncloned
M17 check prints the in-place remedy only without the rule killed tests/test_adopt_check_plan_name.py::test_check_and_execute_say_the_same_remedy_of_the_sources_own_name[no-family]
M18 the date finding reads reference: by truth killed T::test_a_bad_date_says_it_chooses_the_reference_only_where_none_is_set[empty-reference]
M19 a float is text killed T::test_check_finds_a_branch_read_as_a_number_or_a_boolean[tracking-1.50]
E1 equivalent: org_given by truth survived equivalent (127 passed in 43.70s)
E2 equivalent: _checked_plan_inputs reads --elected-on by truth survived equivalent (127 passed in 45.11s)
E3 equivalent: the name asked with no pins survived equivalent (184 passed in 141.33s (0:02:21))

M1 to M19 and E1 to E3 ran on 8016f0b3. The merges of 04923f10 and 3864b18c change none of their lines: each mutant's text is in adopt-project.py once on abf87558 and once on 8e04a6d1, as it was on 8016f0b3, 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 on 8e04a6d1 with 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 four elected-on-empty cases), 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 --org or --elected-on is 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 8e04a6d1 found one MUST and one SHOULD. Commit 9261ff2f, on 8e04a6d1. Main was not merged in again: what it changed since (2d6f928b) is none of this branch's files.

Tests on 9261ff2f, each file alone, under timeout 2400, a stub gh first on PATH (it exits 97), GH_TOKEN=bogus, an empty GH_CONFIG_DIR, GIT_SSH_COMMAND=false and EMAIL=tests@openreposhape.invalid:

  • tests/test_adopt_values_as_typed.py: 40 passed in 27.75s
  • tests/test_adopt_branch_names.py: 29 passed in 25.29s
  • tests/test_adopt_project_is_the_source.py: 23 passed in 19.79s
  • tests/test_adopt_check_plan_name.py: 45 passed in 57.62s
  • tests/test_adopt_plan_flag_refusals_first.py: 89 passed in 34.17s
  • tests/test_scaffold_leg_path_collisions.py: 104 passed in 4.38s
  • the docs guards, in one run: tests/test_agents_md_codes.py, tests/test_cli_reference.py, tests/test_update_add_codes_documented.py and tests/test_repo_hygiene.py: 176 passed in 36.50s
  • tests/test_no_auto_maintenance.py: 60 passed in 8.54s
  • tests/test_shape_doctor.py: not run again in this round. It passed on 8e04a6d1 (445 passed, 3 skipped). The fix changes _checked_plan_values, which only cmd_execute calls.

Mutants of fix round 1, each applied to adopt-project.py as 9261ff2f holds it, the new test run with -x, the file restored:

Mutant Result Killed by
MR1: _checked_branch_value asks checked_value only, as execute did (the MUST-1 fix removed) killed tests/test_adopt_values_as_typed.py::test_execute_refuses_a_negative_number_in_checks_words[tracking-branch]
R2 (RV1016's): _own_name_remediation always gives naming-unclassified's rule killed tests/test_adopt_project_is_the_source.py::test_not_the_source_gives_the_rule_of_the_names_own_refusal

Merges

git merge-tree --write-tree of this head, 9261ff2f, which holds main 3864b18c, with each:

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:

  • Preserve explicitly typed empty flag values and reject YAML numbers or booleans where textual IDs and branch names are required, with consistent diagnostics before any adoption work begins.
  • Correct refusal ordering so naming-policy violations are reported before invalid passed IDs, and identify derived IDs and source names accurately in remediation guidance.
  • Treat empty references as explicit values and validate them consistently in plans, checks, and execution.

Enhancements:

  • Unify naming and ID remediation messages so they state the applicable naming or identifier rules before offering corrective actions.

Documentation:

  • Clarify adoption guidance for organization defaults and when --org is required.

Tests:

  • Add comprehensive coverage for typed plan values, empty flags, refusal ordering, derived IDs, naming remediation, and consistent check/execute behavior.

Brett Heap and others added 6 commits October 19, 2026 14:04
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>
Main since c7a3ff1: #984, #966, #971, #972 and #957. No conflict: #966's
docstring edits in tests/test_adopt_check_plan_name.py and
tests/test_adopt_project_is_the_source.py merged beside #902's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…'s one neutral-pin spelling

Clean merge. #995 moves `_neutral_pin_entry` and `_PLAIN_RE`'s pattern into
`scripts/shape_materialize.py`, with the same regex; #902 touches neither.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…'s source-unreadable

Clean merge. #970 adds `_is_a`, `_is_there`, `source-unreadable` and its
helpers to `adopt-project.py`, and #956 and #970 change `docs/cli.md` and
`validate-family.py`; #902 touches none of those hunks.

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

sourcery-ai Bot commented Oct 19, 2026

Copy link
Copy Markdown

Reviewer's Guide

The 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 refusals

sequenceDiagram
    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
Loading

Flow diagram for typed plan value validation

flowchart 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]
Loading

File-Level Changes

Change Details Files
Preserve explicitly typed values instead of treating empty strings as absent or coercing YAML numbers and booleans into text.
  • Reject numeric and boolean id, tracking_branch, and adopt_branch plan values with key-specific diagnostics and quoting guidance.
  • Treat empty --org, --elected-on, and --reference values as passed values, validating them before source access; preserve empty reference: semantics in plans.
  • Add shared helpers for non-text detection and typed-value remediation.
adopt-project.py
tests/test_adopt_values_as_typed.py
tests/test_adopt_plan_flag_refusals_first.py
tests/test_adopt_plan_yaml_quoting.py
tests/test_adopt_manifest_values.py
Make identifier and naming refusals identify the actual source of the invalid value and present the applicable naming rule before remediation.
  • Distinguish a derived id from a plan-provided id and explain that derived ids must be fixed through legs.assembly.
  • Use explicit id-format remediation for command-line and plan ids.
  • Run naming checks before invalid passed-id refusals where the naming policy owns the project names, including pre-clone repository sources.
  • Centralize naming rules and in-place/name-mismatch remediation so plan, check, and execute report consistent guidance.
adopt-project.py
tests/test_adopt_plan.py
tests/test_adopt_project_is_the_source.py
tests/test_adopt_check_plan_name.py
tests/test_adopt_values_as_typed.py
Document the revised adoption organization behavior and expand regression coverage for refusal ordering and early side-effect-free validation.
  • Clarify --org defaults and when it is required in the adoption instructions.
  • Update existing expectations for empty flags, derived ids, naming remedies, and YAML misreads.
  • Add comprehensive tests covering typed values, empty inputs, reference validation, derived identifiers, naming precedence, and no-clone/no-leg guarantees.
AGENTS.md
tests/test_adopt_values_as_typed.py
tests/test_adopt_plan_flag_refusals_first.py
tests/test_adopt_plan_yaml_quoting.py
tests/test_adopt_project_is_the_source.py

Assessment against linked issues

Issue Objective Addressed Explanation
#902 Refuse plan values read as numbers or booleans when the manifest expects text, identifying what was read and how execution would interpret it. ✅
#902 Treat empty --org, --elected-on, and --reference arguments as passed values, and refuse them by name rather than silently substituting defaults or inferred values. ✅
#902 Make id and naming refusals accurately identify the value’s source and give a remedy applicable to what was typed, including missing plan IDs and in-place renames; document the --org default in AGENTS.md. ✅

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@brettheap

Copy link
Copy Markdown
Contributor Author

(lane openRepoShape-3, 2026-10-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 tracking_branch: -007 (or adopt_branch: -007): check says FINDING plan-bad-tracking-branch: ... read out of the plan as the number -7 ..., but execute says REFUSED unsafe-value: tracking_branch is '-7' ..., another code and words, naming a value nobody typed. On main 3969409 both said unsafe-value '-7'. This breaks #520's same-words rule (_checked_plan_values docstring, :12983) and the body's "execute refuses it with check's other findings". Fix: in _checked_plan_values, before :12994, for key, default, code in BRANCH_KEYS: raise Refusal(code, _not_text(key, value), NOT_TEXT_REMEDIATION) when _a_number_or_boolean(plan.get(key, default)); add -007 rows to tests/test_adopt_values_as_typed.py:200. Tried in scratch: check and execute then say the same plan-bad-*-branch sentence; test_adopt_branch_names.py + test_adopt_values_as_typed.py: 67 passed in 52.92s.

SHOULD-1 adopt-project.py:15442: the role-mismatch rule in _own_name_remediation is untested. Mutant "always naming-unclassified's rule" survives test_adopt_values_as_typed.py, test_adopt_project_is_the_source.py and test_adopt_check_plan_name.py, and changes what is printed for origin acme/Tools-Install with --project ToolsInstall. Add that row to tests/test_adopt_project_is_the_source.py:169.
SHOULD-2: elected_by: 007/true/1.50, reference: 1.50/007 and org: 007/true still give check exit 0, plan ok (measured on the head). #457 holds no line for them; post one when #902 closes.
NIT-1 :4796: with origin testorg/Atlas, --project North_Wind --id north_wind gets naming-unclassified ("--project takes CamelCase"), while --id north-wind gets adopt-not-the-source ("--project Atlas"). Reword the body's "in the words it has when it speaks later", or let not-the-source speak first.
NIT-2: the Merges section is stale: main is 2d6f928, and #1015 now changes adopt-project.py and tests/test_adopt_plan_yaml_quoting.py.
NIT-3: PASSED_ID_REMEDIATION (:4710) repeats scaffold-project.py:949; it could be shared.

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>
@brettheap

Copy link
Copy Markdown
Contributor Author

(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:

  • tracking_branch: -007: check exits 1 with FINDING plan-bad-tracking-branch: ... read out of the plan as the number -7 ...; execute exits 2 with REFUSED plan-bad-tracking-branch:, the same sentence and check's remedy, and no remotes made. adopt_branch: -007 is the same under plan-bad-adopt-branch.
  • The writer's order case, adopt_branch: -x with tracking_branch: 007: check finds unsafe-value: adopt_branch is '-x' and then plan-bad-tracking-branch; execute refuses unsafe-value: adopt_branch is '-x', check's first finding. The mirror case (adopt_branch: 007, tracking_branch: -x) refuses plan-bad-adopt-branch first in both. Asking one key at a time is right.
  • test_adopt_branch_names.py, which holds adopt-project.py check: refuse the branch names execute fails on after both legs exist (case, hideRefs, git's own rules) #520's same-words rows: 29 passed.

Mutants at 9261ff2:

  • R2 (not-the-source always gives naming-unclassified's rule): killed by test_adopt_project_is_the_source.py::test_not_the_source_gives_the_rule_of_the_names_own_refusal.
  • The fix removed (_checked_branch_value asks checked_value only): killed by test_adopt_values_as_typed.py::test_execute_refuses_a_negative_number_in_checks_words[tracking-branch].

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 --id is passed". With no --id, the first refusal in place is the derived id's adopt-bad-id. The rule and then "rename first" is what a valid --id (north-wind) gets. Suggest "the words it has when the --id passes". Main has also moved to dbf12e0 (scaffold-project.py only), which is not one of this branch's files.

Lane: openreposhape-3 (openRepoShape-3)

@sonarqubecloud

Copy link
Copy Markdown

@brettheap
brettheap marked this pull request as ready for review October 20, 2026 03:49
@sourcery-ai

sourcery-ai Bot commented Oct 20, 2026

Copy link
Copy Markdown

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

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

@brettheap
brettheap merged commit 42cd417 into main Oct 20, 2026
5 checks passed
@brettheap
brettheap deleted the l3-902-adopt-plan-values branch October 20, 2026 03:49
brettheap pushed a commit that referenced this pull request Oct 20, 2026
…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>
brettheap pushed a commit that referenced this pull request Oct 22, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

adopt-project.py plan and check: an empty flag or a bare plan value is taken without a word, and the id refusals name what the person did not type

1 participant