From cbe44fe0f41956f6f1c92626bab29b1b847acc37 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Mon, 12 Oct 2026 21:51:34 +0000 Subject: [PATCH 1/7] docs/release-notes/v1.0.0.md: the v1.0.0 release notes, a draft for Brett to amend before he tags, saying what 1.0 contains and promises, how to pin it by commit, what changed on the way grouped by the five tracking areas, and every known limit as the input that triggers it and what the person sees (#472) Nothing in the repository said what v1.0.0 covers or listed its known limits, and the release gate asks for both (#472). This file is that text. It is a DRAFT that lands late, just before the tag: the in-scope list keeps moving until then, and Brett may move items in or out of scope once he reads the known limits listed together. At tag time he copies the text into the GitHub release body. Before that, release/REFRESH.md's read-only commands bring it up to date: newly merged PRs go to their areas, closed limits drop out, and section 7 is rewritten from the milestone. It is a new file under docs/ and touches nothing else. No rule in tests/test_repo_hygiene.py requires a README or handbook line for a file there. The rules that walk every tracked .md are the host-path scan and the " naming families" count, and this file trips neither. README.md and AGENTS.md stay at their caps, as #472 asks. Sections: 1. What openRepoShape is, from README.md, and the 1.0 promise. 2. The procedures AGENTS.md documents, one line and one command each. 3. Requirements and platforms, as README.md states them. 4. How to pin ``. The flags were checked against docs/cli.md and the scripts. `apply` is shown without `--yes`, so it asks `Type yes to proceed:` before it writes, as AGENTS.md has the verdicts shown first. 5. All 180 merged PRs, grouped by theme under the five tracking areas (#457 to #461). 6. 64 known-limit issues, plus the parents' unfiled checklist items. 7. The open v1.0.0 milestone issues. This section opens with ``. 8. Links to the full PR list and the milestone. The sentence that CI runs the full suite on Windows carries a comment in both places it appears, because it holds only once #471 makes the Windows job run the adoption suites. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 --- docs/release-notes/v1.0.0.md | 344 +++++++++++++++++++++++++++++++++++ 1 file changed, 344 insertions(+) create mode 100644 docs/release-notes/v1.0.0.md diff --git a/docs/release-notes/v1.0.0.md b/docs/release-notes/v1.0.0.md new file mode 100644 index 00000000..11497a33 --- /dev/null +++ b/docs/release-notes/v1.0.0.md @@ -0,0 +1,344 @@ +# openRepoShape v1.0.0 + +openRepoShape is the convention, as data and as running code, for a project +that spans several repositories: what they are called, how they are pinned to +each other, how one clone brings the whole project down, and what an AI +assistant does when asked to scaffold one. Its preferred shape, the Triad, is an +assembly root that mounts a spec leg and a code leg. The shape is elective and +confers nothing. Version 1.0 promises that every procedure in AGENTS.md works +end to end for ordinary repositories on Linux, macOS and Windows. It is tagged +only when every in-scope issue is closed and the full test suite passes on all +three systems on the release commit. Edge cases that only unusual input reaches +are listed below as known limits. + +## What 1.0 contains + +- **New project:** `openRepoShape --org `, or `./setup.sh`, or + `py setup-project.py` on Windows. It runs a preflight, checks the names, + shows the plan and asks for one typed `yes`. +- **Direct scaffold:** `scaffold-project.py`. Add `--pin open@` + for a declared descendant. +- **Adopt in place:** `adopt-project.py plan`, then `check`, then `execute`. +- **Compliance check:** `openRepoShape --doctor []` or + `shape-doctor.py --root `. +- **Stay current:** `update-shape.py check`, then `apply`. +- **Advance a leg:** `scripts/bump-leg.py --root --leg spec|code --to <40-hex>`. +- **Families:** `scripts/family.py init|add|bump|remove|siblings`. +- **Park and resume:** `make park` and `make resume`. +- **Each project's own gate:** `make bootstrap` and `make validate` run pinned + copies of the scripts, so the gate never needs this standard. + +## Requirements and platforms + +- `git` and Python 3.9 or newer. The tools use the standard library only. +- `gh`, logged in, wherever a tool creates repositories or pull requests on + GitHub. `git-filter-repo` is needed for adoption only. `make` is optional. +- `openRepoShape --preflight` checks the machine and creates nothing. On + Windows, run `py setup-project.py --preflight`. It offers each missing + install behind a typed `yes`. +- **Linux** and **macOS** are supported, and the stock macOS `/bin/bash` 3.2 is + enough. **Native Windows** uses `setup-project.py`, after + `git config --global core.autocrlf false`. **WSL2** runs the bash entry + points. Park and resume do not work on native Windows. +- CI runs the full suite on `ubuntu-latest`, `macos-latest` and + `windows-latest`. + + +## How to pin 1.0.0 + +Projects pin the standard by commit in `contracts/shape-pin.yaml`. The README's +rule is that a tag is never the referent, because a tag can be moved and a +commit cannot. Pin ``, not the tag. + +- **New project:** run + `OPENREPOSHAPE_REF= openRepoShape --org `. + The command forwards `--shape-ref ` to `setup.sh`. Outside a + checkout, `setup.sh` and `setup-project.py` take `--shape-ref` directly. +- **Existing project or family holder:** run these from a checkout of the + standard. `apply` shows what it will re-copy and asks `Type yes to proceed:` + before it writes anything: + + ```sh + ./update-shape.py check --root --at + ./update-shape.py apply --root --at \ + --branch shape/update- + git -C push -u origin shape/update- # then open a pull request + ``` + +- **Confirm:** run `./shape-doctor.py --root ` from a checkout at + ``. `COMPLIANT`, exit 0, means the pin names that commit. + +## What changed on the way to 1.0 + +### adopt-project.py + +- **In-place adoption.** The repository becomes the assembly root and keeps + its name and history. The legs are extracted with `git filter-repo`, and the + split lands as one pull request. Every source path is verified by blob sha. + The plan asks about any path it cannot place, and an empty leg is seeded with + `--allow-empty-leg` (#5, #16, #17, #24). +- **Refused before any leg exists.** `execute` refuses everything `check` + finds before it creates a leg. That covers bad mount paths, root paths that + clash with the shape's files, values no file can hold + (`adopt-unwritable-value`), and paths it cannot extract, which `plan` now + asks about + (#170, #187, #198, #231, #270, #313, #353, #377). +- **Submodules.** `.gitmodules` stays in one leg with its submodules. An + unreadable one is a `plan-gitmodules-unreadable` finding, not a crash, and + relative URLs are checked from both sides. Verification accepts no root + gitlink except the two leg mounts (`adopt-root-submodule`, + `adopt-mount-commit` and others) + (#162, #173, #194, #226, #240, #245, #268). +- **Ignore rules and odd names.** Shape files are committed even when the + source's `.gitignore` matches them, and leg mounts use `--force`. Findings + spell out control characters and bytes that are not UTF-8, follow-up + searches take paths literally, and a cp1252 console gets escaped names + (#182, #200, #223, #228, #246, #247, #378). +- **Late failures.** After a leg exists, a failure prints `REFUSED .` + and what the run left behind (#445). Exit 1 means the verification failed. + Exit 2 with `IS pushed` is finished by hand with the command it prints, + never by re-running (#371). + +### update-shape.py and bump-leg.py + +- **Re-syncing copies.** `check` gives each pinned file a verdict + (`unchanged`, `upstream-changed`, `locally-modified` or `both`) and names + files the standard has added (`upstream-added`). It exits 1 when there is + work to do. `apply` re-copies and re-pins, and rolls back if the project's + validators fail. `--add` and `--accept-local` take one file at a time. + Family holders, and their README's `Shape:` line, update the same way + (#10, #29, #47, #52, #85, #152, #159). +- **Landing.** `apply --branch` takes `--trailer` lines. The `gh pr create` it + prints targets the root's own branch and repository. A failed push keeps the + commit and prints a NEXT line. A later `--pr` run opens the pull request, + unless the tree has changed since (`update-rerun-uncommitted`) + (#112, #218, #227, #379). +- **Clean rollback.** A failure before the commit restores everything. + Additions through links or under files are refused (`update-add-symlink`, + `update-add-not-a-directory`). Pin rewrites keep layout and comments, and + non-ASCII project names work under cp1252 + (#224, #233, #269, #281, #346, #380). +- **bump-leg.py.** One commit moves a leg's gitlink, pin and workflow + references, and `--dry-run` shows `old -> new` and the trailers first. + Untracked workflows are refused (`bump-leg-workflow-untracked`), and + unreadable files are refused by name. Case-only renames are followed, and + any failure rolls back (#35, #151, #222, #264, #344, #352, #403). + +### setup, scaffold, family and the shared helpers + +- **Front door.** `setup.sh` self-bootstraps from upstream with an explicit + `--org`, so no fork is needed. It is a shim over `setup-project.py`, the + native Windows way in. `openRepoShape` installs as a command. The preflight + offers installs behind a typed `yes`. `--local-remote-dir` rehearses a run + with nothing created on GitHub, and `--family` lands the clone in a family + folder. `setup.sh --help` outside a checkout answers without cloning anything + (#2, #3, #14, #37, #53, #56, #57, #60, #62, #65, #75, #78, #452). +- **Naming and descent.** Names are classified offline against six families. + A descendant must pin its `open`, directly or through a recorded + chain. The default `reference:` follows the election date. The Triad + advisory speaks once and never blocks, and `single-repository.yaml` silences + it (#4, #6, #19, #27, #42, #45, #86, #164). +- **Scaffold refusals before anything exists,** dry runs included: + - colliding leg paths (`scaffold-leg-path-collides`); + - pins the shape cannot write (`scaffold-pin-collides`); + - bad chain links; + - manifest values the project would misread, and text no file can hold; + - a `--tracking-branch` git would not take as a branch name, here and in + `family.py init`. + + Shape files and leg mounts get past the operator's own ignore rules, and + templates are filled in one pass + (#185, #201, #234, #235, #256, #271, #314, #316, #351, #355). +- **Each project's own gate.** `make bootstrap` places each leg at its pin, + and `.gitattributes` keeps LF line endings. `AGENTS-shape.md` carries the + rules for assistants, and a `-wip` template ships too. The `validate` + workflow reads private legs with a GitHub App or `SHAPE_LEGS_TOKEN`, and + carries on with a warning without one. `validate-pins.py` rechecks + neutral-product pins offline where it can, and refuses `git-not-runnable` + (exit 2) (#1, #7, #8, #12, #13, #15, #18, #25, #30, #58, #154, #312, #446). +- **Families.** A holder pins member roots under `members/`, and + `make siblings` clones working copies beside it without moving anything. + `add`, `bump` and `remove` have `--dry-run`. They check before they write, + and restore the holder on failure. `add` refuses duplicates by name or id + (`member-duplicate-id`). A repeated `bump` says `nothing to do` and exits 1 + (#16, #79, #219, #221, #236, #308, #315, #317, #318, #345, #350, #381, #399, #400, #406, #416). +- **Park and resume.** `make park` and `make resume` hand off to the Speckit + git extension. The installed commands moved to opensoft/openRepoTools + (#80, #83, #93, #94). +- **Shared helpers.** The YAML reader refuses what it cannot read exactly + (`yaml-unreadable`). Output from git and `gh` is read as UTF-8 under any + locale. "Same repository" now has one definition + (#156, #160, #193, #230, #244, #257, #265, #284, #390). + +### shape-doctor.py + +- **One verdict.** `openRepoShape --doctor` writes nothing and fetches + nothing. It prints a row per check, then one verdict: + - `COMPLIANT`: exit 0; + - `COMPLIANT, SHAPE BEHIND`, `DRIFTED`, `MISPLACED` or `INVALID`: exit 1; + - `NOT A SHAPE ROOT`: exit 2; + - `CANNOT ANSWER`: exit 3. + + Only a `FINDING` moves the verdict. The machine check is now `--preflight` + (#96). +- **Placement.** The `placement` row checks each leg's paths against the + adoption policy. `--placement-plan` writes a plan for a person to resolve + (#100, #186). +- **NEXT commands you can paste.** They are quoted for sh or PowerShell, use + `Copy-Item -LiteralPath` on Windows, and set the `PYTHONPATH` a validator + needs (#102, #106, #107, #108, #110, #287, #311, #354). +- **More accurate verdicts.** An unloadable manifest or shape pin is now + `INVALID`, where it was `NOT A SHAPE ROOT` or `CANNOT ANSWER`. A misplaced + `family.yaml` is a `manifest kinds` finding, and a crashed validator says so. + A fork beside a holder is no longer taken for the member + (#147, #220, #232, #267, #357, #375, #404). + +### The test suite, CI and the docs + +- **Three operating systems.** Every pull request runs the full suite on + Linux, macOS and Windows. Every push to `main` runs it on Linux + (#9, #11, #53, #57, #124, #129, #336). + +- **Hermetic tests.** No test reaches the real `gh` or the network + (#64, #116, #120, #128, #131, #274, #310, #335, #349). +- **Docs.** The README has a quick start for each platform, and + `docs/handbook.html` is a one-page reading of it. `docs/cli.md` is generated + from every tool's `--help`. A test ties each refusal code AGENTS.md names to + the tool that prints it + (#23, #46, #70, #71, #73, #88, #90, #98, #161, #309, #396). +- **Maintainability.** Static-analysis findings were fixed and long functions + split, with no change in behaviour + (#67, #117, #121, #125, #139, #140, #141, #142, #143, #144, #145, #398). + openRepoShape itself stays a single repository (#372). + +## Known limits + +Each line names the input that triggers the limit, then what you see. Each +limit stays open under its area's tracking issue. + +### [adopt-project.py](https://github.com/opensoft/openRepoShape/issues/457) + +- #178: A dangling or outward symlink the root keeps at or above a path the shape writes: `check` passes, then `execute` crashes or writes outside the assembly after both legs are pushed. +- #181: `plan --spec-path null`, or another YAML keyword: the plan writes it unquoted, and the leg is mounted at `spec`. +- #183: A root-kept symlink to elsewhere on disk, or a link loop: the shape's files are written through the link, or the run crashes, after both legs are pushed. +- #189: A source with a submodule kept in a leg: the doctor can only note it, because the assembly does not record which leg was chosen. +- #293: A hand-edited `follow_ups:` that is not a list: `check` and `execute` end in a `TypeError` traceback, `execute` after both legs exist. +- #294: A plan with a bad `elected_on:` and no `reference:`: `check` says `plan ok`, and `execute` refuses `election-date-malformed` only after both legs exist. +- #303: A source symlink with an empty target: `check` passes, and `execute` fails after both legs exist. +- #328: A hand-edited `question:`, or a submodule name, holding a line break or ESC: it is printed raw in a finding, and can forge a line. +- #386: A hand-edited plan field of the wrong type (`pins:`, `paths:`, `legs:`, `source:`): `check` and `execute` crash, and `pins: openGlass` adopts with one-letter pins. +- #388: A source symlink whose target begins with a NUL byte: `check` passes, and `execute` fails after both legs exist, with nothing on stderr. +- #389: A plan that keeps the source's own `project.yaml` in the root: verification never checks the root manifest, so it can say `adoption verified` over an assembly the doctor calls NOT A SHAPE ROOT. +- #417: A relative submodule URL under `--local-remote-dir`: `check` and `execute` resolve it from different remotes, so one can refuse what the other passes. +- #418: A kept submodule whose URL names no repository: `adoption verified`, then the recursive clone the `NEXT:` line prints fails. +- #420: A branch name git refuses (`~`, `a..b`, `x.lock`): `plan` and `check` pass, and `execute` dies after a leg exists. +- #425: A source whose default branch name is not UTF-8: `plan`, `check` and `execute` end in a `UnicodeDecodeError` traceback. +- #427: An `origin` whose organisation is not ASCII or holds a space: `plan` refuses `unsafe-value: --org`, a flag nobody passed. +- #449: A hand edit that makes a neutral pin's `commit:` and `tree_sha256:` disagree: `adoption verified`, then the root's `validate-pins.py` fails `neutral-pin-digest-mismatch`. +- #451: A `--pin-source` that nothing reads, even a missing path: `plan` (and the scaffold's `--dry-run`) exit 0 without a word. +- A moved or dropped non-ASCII path under `LC_ALL=C PYTHONUTF8=0`: `execute` ends in a `UnicodeEncodeError` traceback after both legs are pushed. +- A kept or dropped name holding a line break, a control character or a byte that is not UTF-8: it reaches the split commit message and the pull request body unspelled. +- A source with its own `single-repository.yaml`: `plan` asks the general top-level YAML question. A dedicated rule (drop or root, defaulting to drop) is planned. +- A source branch that differs from the split branch only by letter case, on macOS or Windows: `check` passes, and `execute` fails after both legs exist. +- A name holding a backtick or a double quote, or wrapped in backticks: its spelled form can name another entry, or make the plan unreadable. +- A path policy whose `question:` or rule id holds control characters: `plan`'s terminal report prints them raw. +- A hand-edited `tracking_branch:` or `adopt_branch:` such as `a=b`: `check` says `plan ok`, and `execute` refuses `unsafe-value` before any leg exists. + +### [update-shape.py and bump-leg.py](https://github.com/opensoft/openRepoShape/issues/458) + +- #286: A symlink the root keeps at a pinned path: `apply` writes the standard's bytes through it, possibly outside the root, and exits 0. +- #290: A shape pin whose `files:` is not a list (`5`, `true`): the doctor, `update-shape.py` and the project's validators end in a `TypeError` traceback. +- #422: A pinned file kept as a hard link: `apply` rewrites it through its other name, which may lie outside the root. +- #423: A file name that is not UTF-8 under `core.quotePath=false`: `bump-leg.py`, the scaffold and `family.py init` end in a `UnicodeDecodeError` traceback, not a refusal or warning. +- #428: An `apply` whose rollback cannot put one file back (an unwritable copy, a full disk): an `OSError` traceback, with the remaining files left unrestored. +- #431: `apply --branch` started from a directory git cannot open: a good branch name is refused as `update-branch-invalid`. +- #437: A trailing comment on the pin's `files:` or the manifest's `shape:` line: `apply` refuses `update-pin-unrecognised` or `update-manifest-no-shape`, though `check` sent you there. +- #438: A leg pin written as `digests: {tree_sha256: …}`: `bump-leg.py --dry-run` exits 0, and the real run refuses `bump-leg-pin-unrecognised`. +- `bump-leg.py --to` an annotated tag's own id: the dry run shows the tag id and exits 0, while the real run is refused and rolled back with exit 2. + +### [setup, scaffold, family and the shared helpers](https://github.com/opensoft/openRepoShape/issues/459) + +- #225: A leg path YAML reads as another type (`--spec-path null`, `true`, `2026`): all three repositories are created, and the project's bootstrap fails `manifest-leg-path`. +- #259: A value YAML reads as another type (`007`, `2026`, `true`, `null`) in one of eight template fields: the project misreads it, or the tool refuses it. +- #275: Two neutral products whose names differ only in `-`, `_` or `.`: they share one `SHAPE_PIN_SOURCE_` variable, and one gets a `neutral-pin-source-unreadable` finding. +- #322: A non-ASCII `--trailer` under the C locale, on a git without `commit --trailer`: the trailer is committed with `?` for each byte. +- #324: `--tracking-branch trunk` in an organisation whose ruleset refuses the push: the printed hint names `main`, and targets the wrong base. +- #338: A `--link-source` path holding `=`, or a mistyped key: the link is skipped in silence, and a source that cannot be entered crashes the naming check. +- #339: A `--pin-source` that does not exist, is a file or cannot be entered: the scaffold ends in a traceback, `--dry-run` included. +- #359: A workflow file that is not UTF-8: the project's `validate-pins.py` ends in a `UnicodeDecodeError` traceback. +- #362: A holder index holding `Members/…`, a case variant of `members/`, on macOS or Windows: `family.py add` cannot add any member. +- #363: An uncommitted `git rm -r Members` on macOS or Windows: `add` commits the gitlink as `Members/

`, and fresh clones leave the member empty. +- #364: An untracked `Members/

` directory: refused `member-path-taken` on macOS and Windows, but mounted beside it on Linux. +- #391: A manifest `name` the console cannot print, such as a lone surrogate: `make bootstrap` ends in a `UnicodeEncodeError` traceback. +- #392: A flow collection with an empty entry or a leading `:`, `#`, `- ` or `? ` (`[,a]`, `[:]`): the YAML reader accepts it, and reads `[a: b]` as a string. +- #393: A plain value that opens with `]`, `}`, `,`, `@`, a backtick or `%`: the YAML reader takes it as text, where YAML refuses it. +- #407: An uncommitted deletion of the holder's `.gitmodules`: `family.py remove` commits it with the member, and fresh clones leave every member empty. +- #421: `--tracking-branch +x`: `--dry-run` passes, and the push fails after every remote exists. +- #426: A `git config user.name` that is not UTF-8, with no `--created-by`: `family.py init` ends in a `UnicodeDecodeError` traceback. +- #433: A `--local-remote-dir` path that is not UTF-8: the scaffold and `family.py init` end in a traceback after the bare remotes exist. +- #434: A `.github/workflows/` directory the user cannot list: `validate-pins.py` ends in a `PermissionError` traceback. +- #441: A bare `--link-source ` in a chain of three or more links: one tree answers for two links, and a correct chain is reported broken. +- #442: An empty path, as in `--link-source openXdox=` or `--pin-source openXdox=`: the current directory is read, and a correct chain is reported broken. +- #443: A scalar (`5`, `openXdox`) where `neutral_product_pins:` or `referent_chain:` should be a list: the validators crash, or read it one letter at a time. +- #444: Product names that fold differently (`openß`, `openss`): the chain readers treat them as one product, and the pin validator as two. +- #450: A `--pin-source` path holding a line break or a byte that is not UTF-8: it is written into the pin file as it stands, or the scaffold crashes after all three repositories exist. +- #454: A family `name` holding a lone surrogate, or any run under `PYTHONIOENCODING=ascii`: `make siblings`, `park` and `resume` end in a `UnicodeEncodeError` traceback. +- #455: A lone surrogate or NUL in `tracking_branch` or a leg's `branch:`: `make bootstrap` ends in a traceback, while `validate-manifest.py` says `manifest ok`. +- `family.py bump --to` a commit whose `project.yaml` `id` another row already pins: `bump` commits, and the holder's validator then refuses `member-duplicate-id`. +- An annotated tag's own id: `family.py add --at` pins the commit it tags, while `bump --to` treats it differently. One rule is still to be chosen. +- No git on `PATH`: the shipped `bootstrap.py` scripts end in a `FileNotFoundError` traceback. +- A directory name that is not UTF-8: `validate-repository-naming.py` ends in a `UnicodeEncodeError` or `UnicodeDecodeError` traceback. +- A holder member directory the user cannot enter: `validate-family.py` refuses `git-not-runnable` and says to put git on `PATH`. +- Re-running before the commit, after the `git rm` that `member-gitlink-unnamed` prints: the finding repeats, and the same command then fails. +- A shape-pin `files:` row with no `path:`: the shipped validators report the path `None` as missing, instead of refusing the row. +- A YAML key written twice: the reader keeps the last one without a word. + +### [shape-doctor.py](https://github.com/opensoft/openRepoShape/issues/460) + +- #342: A `.pth` file in the operator's Python setup that reaches a `repo_shape.py`: the `manifest` row passes for a root that has lost its own copy, and CI then fails. +- #408: A standard whose own `contracts/path-classification.yaml` does not load: a prose remedy is printed as the NEXT command, and zsh runs it. +- #410: A crash of the standard's own naming script, or a directory name that is not UTF-8: the doctor reports `INVALID (naming)` about the project. +- #413: A leg or member whose detached HEAD holds the only copy of a commit: pasting the `legs` or `members` NEXT moves it, so only the reflog keeps the commit. +- #414: A mount with no `.git`, an unborn HEAD, no `.gitmodules` section, or a pin that is not 40 hex: the `legs` NEXT is the wrong command, or leaves the row red. +- #432: A family member URL in `.gitmodules` that is not UTF-8: the doctor ends in a traceback with no verdict. +- #439: A registered-kind file at a path no check reads (such as `contracts/project.yaml` in an assembly root): the doctor counts it validated and answers `COMPLIANT`. +- #447: A validator killed by a signal, or one that exits with nothing printed: the doctor reports a finding about the project, not a crash. +- #448: A `WARNING` from the project's own `validate-manifest.py`, such as an unreachable referent chain: the `manifest` row says `ok`, and the warning is not shown. + +### [The test suite, CI and the docs](https://github.com/opensoft/openRepoShape/issues/461) + +- #415: A Windows root whose path holds a space: no test runs the PowerShell form of the leg NEXT line, and its quoting test passes only because temporary paths need no quoting. +- #429: Looking up `apply --add`'s refusals in the README or the handbook: `update-add-symlink` and `update-add-not-a-directory` are not listed. +- #430: A directory the user cannot search, on Python 3.14: the shipped tools treat it as absent, and skip it or misreport it instead of refusing. +- A test fixture that clones a just-pushed local remote by plain path (about a dozen of them, and `siblings.py`): it can race git's background maintenance. None has failed in CI. +- An unreadable root pin in the doctor's tests: one test still accepts exit 3, where only exit 1 (`INVALID`) is correct. +- Two test modules that load the family template's scripts: they leave `.pyc` files in the template tree, so a test that reads every template file could pass or fail by order. + +## Before tagging (remove at tag time) + + + +Open `v1.0.0` milestone issues at drafting time: + +- #273: Adopting with `plan --pin ` gives a root whose `validate-pins.py` refuses `neutral-pin-missing`. +- #277: A doctor NEXT pasted into macOS's zsh passes its `# comment` as arguments. +- #291: A damaged pin is told "remove the file from the root". +- #305: A source's own `project.yaml` kept in the root adopts into NOT A SHAPE ROOT. +- #307: `family.py init` pushes a holder its own validator refuses for some values. +- #320: Re-running after a failed verification meets the leftover split branch. +- #325: `update-shape.py` NEXT lines print the root unquoted. +- #340: `adopt-project.py plan --help` does not describe `--spec-path` and `--code-path`. +- #370: A family holder's refusals can print an assembly root's remedy. +- #405: `family.py bump --to` an annotated tag's id commits a holder its validator rejects. +- #411: Docs: a descendant project's bootstrap, `bump-leg.py` and `apply` call `gh api`, though the README says offline. +- #424: A root in the middle of a merge reads `COMPLIANT`. +- #436: `apply --branch` over an uncommitted in-place `apply` can commit a red pin or make no branch. +- #440: On the macOS runner, cloning a just-pushed local bare remote races git's background maintenance. +- #462: After a ruleset refuses the first push, neither exit the hint offers can finish. +- #471: The Windows CI job never runs an adoption, because `git-filter-repo` is not installed there, so adoption on Windows is unverified. +- #472: Nothing in the repository yet says what the release covers or lists its known limits. + +## Full list + +- Merged pull requests: +- The `v1.0.0` milestone: From d0d458fd40235342cc7b011a51b1b8bffde4ac0e Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Mon, 12 Oct 2026 22:59:01 +0000 Subject: [PATCH 2/7] docs/release-notes/v1.0.0.md: brought up to main 437a360, so the notes cite #343, #401 and #456, drop the known limits main no longer has (#290, #391, #225, #294), list all 30 open milestone issues, state the Windows park-and-resume exception beside the 1.0 promise, and give a pin procedure that pastes into macOS's zsh (#472) A fact-check of cbe44fe found nothing wrong at the drafting sha, but main had moved. Three PRs landed and fourteen milestone issues were filed in the forty-five minutes after the draft, and several sentences were true only with a qualifier the notes had dropped. This commit refreshes the notes against 437a360 and takes the checker's corrections. Refresh: - Section 5 adds #343 (`update-pin-row` and `shape-pin-row` for a shape pin whose `files:` is not a list), #401 (`plan --help` for `--spec-path` and `--code-path`) and #456 (`make bootstrap` escapes what the console cannot print). - Section 6 drops #290 and #391, which those PRs closed. - Section 6 also drops #225 and #294. Both are still open, but offline probes at 437a360 show #314 fixed them: - `scaffold-project.py --spec-path null --dry-run` refuses `scaffold-manifest-invalid` before anything exists; - `adopt-project.py check` on a plan with `elected_on: someday` and no `reference:` refuses `election-date-malformed`. - #181 now names the input that still reaches it, a source file named `null`. The probe showed `plan` writing `path: null` and `check` then finding the path uncovered. - New unfiled checklist items join section 6: - #458's `ImportError` item, now that #456 is on main; - #457's L2-6; - the `CONTRACTS_DIR` item; - four adoption inputs that cannot be tested on Windows. - Section 7 is rebuilt from the milestone, which now holds 30 open issues. #340 is closed, so its line is gone. Corrections: - **Section 1** states the promise as ruled, then says that on Windows park and resume run under WSL2, because native Windows parks nothing (README). - **The Windows CI sentence** says the adoption suites skip there until #471 lands, with a comment to drop the qualifier then. - **The pin procedure** ends its push line without a `# comment`. An interactive zsh without `interactivecomments` would hand the comment's words to `git push` as refspecs, the class #277 reports. The pull request step is in prose after the block. The section also says that the shape pin always records a commit, and that `apply` counts the files it re-copies before it asks. - **The YAML reader** sentence names `yaml-unparsable` beside `yaml-unreadable`, and claims no more than the reader does. - **The doctor** sentence keeps AGENTS.md's qualifier, "into the repository", and says that outside a checkout it clones the standard first. - **#303** gives both outcomes. **#389** is described as the gap that #305's refusal covers. - **The hermetic-tests** sentence claims only what its PRs and AGENTS.md establish: no test reaches the real `gh`, and the suite scaffolds into local bare repositories. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 --- docs/release-notes/v1.0.0.md | 132 +++++++++++++++++++++++------------ 1 file changed, 87 insertions(+), 45 deletions(-) diff --git a/docs/release-notes/v1.0.0.md b/docs/release-notes/v1.0.0.md index 11497a33..ff4c0ea8 100644 --- a/docs/release-notes/v1.0.0.md +++ b/docs/release-notes/v1.0.0.md @@ -6,9 +6,11 @@ each other, how one clone brings the whole project down, and what an AI assistant does when asked to scaffold one. Its preferred shape, the Triad, is an assembly root that mounts a spec leg and a code leg. The shape is elective and confers nothing. Version 1.0 promises that every procedure in AGENTS.md works -end to end for ordinary repositories on Linux, macOS and Windows. It is tagged -only when every in-scope issue is closed and the full test suite passes on all -three systems on the release commit. Edge cases that only unusual input reaches +end to end for ordinary repositories on Linux, macOS and Windows. On Windows, +`make park` and `make resume` run under WSL2, as the README says: native +Windows parks nothing. The release is tagged only when every in-scope issue is +closed and the full test suite passes on all three systems on the release +commit. Edge cases that only unusual input reaches are listed below as known limits. ## What 1.0 contains @@ -39,32 +41,37 @@ are listed below as known limits. - **Linux** and **macOS** are supported, and the stock macOS `/bin/bash` 3.2 is enough. **Native Windows** uses `setup-project.py`, after `git config --global core.autocrlf false`. **WSL2** runs the bash entry - points. Park and resume do not work on native Windows. + points. Park and resume do not work on native Windows; run them under + WSL2. - CI runs the full suite on `ubuntu-latest`, `macos-latest` and - `windows-latest`. - + `windows-latest`. On Windows the adoption suites skip, because + `git-filter-repo` is not installed there (#471). + ## How to pin 1.0.0 -Projects pin the standard by commit in `contracts/shape-pin.yaml`. The README's -rule is that a tag is never the referent, because a tag can be moved and a -commit cannot. Pin ``, not the tag. +A project pins the standard in `contracts/shape-pin.yaml`, which always +records a 40-hex commit. Name `` itself rather than the tag: a +tag can be moved and a commit cannot, as the README and AGENTS.md say of every +pin. - **New project:** run `OPENREPOSHAPE_REF= openRepoShape --org `. The command forwards `--shape-ref ` to `setup.sh`. Outside a checkout, `setup.sh` and `setup-project.py` take `--shape-ref` directly. - **Existing project or family holder:** run these from a checkout of the - standard. `apply` shows what it will re-copy and asks `Type yes to proceed:` - before it writes anything: + standard. `check` lists the files that change. `apply` says how many it + will re-copy and asks `Type yes to proceed:` before it writes anything: ```sh ./update-shape.py check --root --at ./update-shape.py apply --root --at \ --branch shape/update- - git -C push -u origin shape/update- # then open a pull request + git -C push -u origin shape/update- ``` + Then open a pull request from that branch. + - **Confirm:** run `./shape-doctor.py --root ` from a checkout at ``. `COMPLIANT`, exit 0, means the pin names that commit. @@ -75,8 +82,9 @@ commit cannot. Pin ``, not the tag. - **In-place adoption.** The repository becomes the assembly root and keeps its name and history. The legs are extracted with `git filter-repo`, and the split lands as one pull request. Every source path is verified by blob sha. - The plan asks about any path it cannot place, and an empty leg is seeded with - `--allow-empty-leg` (#5, #16, #17, #24). + The plan asks about any path it cannot place, an empty leg is seeded with + `--allow-empty-leg`, and the `xf-project-` topic is set + (#5, #16, #17, #24). - **Refused before any leg exists.** `execute` refuses everything `check` finds before it creates a leg. That covers bad mount paths, root paths that clash with the shape's files, values no file can hold @@ -86,8 +94,8 @@ commit cannot. Pin ``, not the tag. - **Submodules.** `.gitmodules` stays in one leg with its submodules. An unreadable one is a `plan-gitmodules-unreadable` finding, not a crash, and relative URLs are checked from both sides. Verification accepts no root - gitlink except the two leg mounts (`adopt-root-submodule`, - `adopt-mount-commit` and others) + gitlink but the two leg mounts, each at the commit pushed to its leg + (`adopt-root-submodule`, `adopt-mount-commit` and others) (#162, #173, #194, #226, #240, #245, #268). - **Ignore rules and odd names.** Shape files are committed even when the source's `.gitignore` matches them, and leg mounts use `--force`. Findings @@ -105,8 +113,9 @@ commit cannot. Pin ``, not the tag. (`unchanged`, `upstream-changed`, `locally-modified` or `both`) and names files the standard has added (`upstream-added`). It exits 1 when there is work to do. `apply` re-copies and re-pins, and rolls back if the project's - validators fail. `--add` and `--accept-local` take one file at a time. - Family holders, and their README's `Shape:` line, update the same way + validators fail. `--add` and `--accept-local` each name one file and can be + repeated. Family holders, and their README's `Shape:` line, update the same + way, and the commit and refusals name the holder's own manifest (#10, #29, #47, #52, #85, #152, #159). - **Landing.** `apply --branch` takes `--trailer` lines. The `gh pr create` it prints targets the root's own branch and repository. A failed push keeps the @@ -115,9 +124,11 @@ commit cannot. Pin ``, not the tag. (#112, #218, #227, #379). - **Clean rollback.** A failure before the commit restores everything. Additions through links or under files are refused (`update-add-symlink`, - `update-add-not-a-directory`). Pin rewrites keep layout and comments, and - non-ASCII project names work under cp1252 - (#224, #233, #269, #281, #346, #380). + `update-add-not-a-directory`). A shape pin whose `files:` is not a list is + refused `update-pin-row`, and the project's validators report + `shape-pin-row`, where all of them ended in a traceback. Pin rewrites keep + layout and comments, and non-ASCII project names work under cp1252 + (#224, #233, #269, #281, #343, #346, #380). - **bump-leg.py.** One commit moves a leg's gitlink, pin and workflow references, and `--dry-run` shows `old -> new` and the trailers first. Untracked workflows are refused (`bump-leg-workflow-untracked`), and @@ -155,25 +166,33 @@ commit cannot. Pin ``, not the tag. workflow reads private legs with a GitHub App or `SHAPE_LEGS_TOKEN`, and carries on with a warning without one. `validate-pins.py` rechecks neutral-product pins offline where it can, and refuses `git-not-runnable` - (exit 2) (#1, #7, #8, #12, #13, #15, #18, #25, #30, #58, #154, #312, #446). + (exit 2). `make bootstrap` escapes what the console cannot print, so a + `name` such as `日本` under `PYTHONIOENCODING=ascii` no longer ends it in a + traceback + (#1, #7, #8, #12, #13, #15, #18, #25, #30, #58, #154, #312, #446, #456). - **Families.** A holder pins member roots under `members/`, and `make siblings` clones working copies beside it without moving anything. `add`, `bump` and `remove` have `--dry-run`. They check before they write, and restore the holder on failure. `add` refuses duplicates by name or id - (`member-duplicate-id`). A repeated `bump` says `nothing to do` and exits 1 + (`member-already-present`, `member-duplicate-id`). A repeated `bump` says + `nothing to do` and exits 1 (#16, #79, #219, #221, #236, #308, #315, #317, #318, #345, #350, #381, #399, #400, #406, #416). - **Park and resume.** `make park` and `make resume` hand off to the Speckit git extension. The installed commands moved to opensoft/openRepoTools (#80, #83, #93, #94). -- **Shared helpers.** The YAML reader refuses what it cannot read exactly - (`yaml-unreadable`). Output from git and `gh` is read as UTF-8 under any - locale. "Same repository" now has one definition +- **Shared helpers.** The YAML reader refuses a file it cannot open or + decode as UTF-8 (`yaml-unreadable`) and text it cannot parse + (`yaml-unparsable`), and several flow and quoting forms it used to misread + are now read exactly or refused. Output from git and `gh` is read as UTF-8 + under any locale. "Same repository" now has one definition (#156, #160, #193, #230, #244, #257, #265, #284, #390). ### shape-doctor.py -- **One verdict.** `openRepoShape --doctor` writes nothing and fetches - nothing. It prints a row per check, then one verdict: +- **One verdict.** `openRepoShape --doctor` writes nothing into the + repository and fetches nothing into it. Run outside a checkout of the + standard, it first clones the standard into a temporary directory. It prints + a row per check, then one verdict: - `COMPLIANT`: exit 0; - `COMPLIANT, SHAPE BEHIND`, `DRIFTED`, `MISPLACED` or `INVALID`: exit 1; - `NOT A SHAPE ROOT`: exit 2; @@ -196,18 +215,25 @@ commit cannot. Pin ``, not the tag. ### The test suite, CI and the docs - **Three operating systems.** Every pull request runs the full suite on - Linux, macOS and Windows. Every push to `main` runs it on Linux + Linux, macOS and Windows, though on Windows the adoption suites skip until + #471 is fixed. Every push to `main` runs it on Linux. actionlint checks this + repository's workflow and both shipped ones (#9, #11, #53, #57, #124, #129, #336). - -- **Hermetic tests.** No test reaches the real `gh` or the network - (#64, #116, #120, #128, #131, #274, #310, #335, #349). + +- **Hermetic tests.** No test reaches the real `gh` (#274, #349), and the + suite scaffolds into local bare repositories, never GitHub. Tests no longer + depend on the host's paths, pin-source variables or git identity, and a + failing assert names what broke (#64, #116, #120, #128, #131, #310, #335). - **Docs.** The README has a quick start for each platform, and `docs/handbook.html` is a one-page reading of it. `docs/cli.md` is generated from every tool's `--help`. A test ties each refusal code AGENTS.md names to - the tool that prints it - (#23, #46, #70, #71, #73, #88, #90, #98, #161, #309, #396). -- **Maintainability.** Static-analysis findings were fixed and long functions - split, with no change in behaviour + the tool that prints it, and the docs name `$AGENT_PROTOCOL_ROOT` rather + than a fixed path. `adopt-project.py plan --help` says what `--spec-path` + and `--code-path` are, their defaults, and the two refusals `plan` makes on + them (`adopt-bad-leg-path`, `adopt-leg-path-collides`) + (#23, #46, #70, #71, #73, #88, #90, #98, #161, #309, #396, #401). +- **Maintainability.** Static-analysis findings were fixed, long functions + split and docstrings corrected, with no change in behaviour (#67, #117, #121, #125, #139, #140, #141, #142, #143, #144, #145, #398). openRepoShape itself stays a single repository (#372). @@ -219,16 +245,15 @@ limit stays open under its area's tracking issue. ### [adopt-project.py](https://github.com/opensoft/openRepoShape/issues/457) - #178: A dangling or outward symlink the root keeps at or above a path the shape writes: `check` passes, then `execute` crashes or writes outside the assembly after both legs are pushed. -- #181: `plan --spec-path null`, or another YAML keyword: the plan writes it unquoted, and the leg is mounted at `spec`. +- #181: A source holding a file or directory named `null`, `true` or `false`: `plan` writes the name unquoted, so `check` finds the path in no entry, and an entry that covers nothing, until the plan is fixed by hand. - #183: A root-kept symlink to elsewhere on disk, or a link loop: the shape's files are written through the link, or the run crashes, after both legs are pushed. - #189: A source with a submodule kept in a leg: the doctor can only note it, because the assembly does not record which leg was chosen. - #293: A hand-edited `follow_ups:` that is not a list: `check` and `execute` end in a `TypeError` traceback, `execute` after both legs exist. -- #294: A plan with a bad `elected_on:` and no `reference:`: `check` says `plan ok`, and `execute` refuses `election-date-malformed` only after both legs exist. -- #303: A source symlink with an empty target: `check` passes, and `execute` fails after both legs exist. +- #303: A source symlink with an empty target: `check` passes. With symlinks on, `execute` fails after both legs exist; with `core.symlinks=false`, it says `adoption verified` over a tree a clone with symlinks on cannot check out. - #328: A hand-edited `question:`, or a submodule name, holding a line break or ESC: it is printed raw in a finding, and can forge a line. - #386: A hand-edited plan field of the wrong type (`pins:`, `paths:`, `legs:`, `source:`): `check` and `execute` crash, and `pins: openGlass` adopts with one-letter pins. - #388: A source symlink whose target begins with a NUL byte: `check` passes, and `execute` fails after both legs exist, with nothing on stderr. -- #389: A plan that keeps the source's own `project.yaml` in the root: verification never checks the root manifest, so it can say `adoption verified` over an assembly the doctor calls NOT A SHAPE ROOT. +- #389: Verification never checks the root manifest itself. Only `check`'s refusal of a plan that keeps the source's own `project.yaml` in the root (#305) stands between such a plan and `adoption verified`. Once #305 is fixed, no input reaches the gap through `execute`. - #417: A relative submodule URL under `--local-remote-dir`: `check` and `execute` resolve it from different remotes, so one can refuse what the other passes. - #418: A kept submodule whose URL names no repository: `adoption verified`, then the recursive clone the `NEXT:` line prints fails. - #420: A branch name git refuses (`~`, `a..b`, `x.lock`): `plan` and `check` pass, and `execute` dies after a leg exists. @@ -243,11 +268,16 @@ limit stays open under its area's tracking issue. - A name holding a backtick or a double quote, or wrapped in backticks: its spelled form can name another entry, or make the plan unreadable. - A path policy whose `question:` or rule id holds control characters: `plan`'s terminal report prints them raw. - A hand-edited `tracking_branch:` or `adopt_branch:` such as `a=b`: `check` says `plan ok`, and `execute` refuses `unsafe-value` before any leg exists. +- A hand-edited plan with a bad `id:` or `org:` beside a bad leg path: `check` and `execute` name different problems first. No leg is made either way. +- A project adopted with a `--spec-path` other than `spec`: the root Makefile still exports `CONTRACTS_DIR ?= $(CURDIR)/spec/contracts`, so the code leg's tooling points at a directory that does not exist. +- On Windows, a source path holding a character NTFS refuses (a TAB, a control character): it cannot be checked out there, so adopting it is untested on Windows. +- On Windows, a source path that is not UTF-8: it cannot be created there, so adopting it is untested on Windows. +- On Windows, a source tree name holding a newline, a TAB or a backslash: its submodule plan is untested on Windows. +- On Windows, a source that tracks symlinks, without `core.symlinks=true`: the links check out as plain files, so adopting it is untested on Windows. ### [update-shape.py and bump-leg.py](https://github.com/opensoft/openRepoShape/issues/458) - #286: A symlink the root keeps at a pinned path: `apply` writes the standard's bytes through it, possibly outside the root, and exits 0. -- #290: A shape pin whose `files:` is not a list (`5`, `true`): the doctor, `update-shape.py` and the project's validators end in a `TypeError` traceback. - #422: A pinned file kept as a hard link: `apply` rewrites it through its other name, which may lie outside the root. - #423: A file name that is not UTF-8 under `core.quotePath=false`: `bump-leg.py`, the scaffold and `family.py init` end in a `UnicodeDecodeError` traceback, not a refusal or warning. - #428: An `apply` whose rollback cannot put one file back (an unwritable copy, a full disk): an `OSError` traceback, with the remaining files left unrestored. @@ -255,11 +285,11 @@ limit stays open under its area's tracking issue. - #437: A trailing comment on the pin's `files:` or the manifest's `shape:` line: `apply` refuses `update-pin-unrecognised` or `update-manifest-no-shape`, though `check` sent you there. - #438: A leg pin written as `digests: {tree_sha256: …}`: `bump-leg.py --dry-run` exits 0, and the real run refuses `bump-leg-pin-unrecognised`. - `bump-leg.py --to` an annotated tag's own id: the dry run shows the tag id and exits 0, while the real run is refused and rolled back with exit 2. +- A locally edited `scripts/repo_shape.py` kept with `apply --accept-local` while taking the new `bootstrap.py`: `make bootstrap` ends in an `ImportError`, though `apply`'s validators pass and `check` then reads everything unchanged. ### [setup, scaffold, family and the shared helpers](https://github.com/opensoft/openRepoShape/issues/459) -- #225: A leg path YAML reads as another type (`--spec-path null`, `true`, `2026`): all three repositories are created, and the project's bootstrap fails `manifest-leg-path`. -- #259: A value YAML reads as another type (`007`, `2026`, `true`, `null`) in one of eight template fields: the project misreads it, or the tool refuses it. +- #259: A project id, tracking branch or leg path that YAML reads as another type (`007`, `2026`, `true`): the scaffold and `adopt-project.py` refuse it, because the templates write eight placeholders unquoted, so a numeric branch name cannot be used. - #275: Two neutral products whose names differ only in `-`, `_` or `.`: they share one `SHAPE_PIN_SOURCE_` variable, and one gets a `neutral-pin-source-unreadable` finding. - #322: A non-ASCII `--trailer` under the C locale, on a git without `commit --trailer`: the trailer is committed with `?` for each byte. - #324: `--tracking-branch trunk` in an organisation whose ruleset refuses the push: the printed hint names `main`, and targets the wrong base. @@ -269,7 +299,6 @@ limit stays open under its area's tracking issue. - #362: A holder index holding `Members/…`, a case variant of `members/`, on macOS or Windows: `family.py add` cannot add any member. - #363: An uncommitted `git rm -r Members` on macOS or Windows: `add` commits the gitlink as `Members/

`, and fresh clones leave the member empty. - #364: An untracked `Members/

` directory: refused `member-path-taken` on macOS and Windows, but mounted beside it on Linux. -- #391: A manifest `name` the console cannot print, such as a lone surrogate: `make bootstrap` ends in a `UnicodeEncodeError` traceback. - #392: A flow collection with an empty entry or a leading `:`, `#`, `- ` or `? ` (`[,a]`, `[:]`): the YAML reader accepts it, and reads `[a: b]` as a string. - #393: A plain value that opens with `]`, `}`, `,`, `@`, a backtick or `%`: the YAML reader takes it as text, where YAML refuses it. - #407: An uncommitted deletion of the holder's `.gitmodules`: `family.py remove` commits it with the member, and fresh clones leave every member empty. @@ -327,7 +356,6 @@ Open `v1.0.0` milestone issues at drafting time: - #307: `family.py init` pushes a holder its own validator refuses for some values. - #320: Re-running after a failed verification meets the leftover split branch. - #325: `update-shape.py` NEXT lines print the root unquoted. -- #340: `adopt-project.py plan --help` does not describe `--spec-path` and `--code-path`. - #370: A family holder's refusals can print an assembly root's remedy. - #405: `family.py bump --to` an annotated tag's id commits a holder its validator rejects. - #411: Docs: a descendant project's bootstrap, `bump-leg.py` and `apply` call `gh api`, though the README says offline. @@ -337,6 +365,20 @@ Open `v1.0.0` milestone issues at drafting time: - #462: After a ruleset refuses the first push, neither exit the hint offers can finish. - #471: The Windows CI job never runs an adoption, because `git-filter-repo` is not installed there, so adoption on Windows is unverified. - #472: Nothing in the repository yet says what the release covers or lists its known limits. +- #475: After `bump-leg.py`, `make bootstrap` leaves the leg detached: it never fast-forwards a local tracking branch that is behind the new pin. +- #476: `setup.sh` run from an AI assistant's shell, with no terminal, refuses with "Re-run with --yes", the flag AGENTS.md forbids the assistant. +- #477: `bump-leg.py --dry-run` is refused `bump-leg-on-tracking-branch` on every fresh project, before the `old -> new` line. +- #478: A gitlink committed by hand: the doctor's `pins` row and `validate-pins.py` name no command. +- #479: Adopting a source whose default branch is not `main`: both legs are pushed, then the pull request is refused (`--base main`). +- #480: Adopting a source with its own Makefile: `execute`'s NEXT `make bootstrap` fails until a person merges `shape/Makefile`. +- #481: A refused pull request stops `execute` before the `xf-project-` topic is set, and the way out never says so. +- #483: An accuracy pass over README.md and AGENTS.md. +- #485: With the shape pin and a pinned copy both conflicted, pasting the doctor's NEXT drops a person's `--accept-local` edit, and the doctor then says `COMPLIANT`. +- #486: `family.py` and `siblings.py` print commands with unquoted paths. Under a folder with a space, a remedy's `rm -rf` would delete the wrong directory. +- #487: `family.py add`, `bump` and `remove` commit straight onto the holder's tracking branch, and `add`'s NEXT pushes a branch that does not exist. +- #488: `family.py init`, as AGENTS.md spells it, lands the holder inside the openRepoShape checkout, and its NEXT prints a command that cannot run. +- #489: Updating a holder cut before `siblings.py` existed, without `--add scripts/siblings.py`, breaks `make siblings`, `park` and `resume`. +- #492: `setup.sh`'s documented `curl … | bash` one-liner never asks for the yes, even in a person's own terminal, because its stdin is the pipe. ## Full list From 67360ccd30af329698ec74d505af3a62a34cced5 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Tue, 13 Oct 2026 21:48:46 +0000 Subject: [PATCH 3/7] docs/release-notes/v1.0.0.md: brought up to main d41cc29, so section 5 cites the 34 pull requests merged since #343, section 6 drops the 13 known-limit issues that closed and adds the 63 checklist lines the five parents gained, section 7 lists the 12 open milestone issues, and the preflight sentence offers only what the preflight offers (#472) Refresh round 1 of the draft, against main d41cc29 (#402, merged 2026-10-13T21:17:39Z). GitHub was read between 21:23 and 21:43 UTC: the merged pull requests, the v1.0.0 milestone open and closed, every open issue, and the five tracking parents with their sub-issues, bodies and comments. Every merge commit of the 217 merged pull requests is on main. Section 5 places each of the 34 pull requests merged after #343 (2026-10-12T22:31:20Z) in its area, from its own body, one clause each; all 217 merged pull requests are now cited, with #16, #53 and #57 still in two areas. Three earlier sentences are made true of main: `check` has more verdicts than the four it named, and #465's docs and #505's accuracy pass show that `validate-pins.py` may ask `gh api` and that `openRepoShape --preflight` offers only `gh`, its login and its credential helper (`setup-project.py --preflight` also offers `git`). Section 6 lists the 47 open, unmilestoned sub-issues (it listed 60). It drops #189, #293, #303, #328, #386, #286, #259, #275, #322, #324, #338, #339 and #359, which closed. #389 is reworded now that #305's refusal has landed. #420 and the `a=b` line are narrowed to a hand-edited `adopt_branch:`, because an offline probe on d41cc29 shows #493's `plan-tracking-branch-missing` now stops a bad `tracking_branch:` at `check`, with no leg made, while `adopt_branch: "a..b"` still passes `check` and fails after both legs exist. The family annotated-tag line says `bump --to` now refuses one (#466). The 63 new lines are the parents' checklist lines posted or added since the first build, each checked against the code at d41cc29. Left out, and said so in the refresh record: docstring, comment and wording nits (as L2-7 was), lines that describe a branch not on main (#504's, #507's, #511's, #515's and #517's), lines filed into the milestone (#485, #514, #516), and three with no effect a person meets. Section 7 lists the 12 open milestone issues; 24 of the 30 it listed have closed. Lane 4's #510, #511, #512 and #513 have not landed, so #471, #499, #500, #501 and #502 stay open here and the Windows qualifier stays. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 --- docs/release-notes/v1.0.0.md | 277 +++++++++++++++++++++++------------ 1 file changed, 183 insertions(+), 94 deletions(-) diff --git a/docs/release-notes/v1.0.0.md b/docs/release-notes/v1.0.0.md index ff4c0ea8..70d8d52f 100644 --- a/docs/release-notes/v1.0.0.md +++ b/docs/release-notes/v1.0.0.md @@ -36,8 +36,9 @@ are listed below as known limits. - `gh`, logged in, wherever a tool creates repositories or pull requests on GitHub. `git-filter-repo` is needed for adoption only. `make` is optional. - `openRepoShape --preflight` checks the machine and creates nothing. On - Windows, run `py setup-project.py --preflight`. It offers each missing - install behind a typed `yes`. + Windows, run `py setup-project.py --preflight`. It names what is missing, + and offers to install `gh`, log it in and set up its credential helper, + each behind a typed `yes`. `setup-project.py --preflight` also offers `git`. - **Linux** and **macOS** are supported, and the stock macOS `/bin/bash` 3.2 is enough. **Native Windows** uses `setup-project.py`, after `git config --global core.autocrlf false`. **WSL2** runs the bash entry @@ -85,23 +86,38 @@ pin. The plan asks about any path it cannot place, an empty leg is seeded with `--allow-empty-leg`, and the `xf-project-` topic is set (#5, #16, #17, #24). +- **Branches and pins.** The tracking branch defaults to the source's default + branch, and `check` and `execute` refuse one that is no branch of the + source (`plan-tracking-branch-missing`). The topic is set before the pull + request, and NEXT names `scripts/bootstrap.py` when the source kept its own + Makefile (#493). `plan --pin` takes `@`, and `execute` + writes the pin file the scaffold writes, so the adopted root's + `validate-pins.py` passes; a `--pin-source` that cannot answer is + `pin-source-unreadable` (#508). - **Refused before any leg exists.** `execute` refuses everything `check` finds before it creates a leg. That covers bad mount paths, root paths that - clash with the shape's files, values no file can hold + clash with the shape's files, the source's own `project.yaml` kept in the + root, a symlink with an empty target, values no file can hold (`adopt-unwritable-value`), and paths it cannot extract, which `plan` now - asks about - (#170, #187, #198, #231, #270, #313, #353, #377). + asks about. A split branch an earlier run left, or a branch in its way, is + `adopt-branch-exists` or `plan-split-branch-taken`, and a hand-edited key + of the wrong type, or a date the tools cannot read, is a finding, not a + traceback + (#170, #187, #198, #231, #270, #313, #353, #356, #377, #382, #387, #435). - **Submodules.** `.gitmodules` stays in one leg with its submodules. An unreadable one is a `plan-gitmodules-unreadable` finding, not a crash, and relative URLs are checked from both sides. Verification accepts no root gitlink but the two leg mounts, each at the commit pushed to its leg - (`adopt-root-submodule`, `adopt-mount-commit` and others) - (#162, #173, #194, #226, #240, #245, #268). + (`adopt-root-submodule`, `adopt-mount-commit` and others). `execute` + records in `project.yaml` the leg each kept submodule went to, and the + doctor holds it there + (#162, #173, #194, #226, #240, #245, #268, #394). - **Ignore rules and odd names.** Shape files are committed even when the source's `.gitignore` matches them, and leg mounts use `--force`. Findings - spell out control characters and bytes that are not UTF-8, follow-up - searches take paths literally, and a cp1252 console gets escaped names - (#182, #200, #223, #228, #246, #247, #378). + spell out control characters and bytes that are not UTF-8, in a + hand-edited `question:` and a submodule's name too, follow-up searches take + paths literally, and a cp1252 console gets escaped names + (#182, #200, #223, #228, #246, #247, #378, #384). - **Late failures.** After a leg exists, a failure prints `REFUSED .` and what the run left behind (#445). Exit 1 means the verification failed. Exit 2 with `IS pushed` is finished by hand with the command it prints, @@ -110,30 +126,40 @@ pin. ### update-shape.py and bump-leg.py - **Re-syncing copies.** `check` gives each pinned file a verdict - (`unchanged`, `upstream-changed`, `locally-modified` or `both`) and names - files the standard has added (`upstream-added`). It exits 1 when there is - work to do. `apply` re-copies and re-pins, and rolls back if the project's - validators fail. `--add` and `--accept-local` each name one file and can be - repeated. Family holders, and their README's `Shape:` line, update the same + (`unchanged`, `upstream-changed`, `locally-modified`, `both` and others) + and names files the standard has added (`upstream-added`). It exits 1 when + there is work to do. `apply` re-copies and re-pins, and rolls back if the + project's validators fail. `--add` and `--accept-local` each name one file + and can be repeated. Family holders, and their README's `Shape:` line, update the same way, and the commit and refusals name the holder's own manifest (#10, #29, #47, #52, #85, #152, #159). -- **Landing.** `apply --branch` takes `--trailer` lines. The `gh pr create` it - prints targets the root's own branch and repository. A failed push keeps the - commit and prints a NEXT line. A later `--pr` run opens the pull request, - unless the tree has changed since (`update-rerun-uncommitted`) - (#112, #218, #227, #379). +- **Landing.** `apply --branch` takes `--trailer` lines, and a non-ASCII + trailer is committed as typed under any locale. Before it writes, + `apply --branch` refuses a pin, manifest or copy that differs from HEAD + (`update-branch-uncommitted`). The `gh pr create` it prints targets the + root's own branch and repository, and every NEXT line quotes its paths for + the shell. A failed push keeps the commit and prints a NEXT line. A later + `--pr` run opens the pull request, unless the tree has changed since + (`update-rerun-uncommitted`) + (#112, #218, #227, #374, #376, #379, #474). - **Clean rollback.** A failure before the commit restores everything. Additions through links or under files are refused (`update-add-symlink`, - `update-add-not-a-directory`). A shape pin whose `files:` is not a list is + `update-add-not-a-directory`), and so is a rewrite of a copy, the pin or + the manifest kept as a symlink (`update-rewrite-symlink`; `check` calls + such a copy `copy-is-link`). A shape pin whose `files:` is not a list is refused `update-pin-row`, and the project's validators report `shape-pin-row`, where all of them ended in a traceback. Pin rewrites keep layout and comments, and non-ASCII project names work under cp1252 - (#224, #233, #269, #281, #343, #346, #380). + (#224, #233, #269, #281, #343, #346, #347, #380). - **bump-leg.py.** One commit moves a leg's gitlink, pin and workflow - references, and `--dry-run` shows `old -> new` and the trailers first. - Untracked workflows are refused (`bump-leg-workflow-untracked`), and - unreadable files are refused by name. Case-only renames are followed, and - any failure rolls back (#35, #151, #222, #264, #344, #352, #403). + references, and `--dry-run` shows `old -> new` and the trailers first. The + dry run also runs on the tracking branch, and names the `git switch -c` the + real run needs. Untracked workflows are refused + (`bump-leg-workflow-untracked`), and unreadable files are refused by name. + Case-only renames are followed, and any failure rolls back. After a bump, + `make bootstrap` moves the leg's tracking branch up to the new pin when + that branch is behind it + (#35, #151, #222, #264, #344, #352, #403, #484, #490). ### setup, scaffold, family and the shared helpers @@ -142,41 +168,60 @@ pin. native Windows way in. `openRepoShape` installs as a command. The preflight offers installs behind a typed `yes`. `--local-remote-dir` rehearses a run with nothing created on GitHub, and `--family` lands the clone in a family - folder. `setup.sh --help` outside a checkout answers without cloning anything - (#2, #3, #14, #37, #53, #56, #57, #60, #62, #65, #75, #78, #452). + folder. `setup.sh --help` outside a checkout answers without cloning + anything. Every question is asked at the person's own terminal, even under + `curl … | bash`; with no terminal at all, the run refuses and names that + terminal as the way on. A clone path that is already taken is refused + before anything is created + (#2, #3, #14, #37, #53, #56, #57, #60, #62, #65, #75, #78, #452, #491, #509). - **Naming and descent.** Names are classified offline against six families. A descendant must pin its `open`, directly or through a recorded chain. The default `reference:` follows the election date. The Triad advisory speaks once and never blocks, and `single-repository.yaml` silences - it (#4, #6, #19, #27, #42, #45, #86, #164). + it. A `--link-source` value is `PRODUCT=PATH` only when its key names a + chain link, and a link source that cannot be entered is a warning, not a + traceback (#4, #6, #19, #27, #42, #45, #86, #164, #453). - **Scaffold refusals before anything exists,** dry runs included: - colliding leg paths (`scaffold-leg-path-collides`); - - pins the shape cannot write (`scaffold-pin-collides`); + - pins the shape cannot write, or two products that share one + `SHAPE_PIN_SOURCE_` variable (`scaffold-pin-collides`); - bad chain links; - manifest values the project would misread, and text no file can hold; - a `--tracking-branch` git would not take as a branch name, here and in `family.py init`. Shape files and leg mounts get past the operator's own ignore rules, and - templates are filled in one pass - (#185, #201, #234, #235, #256, #271, #314, #316, #351, #355). + templates are filled in one pass. They quote every text value they write, + so `--id 007` or `--tracking-branch 2026` reads back as typed, and a leg + path any template line would misread is refused + (#185, #201, #234, #235, #256, #266, #271, #314, #316, #351, #355, #395, #409). +- **Ruleset refusals.** `--reuse-empty-repo` reuses a leg that is empty or + holds only the scaffold's own seed. The ruleset hint offers that one re-run, + by an operator holding the bypass right, and names the run's tracking + branch, not `main` (#373, #506). - **Each project's own gate.** `make bootstrap` places each leg at its pin, and `.gitattributes` keeps LF line endings. `AGENTS-shape.md` carries the rules for assistants, and a `-wip` template ships too. The `validate` workflow reads private legs with a GitHub App or `SHAPE_LEGS_TOKEN`, and carries on with a warning without one. `validate-pins.py` rechecks - neutral-product pins offline where it can, and refuses `git-not-runnable` - (exit 2). `make bootstrap` escapes what the console cannot print, so a - `name` such as `日本` under `PYTHONIOENCODING=ascii` no longer ends it in a - traceback - (#1, #7, #8, #12, #13, #15, #18, #25, #30, #58, #154, #312, #446, #456). + neutral-product pins from a local checkout where it can, else through + `gh api` with the operator's credentials, as the docs now say. It refuses + `git-not-runnable` (exit 2), and a workflow file it cannot read as UTF-8 + is one `workflow-unreadable` refusal naming every such file. + `make bootstrap` escapes what the console cannot print, so a `name` such as + `日本` under `PYTHONIOENCODING=ascii` no longer ends it in a traceback + (#1, #7, #8, #12, #13, #15, #18, #25, #30, #58, #154, #312, #419, #446, #456, #465). - **Families.** A holder pins member roots under `members/`, and `make siblings` clones working copies beside it without moving anything. - `add`, `bump` and `remove` have `--dry-run`. They check before they write, - and restore the holder on failure. `add` refuses duplicates by name or id - (`member-already-present`, `member-duplicate-id`). A repeated `bump` says - `nothing to do` and exits 1 - (#16, #79, #219, #221, #236, #308, #315, #317, #318, #345, #350, #381, #399, #400, #406, #416). + `init` asks the holder's own reader and validator about `family.yaml` + before it creates anything. `add`, `bump` and `remove` have `--dry-run`. + They check before they write, and restore the holder on failure. `add` + refuses duplicates by name or id (`member-already-present`, + `member-duplicate-id`), and `bump --to` an annotated tag, a tree or a blob + is refused `member-to-not-a-commit`. A repeated `bump` says `nothing to do` + and exits 1. `make siblings`, `park` and `resume` refuse by name when + `scripts/siblings.py` is missing + (#16, #79, #219, #221, #236, #308, #315, #317, #318, #345, #350, #381, #397, #399, #400, #406, #416, #466, #503). - **Park and resume.** `make park` and `make resume` hand off to the Speckit git extension. The installed commands moved to opensoft/openRepoTools (#80, #83, #93, #94). @@ -203,14 +248,21 @@ pin. - **Placement.** The `placement` row checks each leg's paths against the adoption policy. `--placement-plan` writes a plan for a person to resolve (#100, #186). -- **NEXT commands you can paste.** They are quoted for sh or PowerShell, use - `Copy-Item -LiteralPath` on Windows, and set the `PYTHONPATH` a validator - needs (#102, #106, #107, #108, #110, #287, #311, #354). +- **NEXT commands you can paste.** A NEXT is the command alone; the sentence + that explains it is a `WHY` line of its own, and `why` in `--json`. They + are quoted for sh or PowerShell, use `Copy-Item -LiteralPath` on Windows, + and set the `PYTHONPATH` a validator needs. A gitlink committed by hand is + offered both repairs, `bump-leg.py --to` the gitlink's commit or the pin's + (#102, #106, #107, #108, #110, #287, #311, #354, #402, #495). - **More accurate verdicts.** An unloadable manifest or shape pin is now - `INVALID`, where it was `NOT A SHAPE ROOT` or `CANNOT ANSWER`. A misplaced - `family.yaml` is a `manifest kinds` finding, and a crashed validator says so. - A fork beside a holder is no longer taken for the member - (#147, #220, #232, #267, #357, #375, #404). + `INVALID`, where it was `NOT A SHAPE ROOT` or `CANNOT ANSWER`, and a root + in the middle of a merge is `INVALID (merge)`, exit 1, where it could read + `COMPLIANT`. A misplaced `family.yaml` is a `manifest kinds` finding, and a + crashed validator says so. A pin that does not load is put back from git, + where the NEXT said to remove it, and a conflicted shape pin is taken + alone, its conflicted copies left to a person to merge. A fork beside a + holder is no longer taken for the member + (#147, #220, #232, #267, #357, #358, #375, #404, #468, #496). ### The test suite, CI and the docs @@ -223,15 +275,20 @@ pin. - **Hermetic tests.** No test reaches the real `gh` (#274, #349), and the suite scaffolds into local bare repositories, never GitHub. Tests no longer depend on the host's paths, pin-source variables or git identity, and a - failing assert names what broke (#64, #116, #120, #128, #131, #310, #335). + failing assert names what broke. The two session fixtures that clone a + scaffold, and `setup-project.py --local-remote-dir`, clone a just-pushed + local remote through git, so git's background maintenance no longer breaks + the clone + (#64, #116, #120, #128, #131, #310, #335, #467). - **Docs.** The README has a quick start for each platform, and `docs/handbook.html` is a one-page reading of it. `docs/cli.md` is generated from every tool's `--help`. A test ties each refusal code AGENTS.md names to the tool that prints it, and the docs name `$AGENT_PROTOCOL_ROOT` rather than a fixed path. `adopt-project.py plan --help` says what `--spec-path` and `--code-path` are, their defaults, and the two refusals `plan` makes on - them (`adopt-bad-leg-path`, `adopt-leg-path-collides`) - (#23, #46, #70, #71, #73, #88, #90, #98, #161, #309, #396, #401). + them (`adopt-bad-leg-path`, `adopt-leg-path-collides`). An accuracy pass + corrected 28 statements in the README and AGENTS.md + (#23, #46, #70, #71, #73, #88, #90, #98, #161, #309, #396, #401, #505). - **Maintainability.** Static-analysis findings were fixed, long functions split and docstrings corrected, with no change in behaviour (#67, #117, #121, #125, #139, #140, #141, #142, #143, #144, #145, #398). @@ -247,16 +304,11 @@ limit stays open under its area's tracking issue. - #178: A dangling or outward symlink the root keeps at or above a path the shape writes: `check` passes, then `execute` crashes or writes outside the assembly after both legs are pushed. - #181: A source holding a file or directory named `null`, `true` or `false`: `plan` writes the name unquoted, so `check` finds the path in no entry, and an entry that covers nothing, until the plan is fixed by hand. - #183: A root-kept symlink to elsewhere on disk, or a link loop: the shape's files are written through the link, or the run crashes, after both legs are pushed. -- #189: A source with a submodule kept in a leg: the doctor can only note it, because the assembly does not record which leg was chosen. -- #293: A hand-edited `follow_ups:` that is not a list: `check` and `execute` end in a `TypeError` traceback, `execute` after both legs exist. -- #303: A source symlink with an empty target: `check` passes. With symlinks on, `execute` fails after both legs exist; with `core.symlinks=false`, it says `adoption verified` over a tree a clone with symlinks on cannot check out. -- #328: A hand-edited `question:`, or a submodule name, holding a line break or ESC: it is printed raw in a finding, and can forge a line. -- #386: A hand-edited plan field of the wrong type (`pins:`, `paths:`, `legs:`, `source:`): `check` and `execute` crash, and `pins: openGlass` adopts with one-letter pins. - #388: A source symlink whose target begins with a NUL byte: `check` passes, and `execute` fails after both legs exist, with nothing on stderr. -- #389: Verification never checks the root manifest itself. Only `check`'s refusal of a plan that keeps the source's own `project.yaml` in the root (#305) stands between such a plan and `adoption verified`. Once #305 is fixed, no input reaches the gap through `execute`. +- #389: A plan that keeps the source's own `project.yaml` in the root: `check` and `execute` now refuse it before any leg exists, but verification never checks the root manifest itself, so nothing behind that refusal would catch it. - #417: A relative submodule URL under `--local-remote-dir`: `check` and `execute` resolve it from different remotes, so one can refuse what the other passes. - #418: A kept submodule whose URL names no repository: `adoption verified`, then the recursive clone the `NEXT:` line prints fails. -- #420: A branch name git refuses (`~`, `a..b`, `x.lock`): `plan` and `check` pass, and `execute` dies after a leg exists. +- #420: A hand-edited `adopt_branch:` that git refuses as a branch name, such as `a..b`: `check` says `plan ok`, and `execute` fails after both legs exist. A bad `tracking_branch:` is caught by `check` as `plan-tracking-branch-missing`. - #425: A source whose default branch name is not UTF-8: `plan`, `check` and `execute` end in a `UnicodeDecodeError` traceback. - #427: An `origin` whose organisation is not ASCII or holds a space: `plan` refuses `unsafe-value: --org`, a flag nobody passed. - #449: A hand edit that makes a neutral pin's `commit:` and `tree_sha256:` disagree: `adoption verified`, then the root's `validate-pins.py` fails `neutral-pin-digest-mismatch`. @@ -267,9 +319,20 @@ limit stays open under its area's tracking issue. - A source branch that differs from the split branch only by letter case, on macOS or Windows: `check` passes, and `execute` fails after both legs exist. - A name holding a backtick or a double quote, or wrapped in backticks: its spelled form can name another entry, or make the plan unreadable. - A path policy whose `question:` or rule id holds control characters: `plan`'s terminal report prints them raw. -- A hand-edited `tracking_branch:` or `adopt_branch:` such as `a=b`: `check` says `plan ok`, and `execute` refuses `unsafe-value` before any leg exists. +- A hand-edited `adopt_branch:` such as `a=b`: `check` says `plan ok`, and `execute` refuses `unsafe-value` before any leg exists. - A hand-edited plan with a bad `id:` or `org:` beside a bad leg path: `check` and `execute` name different problems first. No leg is made either way. - A project adopted with a `--spec-path` other than `spec`: the root Makefile still exports `CONTRACTS_DIR ?= $(CURDIR)/spec/contracts`, so the code leg's tooling points at a directory that does not exist. +- A nested `--code-path`, such as `legs/code` beside `legs/spec`: the cross-leg follow-up names the spec leg `../legs/spec/…` relative to the code leg, where `../spec/…` is right. +- `plan`, `check` or `execute` run without `--work-dir`: each leaves an `openreposhape-adopt-*` directory in the system temp folder, empty or holding the working trees, and nothing removes it. +- `plan --source ` without `--work-dir`, and the temporary clone cleaned before `check` or `execute`: they refuse `source-unclonable`, with text about the repository's name. Passing `--source` again recovers. +- Any adoption: the follow-up `plan` prints, and the split commit lists, says to open the split as a pull request, which `execute` does itself against GitHub, and calls the extracted legs seeded. +- `execute --local-remote-dir`: it ends with `NEXT: review the pull request`, though none was opened. +- An answer in the plan with no `resolution:` beside it: `check` says `plan ok`, though AGENTS.md says such an answer will be read as a finding. +- A verification that fails against GitHub (exit 1): the pull request and the topic are already in place, and neither the report nor AGENTS.md says so. Deleting the split branch, as the way out says, closes the pull request. +- `plan` with no `--elected-by` and no `git config user.name`: it is refused `adopt-manifest-invalid`, naming `--elected-by`, a flag nobody passed. +- A path the plan answered by hand, such as `spec/examples/`: the doctor's `placement` row keeps listing it as `review_required`, because `execute` records only the submodule answers. +- A local source or assembly that hides a branch with `uploadpack.hideRefs` or `transfer.hideRefs`: `check` does not see it, and `execute --local-remote-dir` pushes both legs before the split push fails. +- A submodule moved to the other leg on purpose after adoption: the doctor reads `MISPLACED` from then on, and nothing says how its `submodules:` row is changed. A row whose submodule is gone is never reported. - On Windows, a source path holding a character NTFS refuses (a TAB, a control character): it cannot be checked out there, so adopting it is untested on Windows. - On Windows, a source path that is not UTF-8: it cannot be created there, so adopting it is untested on Windows. - On Windows, a source tree name holding a newline, a TAB or a backslash: its submodule plan is untested on Windows. @@ -277,7 +340,6 @@ limit stays open under its area's tracking issue. ### [update-shape.py and bump-leg.py](https://github.com/opensoft/openRepoShape/issues/458) -- #286: A symlink the root keeps at a pinned path: `apply` writes the standard's bytes through it, possibly outside the root, and exits 0. - #422: A pinned file kept as a hard link: `apply` rewrites it through its other name, which may lie outside the root. - #423: A file name that is not UTF-8 under `core.quotePath=false`: `bump-leg.py`, the scaffold and `family.py init` end in a `UnicodeDecodeError` traceback, not a refusal or warning. - #428: An `apply` whose rollback cannot put one file back (an unwritable copy, a full disk): an `OSError` traceback, with the remaining files left unrestored. @@ -286,16 +348,22 @@ limit stays open under its area's tracking issue. - #438: A leg pin written as `digests: {tree_sha256: …}`: `bump-leg.py --dry-run` exits 0, and the real run refuses `bump-leg-pin-unrecognised`. - `bump-leg.py --to` an annotated tag's own id: the dry run shows the tag id and exits 0, while the real run is refused and rolled back with exit 2. - A locally edited `scripts/repo_shape.py` kept with `apply --accept-local` while taking the new `bootstrap.py`: `make bootstrap` ends in an `ImportError`, though `apply`'s validators pass and `check` then reads everything unchanged. +- `apply` without `--branch`, moving the pin back to what HEAD already commits: it still prints a commit line, and pasted, git answers `nothing to commit`. +- A root under a path holding a space, such as `My Projects/`: the two `switch -c` remedies and the closing push line that `bump-leg.py` prints are unquoted, and paste as another command. +- A root or path holding a space or a shell character: `update-shape.py`'s `update-branch-exists` remedy, its `ADDED UPSTREAM` `--add` list, its `pass --add` note and its `update-local-drift` remedy print unquoted. +- A Windows root whose path holds an apostrophe (`C:\Users\Jane O'Neill\Atlas`): the commands every tool but the doctor prints, the shipped validators' included, are quoted for a POSIX shell, which PowerShell does not read as one argument. +- Every bump: `bump-leg.py` says `make bootstrap` re-places the leg on its tracking branch, which it does only when that branch is at or behind the pin, or ahead of it with no commit of its own. +- A commit made on a detached leg whose branch is already at the pin: `make bootstrap` checks the branch out, leaves the commit on no branch, and says nothing. +- A leg on a branch with commits of its own past the pin: bootstrap's `not moving an existing branch` line says the checkout stays at the pin, though it stays on the branch. +- A checkout git refuses while the leg is already on its branch at the pin, such as behind a stale `index.lock`: `make bootstrap` prints `checked out, and then git said:` though nothing was checked out. +- `bump-leg.py` and `apply` list the paths they staged, not the ones the commit changed, and repairing a gitlink moved by hand prints `X -> X` while the pin moves. +- A `both` file merged by hand and committed: `check`'s NEXT still lacks its `--accept-local`, so the pasted line is refused `update-conflict` again. +- An `update-conflict` refusal: its remedy says to read the upstream bytes with `git show :`, but nothing names the source path. +- A copy re-pinned with `--accept-local`: the commit `apply` writes still says the copies are not hand-edited. +- `apply --branch` without `--pr`: the `gh pr create` it prints carries no `--title`, `--body` or `--fill`, so `gh` asks for them at a prompt. ### [setup, scaffold, family and the shared helpers](https://github.com/opensoft/openRepoShape/issues/459) -- #259: A project id, tracking branch or leg path that YAML reads as another type (`007`, `2026`, `true`): the scaffold and `adopt-project.py` refuse it, because the templates write eight placeholders unquoted, so a numeric branch name cannot be used. -- #275: Two neutral products whose names differ only in `-`, `_` or `.`: they share one `SHAPE_PIN_SOURCE_` variable, and one gets a `neutral-pin-source-unreadable` finding. -- #322: A non-ASCII `--trailer` under the C locale, on a git without `commit --trailer`: the trailer is committed with `?` for each byte. -- #324: `--tracking-branch trunk` in an organisation whose ruleset refuses the push: the printed hint names `main`, and targets the wrong base. -- #338: A `--link-source` path holding `=`, or a mistyped key: the link is skipped in silence, and a source that cannot be entered crashes the naming check. -- #339: A `--pin-source` that does not exist, is a file or cannot be entered: the scaffold ends in a traceback, `--dry-run` included. -- #359: A workflow file that is not UTF-8: the project's `validate-pins.py` ends in a `UnicodeDecodeError` traceback. - #362: A holder index holding `Members/…`, a case variant of `members/`, on macOS or Windows: `family.py add` cannot add any member. - #363: An uncommitted `git rm -r Members` on macOS or Windows: `add` commits the gitlink as `Members/

`, and fresh clones leave the member empty. - #364: An untracked `Members/

` directory: refused `member-path-taken` on macOS and Windows, but mounted beside it on Linux. @@ -314,13 +382,36 @@ limit stays open under its area's tracking issue. - #454: A family `name` holding a lone surrogate, or any run under `PYTHONIOENCODING=ascii`: `make siblings`, `park` and `resume` end in a `UnicodeEncodeError` traceback. - #455: A lone surrogate or NUL in `tracking_branch` or a leg's `branch:`: `make bootstrap` ends in a traceback, while `validate-manifest.py` says `manifest ok`. - `family.py bump --to` a commit whose `project.yaml` `id` another row already pins: `bump` commits, and the holder's validator then refuses `member-duplicate-id`. -- An annotated tag's own id: `family.py add --at` pins the commit it tags, while `bump --to` treats it differently. One rule is still to be chosen. +- An annotated tag's own id: `family.py add --at` pins the commit it tags, while `bump --to` refuses it (`member-to-not-a-commit`). One rule is still to be chosen. - No git on `PATH`: the shipped `bootstrap.py` scripts end in a `FileNotFoundError` traceback. - A directory name that is not UTF-8: `validate-repository-naming.py` ends in a `UnicodeEncodeError` or `UnicodeDecodeError` traceback. - A holder member directory the user cannot enter: `validate-family.py` refuses `git-not-runnable` and says to put git on `PATH`. - Re-running before the commit, after the `git rm` that `member-gitlink-unnamed` prints: the finding repeats, and the same command then fails. - A shape-pin `files:` row with no `path:`: the shipped validators report the path `None` as missing, instead of refusing the row. - A YAML key written twice: the reader keeps the last one without a word. +- `--org OpenSoft`, or any casing of `opensoft` but the lower-case one: the upstream-organisation guard compares exactly and lets it through, though GitHub reads an organisation name in any case. +- A scaffold run without `--work-dir`, which is how `setup.sh` runs it: it leaves its three working trees in a temporary directory. +- The plan a person approves: it names neither the tracking branch nor, in local mode, the visibility. +- A `--pin` passed to setup after `--`: step (4) never asks the naming check about it, so the report says no referent pin is declared, and the plan then says domain-descendant. +- Inside setup, the scaffold's own `NEXT STEPS` block prints just before setup runs those steps itself. +- `--local-remote-dir`: the clone in the scaffold's `NEXT STEPS` fails with `transport 'file' not allowed`, because it lacks the `-c protocol.file.allow=always` that setup passes. +- Step (7)'s by-hand recovery under `--local-remote-dir`: its recursive clone lacks `-c protocol.file.allow=always` too. +- A clone path or `--into` holding a space: setup's closing `cd` and step (7)'s by-hand recovery print the paths unquoted, and the shell splits them. +- `-- --dry-run` passed through setup: step (7)'s refusal says the three repositories WERE created when none were. +- A file standing where `--family` puts the clone's parent folder: step (3) passes, all three repositories are created, and the run ends in a `FileExistsError` traceback. +- `scaffold-project.py --dry-run` against remotes that already exist: it does not ask, so `scaffold-remote-exists` comes only after the yes, before anything is created. +- A printed value holding a C1 control or a direction-override character: setup escapes only C0 controls and DEL, so it reaches the terminal. +- A re-run where `gh` can see a leg that git cannot read: the scaffold refuses with the generic `a git or gh command failed`, not `scaffold-remote-exists`, and on a terminal git can stop to ask for a username. +- A re-run that reuses legs: its visibility notes say the two legs were just created, and can contradict its own per-leg warning. +- A seed found only on `seed/scaffold`, the branch the old ruleset hint made: the re-run accepts it and creates `main` at it, and says nothing about `seed/scaffold`, which on GitHub may stay the leg's default branch. +- A ruleset that refuses `adopt-project.py`'s push: its hint still offers the seed-branch-and-pull-request exit that the scaffold no longer offers. +- A `--pin-source` given as an absolute path: the committed pin file records the scaffolding machine's own directory. +- A lone `shape-copy-drift`: `validate-pins.py` follows it with THE LOCKSTEP RULE, about moving a gitlink, a pin and the workflows together. +- `family.py init --dry-run` against a holder repository that already exists: it exits 0, and the real run refuses `family-remote-exists` after leaving an empty `/` folder. +- `family.py bump --to` a commit not yet pushed to the member's remote: it fails in git's words (`reference is not a tree`), exit 2, and does not say to push first. +- A member whose `tracking_branch` is not its remote's default branch: `make siblings` clones the default branch, not the tracking branch the docs promise. +- `family.py siblings`: it runs the standard checkout's `siblings.py`, not the holder's pinned copy, so a holder pinned at an older shape runs other code than `make siblings` does, and the `siblings-script-missing` remedy says otherwise. +- `make siblings` in a holder whose folder is not named after the family: it clones the members, then prints `mv` lines for the holder alone, so a re-run after them clones the members again. ### [shape-doctor.py](https://github.com/opensoft/openRepoShape/issues/460) @@ -333,6 +424,19 @@ limit stays open under its area's tracking issue. - #439: A registered-kind file at a path no check reads (such as `contracts/project.yaml` in an assembly root): the doctor counts it validated and answers `COMPLIANT`. - #447: A validator killed by a signal, or one that exits with nothing printed: the doctor reports a finding about the project, not a crash. - #448: A `WARNING` from the project's own `validate-manifest.py`, such as an unreachable referent chain: the `manifest` row says `ok`, and the warning is not shown. +- An ordinary git repository: `NOT A SHAPE ROOT` names only the `adopt-project.py plan` command, though AGENTS.md says to report both ways in. +- A machine that is not ready: the `machine` row quotes the preflight's `(see above)` with nothing above it, because the preflight's lines are kept only in `--json`. +- `openRepoShape --help`, and `docs/cli.md` from it: it lists five verdicts, with no `MISPLACED` or `CANNOT ANSWER`, and spells `COMPLIANT SHAPE BEHIND` without the comma the doctor prints. +- The README's `validate-pins.py` paragraph: it no longer says that a digest that recomputes differently is the finding `neutral-pin-digest-mismatch`, exit 1. +- A pinned copy edited and left uncommitted: the `shape currency` NEXT pastes an `apply --branch` that refuses it as `update-branch-uncommitted`, instead of saying to commit or revert the edit first. +- A merge left unfinished inside a leg: the `merge` row reads only the root's own index, so it is not reported. +- A hand-edited `@` in a workflow, or a leg pin whose digest no longer matches: `validate-pins.py` ends the finding at THE LOCKSTEP RULE and names no command. +- A passing `validate-pins.py` that prints a note, such as `neutral-pin-source-collides`: the `pins` row says `ok` and drops the note. +- A NEXT argument that begins with `=`, such as a branch named `=x`: the doctor leaves it bare, and zsh expands a bare `=word`. +- A conflicted shape pin whose conflicted copies neither side edited: each copy goes to a person to merge by hand, and keeping both sides of a hunk, then `--accept-local`, pins a stale line as the project's own edit. +- A leg pin broken in the bump's own commit: the `manifest kinds` NEXT restores the pin from before the bump, and the doctor then reads `DRIFTED`. Nothing says to commit it and run the `pins` row's `bump-leg.py` line. +- An unregistered kind no gate here runs, or a `family.yaml` beside an assembly root: the `manifest kinds` NEXT is prose, with `family.py` commands in backticks that an interactive zsh would run if the line were pasted. +- The test that holds every NEXT to a bare command checks each string constant alone, so it would miss a ` # ` tail split across a `+`, or one with no blank after the `#`. ### [The test suite, CI and the docs](https://github.com/opensoft/openRepoShape/issues/461) @@ -342,6 +446,9 @@ limit stays open under its area's tracking issue. - A test fixture that clones a just-pushed local remote by plain path (about a dozen of them, and `siblings.py`): it can race git's background maintenance. None has failed in CI. - An unreadable root pin in the doctor's tests: one test still accepts exit 3, where only exit 1 (`INVALID`) is correct. - Two test modules that load the family template's scripts: they leave `.pyc` files in the template tree, so a test that reads every template file could pass or fail by order. +- README's by-hand version of what setup runs: its dry run omits the real run's `--visibility` and `--elected-by`, so the plan it shows can name a different elector than the real run writes. +- The adopt suites run without `--work-dir` and set no `TMPDIR` of their own, so every test run leaves `openreposhape-adopt-*` directories in the system temp folder. +- No test holds that a bare or absent `source:` or `legs:` in a plan is no type finding: two mutants survive that file's tests. ## Before tagging (remove at tag time) @@ -349,36 +456,18 @@ limit stays open under its area's tracking issue. Open `v1.0.0` milestone issues at drafting time: -- #273: Adopting with `plan --pin ` gives a root whose `validate-pins.py` refuses `neutral-pin-missing`. -- #277: A doctor NEXT pasted into macOS's zsh passes its `# comment` as arguments. -- #291: A damaged pin is told "remove the file from the root". -- #305: A source's own `project.yaml` kept in the root adopts into NOT A SHAPE ROOT. -- #307: `family.py init` pushes a holder its own validator refuses for some values. -- #320: Re-running after a failed verification meets the leftover split branch. -- #325: `update-shape.py` NEXT lines print the root unquoted. -- #370: A family holder's refusals can print an assembly root's remedy. -- #405: `family.py bump --to` an annotated tag's id commits a holder its validator rejects. -- #411: Docs: a descendant project's bootstrap, `bump-leg.py` and `apply` call `gh api`, though the README says offline. -- #424: A root in the middle of a merge reads `COMPLIANT`. -- #436: `apply --branch` over an uncommitted in-place `apply` can commit a red pin or make no branch. -- #440: On the macOS runner, cloning a just-pushed local bare remote races git's background maintenance. -- #462: After a ruleset refuses the first push, neither exit the hint offers can finish. +- #370: In a family holder, a refusal built without a remedy of its own prints an assembly root's remedy, naming a file and a leg pin no holder has. - #471: The Windows CI job never runs an adoption, because `git-filter-repo` is not installed there, so adoption on Windows is unverified. - #472: Nothing in the repository yet says what the release covers or lists its known limits. -- #475: After `bump-leg.py`, `make bootstrap` leaves the leg detached: it never fast-forwards a local tracking branch that is behind the new pin. -- #476: `setup.sh` run from an AI assistant's shell, with no terminal, refuses with "Re-run with --yes", the flag AGENTS.md forbids the assistant. -- #477: `bump-leg.py --dry-run` is refused `bump-leg-on-tracking-branch` on every fresh project, before the `old -> new` line. -- #478: A gitlink committed by hand: the doctor's `pins` row and `validate-pins.py` name no command. -- #479: Adopting a source whose default branch is not `main`: both legs are pushed, then the pull request is refused (`--base main`). -- #480: Adopting a source with its own Makefile: `execute`'s NEXT `make bootstrap` fails until a person merges `shape/Makefile`. -- #481: A refused pull request stops `execute` before the `xf-project-` topic is set, and the way out never says so. -- #483: An accuracy pass over README.md and AGENTS.md. -- #485: With the shape pin and a pinned copy both conflicted, pasting the doctor's NEXT drops a person's `--accept-local` edit, and the doctor then says `COMPLIANT`. - #486: `family.py` and `siblings.py` print commands with unquoted paths. Under a folder with a space, a remedy's `rm -rf` would delete the wrong directory. - #487: `family.py add`, `bump` and `remove` commit straight onto the holder's tracking branch, and `add`'s NEXT pushes a branch that does not exist. - #488: `family.py init`, as AGENTS.md spells it, lands the holder inside the openRepoShape checkout, and its NEXT prints a command that cannot run. -- #489: Updating a holder cut before `siblings.py` existed, without `--add scripts/siblings.py`, breaks `make siblings`, `park` and `resume`. -- #492: `setup.sh`'s documented `curl … | bash` one-liner never asks for the yes, even in a person's own terminal, because its stdin is the pipe. +- #499: `adopt-project.py plan` never compares `--project` or `--org` with the repository it adopts, so a source named `billing-service` can be steered onto another repository of the same name. +- #500: `openRepoShape --doctor` prints every NEXT as a path into its temporary clone, which is deleted before the command can be pasted, and it refuses `--json` and `--placement-plan`. +- #501: `update-shape.py check`'s NEXT, and the doctor's `shape currency` row, already carry `--accept-local` for a locally edited copy, so pasting it re-pins the edit with no question to the human. +- #502: Once an adopted root merges a `shape/` copy into its own file and removes the copy, its pin row is `copy-missing`, the gate goes red, and no tool removes the row. +- #514: `adopt-project.py plan --pin 007@` says `adoption verified` over a root whose own `validate-pins.py` refuses `entry 7`. +- #516: `scaffold-project.py --pin 007@` scaffolds a root whose own `validate-pins.py` refuses `entry 7`. ## Full list From af146167c67e10cc01260c882b6b8224941f82c6 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Wed, 14 Oct 2026 03:23:00 +0000 Subject: [PATCH 4/7] docs/release-notes/v1.0.0.md: brought up to main 8beb3d9, so section 5 cites the six pull requests merged since #402, section 6 drops the `--help` verdict line #510 fixed and AT-family N2, which #501's ruling decides, and adds 28 checklist lines from the parents, and section 7 lists the 3 open milestone issues (#472) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refresh round 2. GitHub was read from 2026-10-14T03:11:22Z to 03:18:32Z; main was 8beb3d92 (#512) throughout. Every merge commit of the 223 merged pull requests is an ancestor of it. Section 5 adds, from each squash message and PR body: - adopt-project.py: #515 (a pinned `007` listed as text) and #513 (a new "The source's own name" bullet: `adopt-not-the-source`, `plan-not-the-source`, rename first); - update-shape.py and bump-leg.py: #512 (a new bullet: no NEXT passes `--accept-local`; `merged-away` rows dropped by `apply`); - setup, scaffold, family and the shared helpers: #517 (a `--pin` product listed as text) and #507 (`family-landing-in-standard`, `family-on-tracking-branch`, a quoted NEXT that pushes the branch); - shape-doctor.py: #510 (the installed doctor keeps its clone, and passes `--json` and `--placement-plan` through). The bullet's "writes nothing into the repository" now adds "but a `--placement-plan` file you name": a relative plan file given from inside the repository is written there, as #460's new line says. Section 6 drops two lines and adds 28; no known-limit issue closed, so the 47 issues stay: - #457, 16 lines: L2-14, which #515 makes true of main; 10 of lane 4's 14 lines from #513 (00:06Z), two of them, a backslash `origin` and a trailing slash, in one bullet; and L1-K1, K2, K3, K7, K9 and K10 from lane 1 (01:17Z). - #458: AT-family N2 is dropped, as lane 4's status note there says (00:48Z); 2 lines added from #512. - #459, 3 lines that #507 and #517 make true of main: the validate-family remedies line (03:27Z), the README `--into` line (11:46Z) and L2-15. - #460: the `openRepoShape --help` verdicts line is dropped (#510 fixed it); the `shape currency` line is reworded, because since #402 the `apply --branch` is the row's WHY and since #512 it has no `--accept-local`, but under `--branch` it is still refused `update-branch-uncommitted` before `update-local-drift`; 7 of lane 4's 8 lines from #510 (23:34Z) are added. Left out, by round 1's rules: #513's `insteadOf` line and README sentence (wording), its comments line (comment wording), its extra `git config` read (no effect a person meets); L1-K4 (cannot arise today); L1-K5, K6 and K8, which describe #518's and #504's branches; and #510's `--help` synopsis column (cosmetic). #458's 03:26Z restatement is folded into its existing `repo_shape.py` line: #507 removed the instance it added. Section 7 lists #370, #471 and #472, the milestone's three open issues of 47, and says #178, #181 and #183 are not in the milestone. What was read at 8beb3d92, by `git show` or `git grep` of the code or text each line cites: - section 5: all six new sentences (adopt-project.py:6986-7030 and :7812-7960; update-shape.py:287 and :1939-1969; shape-doctor.py:3238-3243 and :3298-3314; scaffold-project.py:1293-1330 and the neutral pin template's line 30; scripts/family.py:620-720 and :925-960; openRepoShape:97-111, :489-490 and :540-558); - #457: L2-14, the backslash and trailing-slash line (`_remote_repository`, adopt-project.py:1164-1182), `check`'s missing rename-first remedy (:5137), L1-K3 (scripts/repo_shape.py:2048), L1-K9 (adopt-project.py:3316-3333); - #458: both new lines (shape-doctor.py:2005-2060 and :3222-3227), and N2's drop (update-shape.py:2915-2919 and `apply_command`); - #459: all three (validate-family.py:356-358 and :1066-1071; README.md:861- 862, docs/handbook.html:1247; scaffold-project.py:1313-1330 against adopt-project.py:7017-7030); - #460: the dropped and reworded lines (openRepoShape:97-100, docs/cli.md: 160-163; shape-doctor.py:3306-3314 and `cmd_apply`'s refusal order), the adoption NEXT with no `--org` (shape-doctor.py:6141-6143), the two `--placement-plan` line (openRepoShape:540-558, shape-doctor.py:6828), the "writes nothing" line (openRepoShape:55 and :605, README.md:1112, docs/handbook.html:1467) and the kept-clone line (openRepoShape:490); - kept lines whose code a landing touched: #458's "not hand-edited" commit and title-less `gh pr create` (update-shape.py:2349-2353, :3796-3797), AT-family N3, #460's `=word` line (`UNQUOTED_POSIX` admits `=`), and #459's unquoted setup `cd` and step (7) (setup-project.py:2023-2029, :2169). The rest rest on the posting lane's measurement, not re-read here: of #457's lines, lane 4's mirror path, `--source` after the clone, recorded `source:`, `ATLAS`, fork, `straße` and null-`org:` lines (measured at cf118ff; adopt-project.py is the same at 8beb3d92), and lane 1's L1-K1, K2, K7 and K10 (measured on 8beb3d92); of #460's, the TERM, interrupted-run and drifted-project-test lines (measured at 173c00e; `openRepoShape` is the same at 8beb3d92). The file is 7,354 words, from 6,611: section 5 is 2,291 (2,085), section 6 4,351 (3,607) and section 7 109 (316). No test names `docs/release-notes/` (`git grep -n release-notes origin/main -- tests/` finds nothing), but two tests in tests/test_repo_hygiene.py read every tracked `.md` through `git ls-files`: the host-absolute-path test (`HOST_ABSOLUTE_PATH`, :1496-1527) and the " naming families" count (:1545-1601). Their two patterns, applied to this commit's file, match nothing. No test was run. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 --- docs/release-notes/v1.0.0.md | 82 ++++++++++++++++++++++++++---------- 1 file changed, 60 insertions(+), 22 deletions(-) diff --git a/docs/release-notes/v1.0.0.md b/docs/release-notes/v1.0.0.md index 70d8d52f..16982da3 100644 --- a/docs/release-notes/v1.0.0.md +++ b/docs/release-notes/v1.0.0.md @@ -93,7 +93,13 @@ pin. Makefile (#493). `plan --pin` takes `@`, and `execute` writes the pin file the scaffold writes, so the adopted root's `validate-pins.py` passes; a `--pin-source` that cannot answer is - `pin-source-unreadable` (#508). + `pin-source-unreadable` (#508). A pinned product such as `007` is listed + in `project.yaml` as text, not as the number 7 (#515). +- **The source's own name.** `plan` refuses an `--org` and `--project` that + name another repository than the source's, compared without case + (`adopt-not-the-source`). `check` and `execute` refuse a plan edited to + name one (`plan-not-the-source`), and a source whose own name the naming + policy refuses is told to rename it first (#513). - **Refused before any leg exists.** `execute` refuses everything `check` finds before it creates a leg. That covers bad mount paths, root paths that clash with the shape's files, the source's own `project.yaml` kept in the @@ -133,6 +139,11 @@ pin. and can be repeated. Family holders, and their README's `Shape:` line, update the same way, and the commit and refusals name the holder's own manifest (#10, #29, #47, #52, #85, #152, #159). +- **Local edits and merged copies.** No NEXT line, `check`'s or the + doctor's, passes `--accept-local`, so a locally edited copy meets + `update-local-drift`, which leaves the choice to a person. A `shape/` + collision copy merged into its original and removed is `merged-away`, and + `apply` drops its pin row (#512). - **Landing.** `apply --branch` takes `--trailer` lines, and a non-ASCII trailer is committed as typed under any locale. Before it writes, `apply --branch` refuses a pin, manifest or copy that differs from HEAD @@ -193,8 +204,9 @@ pin. Shape files and leg mounts get past the operator's own ignore rules, and templates are filled in one pass. They quote every text value they write, so `--id 007` or `--tracking-branch 2026` reads back as typed, and a leg - path any template line would misread is refused - (#185, #201, #234, #235, #256, #266, #271, #314, #316, #351, #355, #395, #409). + path any template line would misread is refused. A `--pin` product such + as `007` is listed as text, so the root's own `validate-pins.py` passes + (#185, #201, #234, #235, #256, #266, #271, #314, #316, #351, #355, #395, #409, #517). - **Ruleset refusals.** `--reuse-empty-repo` reuses a leg that is empty or holds only the scaffold's own seed. The ruleset hint offers that one re-run, by an operator holding the bypass right, and names the run's tracking @@ -220,8 +232,12 @@ pin. `member-duplicate-id`), and `bump --to` an annotated tag, a tree or a blob is refused `member-to-not-a-commit`. A repeated `bump` says `nothing to do` and exits 1. `make siblings`, `park` and `resume` refuse by name when - `scripts/siblings.py` is missing - (#16, #79, #219, #221, #236, #308, #315, #317, #318, #345, #350, #381, #397, #399, #400, #406, #416, #466, #503). + `scripts/siblings.py` is missing. `init` refuses its default landing + inside a checkout of the standard (`family-landing-in-standard`), and + `add`, `bump` and `remove` refuse the holder's tracking branch + (`family-on-tracking-branch`) and end with a quoted NEXT that pushes + their own branch + (#16, #79, #219, #221, #236, #308, #315, #317, #318, #345, #350, #381, #397, #399, #400, #406, #416, #466, #503, #507). - **Park and resume.** `make park` and `make resume` hand off to the Speckit git extension. The installed commands moved to opensoft/openRepoTools (#80, #83, #93, #94). @@ -234,10 +250,13 @@ pin. ### shape-doctor.py -- **One verdict.** `openRepoShape --doctor` writes nothing into the - repository and fetches nothing into it. Run outside a checkout of the - standard, it first clones the standard into a temporary directory. It prints - a row per check, then one verdict: +- **One verdict.** `openRepoShape --doctor` fetches nothing into the + repository, and writes nothing there but a `--placement-plan` file you + name. Run outside a checkout of the standard, it clones the standard + into a temporary directory and keeps it, naming it on the report's last + line, so a NEXT that names a file in it still runs after the command + exits. `--json` and `--placement-plan ` pass through to + `shape-doctor.py` (#510). It prints a row per check, then one verdict: - `COMPLIANT`: exit 0; - `COMPLIANT, SHAPE BEHIND`, `DRIFTED`, `MISPLACED` or `INVALID`: exit 1; - `NOT A SHAPE ROOT`: exit 2; @@ -333,6 +352,22 @@ limit stays open under its area's tracking issue. - A path the plan answered by hand, such as `spec/examples/`: the doctor's `placement` row keeps listing it as `review_required`, because `execute` records only the submodule answers. - A local source or assembly that hides a branch with `uploadpack.hideRefs` or `transfer.hideRefs`: `check` does not see it, and `execute --local-remote-dir` pushes both legs before the split push fails. - A submodule moved to the other leg on purpose after adoption: the doctor reads `MISPLACED` from then on, and nothing says how its `submodules:` row is changed. A row whose submodule is gone is never reported. +- A pinned product that only a standard YAML reader misreads, such as `yes`, `on` or `.5`: `execute` lists it bare, because only the project's own reader is asked. The project's validators read it as text. +- A source cloned from a local clone or mirror whose path does not end in `/`: `plan` refuses `adopt-not-the-source` even with the right `--org`, and says the name was read from the path. +- An `origin` that is a Windows path with backslashes, or a URL with a trailing slash: it names no repository, so `--org` and `--project` are not compared with it, and a mistyped `--project` plans with exit 0. +- `plan --source /` with another `--project`: `adopt-not-the-source` comes only after the clone, though the name is known before it. +- A hand-edited plan in the source's own refused name, such as `billing-service`: `check` reports `naming-unclassified` without the rename-first remedy that `execute` gives. +- An `origin` removed after `plan`, or `execute --source` a copy with no `origin`: the plan's recorded `source:` repository is not consulted, so a plan hand-edited to another name passes, and `execute` acts on that name. +- `--project ATLAS` for `acme/Atlas`: accepted, as ruled, and recorded as typed, so the legs are `ATLAS-spec` and `ATLAS-code`. +- A clone whose `origin` is a fork: `plan` refuses it, and, as ruled, the way on is `--source /` or a clone of the upstream, as the remediation says. +- A path `origin` such as `…/straße/Atlas.git` with `--org strasse`: the source check takes them for one repository, though GitHub folds ASCII case only. +- A hand-edited `org:` that is null, deleted, `false` or `0`, for a source whose repository is literally `None/` (or `False/`, `0/`): `check` says `plan ok` where a text `org:` finds `plan-split-branch-taken`. `execute` still refuses. +- A file or link the root keeps at `contracts/shape-pin.yaml`: `check` passes, `execute` fails `adopt-shape-pin-unreadable` after both legs are pushed, and the first bootstrap fails. +- A declared pin's file reached through a root-kept link, such as `contracts -> governance` with a `governance/openglass-pin.yaml`: `check` and `execute` pass, and the first bootstrap fails `yaml-unparsable` or `neutral-pin-unreadable`. +- A hand-edited plan with a bare number or boolean in a text field (`id: 007`, `tracking_branch: true`): it is turned into text (`"7"`, `"True"`) without a word, and `check` and `execute` pass. +- A hand-edited pin whose product holds a tab: `check` reports two findings about the one value, `plan-pin-malformed` and `plan-unwritable-value`. No leg is made. +- A root-kept link at a shape copy's path, such as `shape/README.md -> ../README.md`: `check`'s `plan-root-blocks-shape-copy` says `execute` would write through it after both legs are pushed, but `execute` refuses it before any leg exists. +- A `--work-dir` the user cannot write: `check` passes, and `execute` refuses `extracting the spec leg` after both leg repositories are pushed. - On Windows, a source path holding a character NTFS refuses (a TAB, a control character): it cannot be checked out there, so adopting it is untested on Windows. - On Windows, a source path that is not UTF-8: it cannot be created there, so adopting it is untested on Windows. - On Windows, a source tree name holding a newline, a TAB or a backslash: its submodule plan is untested on Windows. @@ -357,10 +392,11 @@ limit stays open under its area's tracking issue. - A leg on a branch with commits of its own past the pin: bootstrap's `not moving an existing branch` line says the checkout stays at the pin, though it stays on the branch. - A checkout git refuses while the leg is already on its branch at the pin, such as behind a stale `index.lock`: `make bootstrap` prints `checked out, and then git said:` though nothing was checked out. - `bump-leg.py` and `apply` list the paths they staged, not the ones the commit changed, and repairing a gitlink moved by hand prints `X -> X` while the pin moves. -- A `both` file merged by hand and committed: `check`'s NEXT still lacks its `--accept-local`, so the pasted line is refused `update-conflict` again. - An `update-conflict` refusal: its remedy says to read the upstream bytes with `git show :`, but nothing names the source path. - A copy re-pinned with `--accept-local`: the commit `apply` writes still says the copies are not hand-edited. - `apply --branch` without `--pr`: the `gh pr create` it prints carries no `--title`, `--body` or `--fill`, so `gh` asks for them at a prompt. +- Merged-away rows beside a pinned copy kept as a symlink: the doctor's `pins` row names `update-shape.py check` and says its `apply` drops their rows, though that `apply` is refused `update-rewrite-symlink`. +- A root with `copy-is-link`, `copy-missing`, `upstream-removed`, `unmapped` or `already-at-target` rows: the doctor's `shape currency` counts fall short of its rows. ### [setup, scaffold, family and the shared helpers](https://github.com/opensoft/openRepoShape/issues/459) @@ -412,6 +448,9 @@ limit stays open under its area's tracking issue. - A member whose `tracking_branch` is not its remote's default branch: `make siblings` clones the default branch, not the tracking branch the docs promise. - `family.py siblings`: it runs the standard checkout's `siblings.py`, not the holder's pinned copy, so a holder pinned at an older shape runs other code than `make siblings` does, and the `siblings-script-missing` remedy says otherwise. - `make siblings` in a holder whose folder is not named after the family: it clones the members, then prints `mv` lines for the holder alone, so a re-run after them clones the members again. +- The holder's `validate-family.py` remedies that say to run `family.py add` (`member-gitlink-absent`, `member-gitlink-unnamed`): they name no branch step, so the `add` is first refused `family-on-tracking-branch`, whose remedy names the `git switch -c`. +- The README's and the handbook's `--into` sentence: it gives the default, but not that `init` refuses that default inside a checkout of the standard (`family-landing-in-standard`). +- A `--pin` product that begins with a digit or `-`, such as `3dGlass` or `0x1F`: the scaffold may write it bare where `adopt-project.py` quotes it, so the two tools spell the same list differently. The project's own reader reads both back as typed. ### [shape-doctor.py](https://github.com/opensoft/openRepoShape/issues/460) @@ -426,9 +465,8 @@ limit stays open under its area's tracking issue. - #448: A `WARNING` from the project's own `validate-manifest.py`, such as an unreachable referent chain: the `manifest` row says `ok`, and the warning is not shown. - An ordinary git repository: `NOT A SHAPE ROOT` names only the `adopt-project.py plan` command, though AGENTS.md says to report both ways in. - A machine that is not ready: the `machine` row quotes the preflight's `(see above)` with nothing above it, because the preflight's lines are kept only in `--json`. -- `openRepoShape --help`, and `docs/cli.md` from it: it lists five verdicts, with no `MISPLACED` or `CANNOT ANSWER`, and spells `COMPLIANT SHAPE BEHIND` without the comma the doctor prints. - The README's `validate-pins.py` paragraph: it no longer says that a digest that recomputes differently is the finding `neutral-pin-digest-mismatch`, exit 1. -- A pinned copy edited and left uncommitted: the `shape currency` NEXT pastes an `apply --branch` that refuses it as `update-branch-uncommitted`, instead of saying to commit or revert the edit first. +- A pinned copy edited and left uncommitted: the `apply --branch` the `shape currency` row names after its `check` is refused `update-branch-uncommitted`, and the row does not say to commit or revert the edit first. - A merge left unfinished inside a leg: the `merge` row reads only the root's own index, so it is not reported. - A hand-edited `@` in a workflow, or a leg pin whose digest no longer matches: `validate-pins.py` ends the finding at THE LOCKSTEP RULE and names no command. - A passing `validate-pins.py` that prints a note, such as `neutral-pin-source-collides`: the `pins` row says `ok` and drops the note. @@ -437,6 +475,13 @@ limit stays open under its area's tracking issue. - A leg pin broken in the bump's own commit: the `manifest kinds` NEXT restores the pin from before the bump, and the doctor then reads `DRIFTED`. Nothing says to commit it and run the `pins` row's `bump-leg.py` line. - An unregistered kind no gate here runs, or a `family.yaml` beside an assembly root: the `manifest kinds` NEXT is prose, with `family.py` commands in backticks that an interactive zsh would run if the line were pasted. - The test that holds every NEXT to a bare command checks each string constant alone, so it would miss a ` # ` tail split across a `+`, or one with no blank after the `#`. +- A git repository with no `origin`: the doctor's adoption NEXT carries no `--org`, so pasted, it is refused `adopt-no-org`. +- `openRepoShape --doctor --placement-plan a.yaml --placement-plan b.yaml`: only the last plan is written, without a word. +- A relative `--placement-plan ` given from inside the repository: it writes that file there, though `openRepoShape`'s usage and refusal lines, the README and the handbook say the doctor writes nothing into the repository. +- The kept clone's last line: it says any NEXT above runs from the clone, but the `pins` row's NEXT names no file in it, and the `placement` row's relative `--placement-plan`, run from the clone, writes the plan into the directory the line calls safe to delete. +- A TERM sent to the installed doctor's shell alone, mid-run: it deletes the kept clone under the still-running doctor. +- An interrupted `openRepoShape --doctor` (INT, TERM, HUP): it deletes its clone, but no test holds that. +- The installed doctor's NEXT lines: only a drifted project's are tested. A plain repository's were run by hand. ### [The test suite, CI and the docs](https://github.com/opensoft/openRepoShape/issues/461) @@ -454,20 +499,13 @@ limit stays open under its area's tracking issue. -Open `v1.0.0` milestone issues at drafting time: +Open `v1.0.0` milestone issues at drafting time (3 open, 44 closed): - #370: In a family holder, a refusal built without a remedy of its own prints an assembly root's remedy, naming a file and a leg pin no holder has. - #471: The Windows CI job never runs an adoption, because `git-filter-repo` is not installed there, so adoption on Windows is unverified. - #472: Nothing in the repository yet says what the release covers or lists its known limits. -- #486: `family.py` and `siblings.py` print commands with unquoted paths. Under a folder with a space, a remedy's `rm -rf` would delete the wrong directory. -- #487: `family.py add`, `bump` and `remove` commit straight onto the holder's tracking branch, and `add`'s NEXT pushes a branch that does not exist. -- #488: `family.py init`, as AGENTS.md spells it, lands the holder inside the openRepoShape checkout, and its NEXT prints a command that cannot run. -- #499: `adopt-project.py plan` never compares `--project` or `--org` with the repository it adopts, so a source named `billing-service` can be steered onto another repository of the same name. -- #500: `openRepoShape --doctor` prints every NEXT as a path into its temporary clone, which is deleted before the command can be pasted, and it refuses `--json` and `--placement-plan`. -- #501: `update-shape.py check`'s NEXT, and the doctor's `shape currency` row, already carry `--accept-local` for a locally edited copy, so pasting it re-pins the edit with no question to the human. -- #502: Once an adopted root merges a `shape/` copy into its own file and removes the copy, its pin row is `copy-missing`, the gate goes red, and no tool removes the row. -- #514: `adopt-project.py plan --pin 007@` says `adoption verified` over a root whose own `validate-pins.py` refuses `entry 7`. -- #516: `scaffold-project.py --pin 007@` scaffolds a root whose own `validate-pins.py` refuses `entry 7`. + +#178, #181 and #183 are not in the milestone. They are listed above as known limits. ## Full list From 17f4135836cb728e70843bb21e7d977158c4e7fb Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Wed, 14 Oct 2026 13:35:34 +0000 Subject: [PATCH 5/7] docs/release-notes/v1.0.0.md: brought up to main 3121945, so section 5 cites #504 and #494, section 6 drops #178 and #183, which #504 closed, adds the four limits #504 disclosed for #457 (J1, J2, J3, reftable) now that they describe main, and narrows two lines #504 and #494 made partly untrue, and section 7 lists the 2 open milestone issues (#472) Refresh round 3a, against main 31219459 (#494, merged 2026-10-14T13:21:57Z). Two pull requests merged after round 2's 8beb3d92, and both merge commits are on main, as are all 225: - #504 (b0826e02, lane 1, closes #178 and #183) under adopt-project.py, as a new bullet, "Symlinks the root keeps": `plan-root-symlink` and `adopt-symlinks-off`, both before any leg. - #494 (31219459, lane 3, closes #370) under the shared helpers, where #370's parent #459 puts it: a refusal with no remedy of its own prints its `REFUSED` line alone, `validate-pins.py` keeps the lockstep remedy, and `family.py add` refuses `gitmodules-unreadable`. Lane 3 asked on #473 (13:22Z) that #370 be placed in the notes. Each line was written from the PR body and its squash message, and its central claim read in main's code: `_entry_findings` and `_root_symlink_findings` (adopt-project.py:4701, :5717), `_refuse_symlinks_checked_out_as_files` (:5824) before `_create_leg_remotes` (:7542, :7580); `Refusal`'s empty default (scripts/repo_shape.py:166-174), `validate-pins.py`'s handler (templates/assembly-root/scripts/validate-pins.py:1181-1184), family.py's `gitmodules-unreadable` (:1912-1926), and the doctor rows that take `exc.remediation or None` (shape-doctor.py:4546). Section 6: - Dropped #178 and #183, closed by #504. - Added L1-J1, L1-J2, L1-J3 (#457, 2026-10-12T23:36Z) and L1-K8 (#457, 2026-10-14T01:17Z). Round 2 left them out because they described #504's branch. #504's body lists all four as its known limits. J2's `check` half (the refusal and its false sentence), J1's `check` half (`plan ok`) and J3's `check` halves were run at 31219459 in an offline probe. J1's and J3's `execute` halves rest on lane 1's measurement and the code (`_has` and `CLONE_GIT_TREES` at :5427-5438; `adopt-symlinks-off` at :5824-5866). K8 is read from the code only (`CLONE_GIT`, :5201-5206), and its line says it is untested. - Narrowed L1-K1's line. A link at `contracts/shape-pin.yaml` that leads nowhere is now a `plan-root-symlink` finding, since the walk follows `SHAPE_PIN` (shape_materialize.py's `assembly_root_paths`). The probe ran it, and a real file there still passes `check`. The line now says a file, or a link there to a declared pin's file, as K1 measured. - Narrowed #408's line. The issue's own repro, a policy file that does not parse, carried `repo_shape`'s default lockstep remedy, which #494 removed, so the `placement` row now has no NEXT. Only a policy file that is not UTF-8 still gets prose there (`load_yaml`'s own remedy, which holds no backtick). An in-process probe of `PathPolicy.load` with main's and 8beb3d92's repo_shape.py shows both. - Kept L1-K2, K9 and K10, which lane 1 measured as the same on #504's branch. The five parents have no new comment and an unchanged body since round 2's read (03:11Z to 03:18Z). Their sub-issues still list every open, unmilestoned one: 45 (#457 10, #458 6, #459 17, #460 9, #461 3). Section 7: the milestone is 45 closed and 2 open (#471, #472). #370 left the list. Of the three issues section 7 named as outside the milestone, only #181 is open. No claim is made about an unlanded pull request (#511, #518), and nothing is said about a tag or a date. 7,354 words before, 7,628 after. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 --- docs/release-notes/v1.0.0.md | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/docs/release-notes/v1.0.0.md b/docs/release-notes/v1.0.0.md index 16982da3..1797027c 100644 --- a/docs/release-notes/v1.0.0.md +++ b/docs/release-notes/v1.0.0.md @@ -110,6 +110,16 @@ pin. of the wrong type, or a date the tools cannot read, is a finding, not a traceback (#170, #187, #198, #231, #270, #313, #353, #356, #377, #382, #387, #435). +- **Symlinks the root keeps.** `check` follows each kept symlink that a + path the shape writes runs through. One the shape cannot write through, + because it dangles, leads out of the tree, enters a leg or a submodule, + loops, or carries the shape's scripts out of their directory, is a + `plan-root-symlink` finding, and `execute` refuses it before any leg + exists. Where git checks a symlink out as a file, `execute` also refuses + one the shape writes beneath (`adopt-symlinks-off`). Such links used to + break the run after both legs were pushed, or put the shape's files + outside the assembly. A link at a shape path to something that is there + still gets the copy beside it (#504). - **Submodules.** `.gitmodules` stays in one leg with its submodules. An unreadable one is a `plan-gitmodules-unreadable` finding, not a crash, and relative URLs are checked from both sides. Verification accepts no root @@ -246,7 +256,13 @@ pin. (`yaml-unparsable`), and several flow and quoting forms it used to misread are now read exactly or refused. Output from git and `gh` is read as UTF-8 under any locale. "Same repository" now has one definition - (#156, #160, #193, #230, #244, #257, #265, #284, #390). + (#156, #160, #193, #230, #244, #257, #265, #284, #390). A refusal built + without a remedy of its own prints its `REFUSED` line alone. Before, each + tool printed the assembly root's lockstep remedy under it, in a family + holder too, and the doctor could offer it as a NEXT. The root's + `validate-pins.py` still prints it. `family.py add` refuses a + `.gitmodules` git cannot read as `gitmodules-unreadable`, as the holder's + gate does (#494). ### shape-doctor.py @@ -320,9 +336,7 @@ limit stays open under its area's tracking issue. ### [adopt-project.py](https://github.com/opensoft/openRepoShape/issues/457) -- #178: A dangling or outward symlink the root keeps at or above a path the shape writes: `check` passes, then `execute` crashes or writes outside the assembly after both legs are pushed. - #181: A source holding a file or directory named `null`, `true` or `false`: `plan` writes the name unquoted, so `check` finds the path in no entry, and an entry that covers nothing, until the plan is fixed by hand. -- #183: A root-kept symlink to elsewhere on disk, or a link loop: the shape's files are written through the link, or the run crashes, after both legs are pushed. - #388: A source symlink whose target begins with a NUL byte: `check` passes, and `execute` fails after both legs exist, with nothing on stderr. - #389: A plan that keeps the source's own `project.yaml` in the root: `check` and `execute` now refuse it before any leg exists, but verification never checks the root manifest itself, so nothing behind that refusal would catch it. - #417: A relative submodule URL under `--local-remote-dir`: `check` and `execute` resolve it from different remotes, so one can refuse what the other passes. @@ -362,8 +376,12 @@ limit stays open under its area's tracking issue. - A clone whose `origin` is a fork: `plan` refuses it, and, as ruled, the way on is `--source /` or a clone of the upstream, as the remediation says. - A path `origin` such as `…/straße/Atlas.git` with `--org strasse`: the source check takes them for one repository, though GitHub folds ASCII case only. - A hand-edited `org:` that is null, deleted, `false` or `0`, for a source whose repository is literally `None/` (or `False/`, `0/`): `check` says `plan ok` where a text `org:` finds `plan-split-branch-taken`. `execute` still refuses. -- A file or link the root keeps at `contracts/shape-pin.yaml`: `check` passes, `execute` fails `adopt-shape-pin-unreadable` after both legs are pushed, and the first bootstrap fails. +- A file the root keeps at `contracts/shape-pin.yaml`, or a link there to a declared pin's file: `check` passes, `execute` fails `adopt-shape-pin-unreadable` after both legs are pushed, and the first bootstrap fails. - A declared pin's file reached through a root-kept link, such as `contracts -> governance` with a `governance/openglass-pin.yaml`: `check` and `execute` pass, and the first bootstrap fails `yaml-unparsable` or `neutral-pin-unreadable`. +- A link at a path the shape writes to a name the clone's `.git` does not hold under `objects/`, `refs/`, `logs/` or `modules/`, such as `CLAUDE.md -> .git/refs/remotes/origin/main`: `check` passes, and `execute` writes the shape's file into the clone's `.git` and says `adoption verified`, or, under `objects/`, fails after both legs exist. +- A link at a path the shape writes whose target climbs back out of `.git` or a directory in it, such as `CLAUDE.md -> .git/refs/../HEAD`: it is refused `plan-root-symlink` before any leg, saying `.git/refs` is not a directory in the assembly root, though the shape's copy could go beside it. +- A kept link above a path the shape writes, where git checks a symlink out as a file: `check`'s `plan-root-symlink` says where the link leads, though the clone holds it as a file, and its "change where it points" leads on to `execute`'s `adopt-symlinks-off`. +- A link at a path the shape writes to `.git/reftable`, on git 2.45 or later with the reftable backend: it is refused `plan-root-symlink`, though the shape's copy could go beside it. Untested: read from the code. - A hand-edited plan with a bare number or boolean in a text field (`id: 007`, `tracking_branch: true`): it is turned into text (`"7"`, `"True"`) without a word, and `check` and `execute` pass. - A hand-edited pin whose product holds a tab: `check` reports two findings about the one value, `plan-pin-malformed` and `plan-unwritable-value`. No leg is made. - A root-kept link at a shape copy's path, such as `shape/README.md -> ../README.md`: `check`'s `plan-root-blocks-shape-copy` says `execute` would write through it after both legs are pushed, but `execute` refuses it before any leg exists. @@ -455,7 +473,7 @@ limit stays open under its area's tracking issue. ### [shape-doctor.py](https://github.com/opensoft/openRepoShape/issues/460) - #342: A `.pth` file in the operator's Python setup that reaches a `repo_shape.py`: the `manifest` row passes for a root that has lost its own copy, and CI then fails. -- #408: A standard whose own `contracts/path-classification.yaml` does not load: a prose remedy is printed as the NEXT command, and zsh runs it. +- #408: A standard whose own `contracts/path-classification.yaml` is not UTF-8: the `placement` row prints the reader's prose remedy as its NEXT command. - #410: A crash of the standard's own naming script, or a directory name that is not UTF-8: the doctor reports `INVALID (naming)` about the project. - #413: A leg or member whose detached HEAD holds the only copy of a commit: pasting the `legs` or `members` NEXT moves it, so only the reflog keeps the commit. - #414: A mount with no `.git`, an unborn HEAD, no `.gitmodules` section, or a pin that is not 40 hex: the `legs` NEXT is the wrong command, or leaves the row red. @@ -499,13 +517,12 @@ limit stays open under its area's tracking issue. -Open `v1.0.0` milestone issues at drafting time (3 open, 44 closed): +Open `v1.0.0` milestone issues at drafting time (2 open, 45 closed): -- #370: In a family holder, a refusal built without a remedy of its own prints an assembly root's remedy, naming a file and a leg pin no holder has. - #471: The Windows CI job never runs an adoption, because `git-filter-repo` is not installed there, so adoption on Windows is unverified. - #472: Nothing in the repository yet says what the release covers or lists its known limits. -#178, #181 and #183 are not in the milestone. They are listed above as known limits. +#181 is not in the milestone. It is listed above as a known limit. ## Full list From 4137268d2248857e7999ca77b1e936b0438592cf Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Wed, 14 Oct 2026 16:25:07 +0000 Subject: [PATCH 6/7] docs/release-notes/v1.0.0.md: brought up to main 5ed3e5d, so section 5 cites #511 and drops the Windows qualifier now that the Windows job runs the adoption suites, section 6 cites the v1.1.0 issue of each line the triage scheduled, drops the five lines closed on their parents and #408, and adds #511's line-ending limits, and section 7 lists #472 alone (#472) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refresh round 3b, against main 5ed3e5de (#511, merged 2026-10-14T16:08:28Z, closing #471). All 226 merge commits are on main. Section 5. #511 is cited twice: under adopt-project.py, as a new bullet on a source's own `.gitattributes`, and under the test suite and CI, in the "Three operating systems" bullet. "Every source path is verified by blob sha" gains the exception AGENTS.md step 7 now states. The claims were read in main's code: - `.github/workflows/tests.yml`:171-182, where `tests-windows` installs `git-filter-repo==2.47.0`; :209, where it runs the whole suite; and :118-125, where it runs on pull requests, not on a push to main; - `scripts/shape_materialize.py`: `EOL_BLOCK_HEAD` (:701), `eol_line` (:712-721), `eol_block_paths` (:724-729), `with_eol_block` (:738-744), and `_append_eol_block` (:813-831), which writes through no symlink, as called from `materialize_assembly_root` (:2256-2257); - `adopt-project.py`: `_land_the_eol_block` (:8081-8124, called at :8063) and the split message's `APPENDED TO` (:7953); - `update-shape.py`: `_extend_the_eol_block` (:3061-3089). The qualifier. Main's workflow now does what the unqualified sentences say, so both `` comments and the two sentences they marked are gone. Section 6, re-read after lane 3's v1.1.0 triage (#473, 16:03:48Z, on Brett Heap's approval at 14:31:28Z): - 74 lines marked `→ #N` or "rides with #N" on their parent now end with that number, as in (#524). For each parent, the set of numbers cited equals the set its marks name. One sentence in the section's opening says what the number is. - Dropped, each closed on its parent: `--project ATLAS` and a fork's `origin` (#457, ruled behaviour); the scaffold's three working trees (#459, fixed by #505 and kept documented); and the `shape currency` row's uncommitted-copy line (#460, obsolete since #402 and #512). Also #408, closed as fixed by #494. - Added, under #457: the symlinked root `.gitattributes`, the nested one (#530), and a root adopted before #511 (lane 4, 2026-10-12T23:25Z); then #511's three posted at 16:09:31Z: a copy written through a symlinked directory, a CRLF `.gitattributes` under a rule other than `text=auto`, and a `filter=` or `working-tree-encoding=` rule. Its fourth, a test helper that feeds text on stdin, is left out: no person meets it. These rest on lane 4's measurement (V511c at 1ce4f456, whose tree is main's); `eol_line` writing only `text eol=lf` and the symlink skip were read in code. - Reworded: - AT-scaffold N9 (#458), with lane 4's 16:10:03Z note. `cmd_apply` always stages the pin and the manifest (`update-shape.py`:3280-3281), and `_land` prints them all (:3224). - The annotated-tag line (#459). It is now ruled that `bump --to` peel it (#541), so "One rule is still to be chosen" is gone. - The Windows symlink line (#457), narrowed to its parent's own words. #511's end-to-end test of a source that tracks a symlinked `.gitattributes` under `core.symlinks=false` carries only `needs_filter_repo` (tests/test_adopt_ignored_shape_files.py:862-863), so it now runs on Windows. - Left out, new: the three CI lines new on #461 (the merge queue and two CI costs). They are this repository's CI, not a limit of the shape. - Lane 1 has posted no #504 batch, so D13 stays out. Section 7: v1.0.0 is 46 closed and #472 open. #181 is still outside the milestone. No claim is made about an unlanded pull request (#518, #551), and nothing is said about a tag or a date. 7,628 words before, 7,906 after. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 --- docs/release-notes/v1.0.0.md | 195 ++++++++++++++++++----------------- 1 file changed, 101 insertions(+), 94 deletions(-) diff --git a/docs/release-notes/v1.0.0.md b/docs/release-notes/v1.0.0.md index 1797027c..12e10442 100644 --- a/docs/release-notes/v1.0.0.md +++ b/docs/release-notes/v1.0.0.md @@ -45,9 +45,7 @@ are listed below as known limits. points. Park and resume do not work on native Windows; run them under WSL2. - CI runs the full suite on `ubuntu-latest`, `macos-latest` and - `windows-latest`. On Windows the adoption suites skip, because - `git-filter-repo` is not installed there (#471). - + `windows-latest`. ## How to pin 1.0.0 @@ -82,7 +80,9 @@ pin. - **In-place adoption.** The repository becomes the assembly root and keeps its name and history. The legs are extracted with `git filter-repo`, and the - split lands as one pull request. Every source path is verified by blob sha. + split lands as one pull request. Every source path is verified by blob + sha, a kept root `.gitattributes` as the source's bytes plus the block + below. The plan asks about any path it cannot place, an empty leg is seeded with `--allow-empty-leg`, and the `xf-project-` topic is set (#5, #16, #17, #24). @@ -120,6 +120,12 @@ pin. break the run after both legs were pushed, or put the shape's files outside the assembly. A link at a shape path to something that is there still gets the copy beside it (#504). +- **A source's own `.gitattributes`.** A source that keeps one at its root + gets one marked block of `/ text eol=lf` lines appended to it in + the split, a line for each pinned copy outside `shape/`, so a Windows + clone writes those copies with the bytes the pin records. The + verification counts that file as the source's bytes followed by exactly + that block, and `update-shape.py apply --add` extends the block (#511). - **Submodules.** `.gitmodules` stays in one leg with its submodules. An unreadable one is a `plan-gitmodules-unreadable` finding, not a crash, and relative URLs are checked from both sides. Verification accepts no root @@ -302,11 +308,10 @@ pin. ### The test suite, CI and the docs - **Three operating systems.** Every pull request runs the full suite on - Linux, macOS and Windows, though on Windows the adoption suites skip until - #471 is fixed. Every push to `main` runs it on Linux. actionlint checks this - repository's workflow and both shipped ones - (#9, #11, #53, #57, #124, #129, #336). - + Linux, macOS and Windows, and the Windows job installs `git-filter-repo`, + so the adoption suites run there too. Every push to `main` runs it on + Linux. actionlint checks this repository's workflow and both shipped ones + (#9, #11, #53, #57, #124, #129, #336, #511). - **Hermetic tests.** No test reaches the real `gh` (#274, #349), and the suite scaffolds into local bare repositories, never GitHub. Tests no longer depend on the host's paths, pin-source variables or git identity, and a @@ -332,7 +337,9 @@ pin. ## Known limits Each line names the input that triggers the limit, then what you see. Each -limit stays open under its area's tracking issue. +limit stays open under its area's tracking issue. A number at the end of a +line, such as (#524), is the issue in the `v1.1.0` milestone that groups it +with others for one pull request. ### [adopt-project.py](https://github.com/opensoft/openRepoShape/issues/457) @@ -346,50 +353,54 @@ limit stays open under its area's tracking issue. - #427: An `origin` whose organisation is not ASCII or holds a space: `plan` refuses `unsafe-value: --org`, a flag nobody passed. - #449: A hand edit that makes a neutral pin's `commit:` and `tree_sha256:` disagree: `adoption verified`, then the root's `validate-pins.py` fails `neutral-pin-digest-mismatch`. - #451: A `--pin-source` that nothing reads, even a missing path: `plan` (and the scaffold's `--dry-run`) exit 0 without a word. -- A moved or dropped non-ASCII path under `LC_ALL=C PYTHONUTF8=0`: `execute` ends in a `UnicodeEncodeError` traceback after both legs are pushed. +- A moved or dropped non-ASCII path under `LC_ALL=C PYTHONUTF8=0`: `execute` ends in a `UnicodeEncodeError` traceback after both legs are pushed (#527). - A kept or dropped name holding a line break, a control character or a byte that is not UTF-8: it reaches the split commit message and the pull request body unspelled. -- A source with its own `single-repository.yaml`: `plan` asks the general top-level YAML question. A dedicated rule (drop or root, defaulting to drop) is planned. -- A source branch that differs from the split branch only by letter case, on macOS or Windows: `check` passes, and `execute` fails after both legs exist. +- A source with its own `single-repository.yaml`: `plan` asks the general top-level YAML question. A dedicated rule (drop or root, defaulting to drop) is planned (#528). +- A source branch that differs from the split branch only by letter case, on macOS or Windows: `check` passes, and `execute` fails after both legs exist (#520). - A name holding a backtick or a double quote, or wrapped in backticks: its spelled form can name another entry, or make the plan unreadable. - A path policy whose `question:` or rule id holds control characters: `plan`'s terminal report prints them raw. -- A hand-edited `adopt_branch:` such as `a=b`: `check` says `plan ok`, and `execute` refuses `unsafe-value` before any leg exists. +- A hand-edited `adopt_branch:` such as `a=b`: `check` says `plan ok`, and `execute` refuses `unsafe-value` before any leg exists (#520). - A hand-edited plan with a bad `id:` or `org:` beside a bad leg path: `check` and `execute` name different problems first. No leg is made either way. -- A project adopted with a `--spec-path` other than `spec`: the root Makefile still exports `CONTRACTS_DIR ?= $(CURDIR)/spec/contracts`, so the code leg's tooling points at a directory that does not exist. -- A nested `--code-path`, such as `legs/code` beside `legs/spec`: the cross-leg follow-up names the spec leg `../legs/spec/…` relative to the code leg, where `../spec/…` is right. -- `plan`, `check` or `execute` run without `--work-dir`: each leaves an `openreposhape-adopt-*` directory in the system temp folder, empty or holding the working trees, and nothing removes it. -- `plan --source ` without `--work-dir`, and the temporary clone cleaned before `check` or `execute`: they refuse `source-unclonable`, with text about the repository's name. Passing `--source` again recovers. -- Any adoption: the follow-up `plan` prints, and the split commit lists, says to open the split as a pull request, which `execute` does itself against GitHub, and calls the extracted legs seeded. -- `execute --local-remote-dir`: it ends with `NEXT: review the pull request`, though none was opened. -- An answer in the plan with no `resolution:` beside it: `check` says `plan ok`, though AGENTS.md says such an answer will be read as a finding. -- A verification that fails against GitHub (exit 1): the pull request and the topic are already in place, and neither the report nor AGENTS.md says so. Deleting the split branch, as the way out says, closes the pull request. -- `plan` with no `--elected-by` and no `git config user.name`: it is refused `adopt-manifest-invalid`, naming `--elected-by`, a flag nobody passed. -- A path the plan answered by hand, such as `spec/examples/`: the doctor's `placement` row keeps listing it as `review_required`, because `execute` records only the submodule answers. -- A local source or assembly that hides a branch with `uploadpack.hideRefs` or `transfer.hideRefs`: `check` does not see it, and `execute --local-remote-dir` pushes both legs before the split push fails. -- A submodule moved to the other leg on purpose after adoption: the doctor reads `MISPLACED` from then on, and nothing says how its `submodules:` row is changed. A row whose submodule is gone is never reported. +- A project adopted with a `--spec-path` other than `spec`: the root Makefile still exports `CONTRACTS_DIR ?= $(CURDIR)/spec/contracts`, so the code leg's tooling points at a directory that does not exist (#522). +- A nested `--code-path`, such as `legs/code` beside `legs/spec`: the cross-leg follow-up names the spec leg `../legs/spec/…` relative to the code leg, where `../spec/…` is right (#522). +- `plan`, `check` or `execute` run without `--work-dir`: each leaves an `openreposhape-adopt-*` directory in the system temp folder, empty or holding the working trees, and nothing removes it (#526). +- `plan --source ` without `--work-dir`, and the temporary clone cleaned before `check` or `execute`: they refuse `source-unclonable`, with text about the repository's name. Passing `--source` again recovers (#526). +- Any adoption: the follow-up `plan` prints, and the split commit lists, says to open the split as a pull request, which `execute` does itself against GitHub, and calls the extracted legs seeded (#523). +- `execute --local-remote-dir`: it ends with `NEXT: review the pull request`, though none was opened (#523). +- An answer in the plan with no `resolution:` beside it: `check` says `plan ok`, though AGENTS.md says such an answer will be read as a finding (#523). +- A verification that fails against GitHub (exit 1): the pull request and the topic are already in place, and neither the report nor AGENTS.md says so. Deleting the split branch, as the way out says, closes the pull request (#523). +- `plan` with no `--elected-by` and no `git config user.name`: it is refused `adopt-manifest-invalid`, naming `--elected-by`, a flag nobody passed (#529). +- A path the plan answered by hand, such as `spec/examples/`: the doctor's `placement` row keeps listing it as `review_required`, because `execute` records only the submodule answers (#525). +- A local source or assembly that hides a branch with `uploadpack.hideRefs` or `transfer.hideRefs`: `check` does not see it, and `execute --local-remote-dir` pushes both legs before the split push fails (#520). +- A submodule moved to the other leg on purpose after adoption: the doctor reads `MISPLACED` from then on, and nothing says how its `submodules:` row is changed. A row whose submodule is gone is never reported (#525). - A pinned product that only a standard YAML reader misreads, such as `yes`, `on` or `.5`: `execute` lists it bare, because only the project's own reader is asked. The project's validators read it as text. -- A source cloned from a local clone or mirror whose path does not end in `/`: `plan` refuses `adopt-not-the-source` even with the right `--org`, and says the name was read from the path. -- An `origin` that is a Windows path with backslashes, or a URL with a trailing slash: it names no repository, so `--org` and `--project` are not compared with it, and a mistyped `--project` plans with exit 0. +- A source cloned from a local clone or mirror whose path does not end in `/`: `plan` refuses `adopt-not-the-source` even with the right `--org`, and says the name was read from the path (#521). +- An `origin` that is a Windows path with backslashes, or a URL with a trailing slash: it names no repository, so `--org` and `--project` are not compared with it, and a mistyped `--project` plans with exit 0 (#521). - `plan --source /` with another `--project`: `adopt-not-the-source` comes only after the clone, though the name is known before it. - A hand-edited plan in the source's own refused name, such as `billing-service`: `check` reports `naming-unclassified` without the rename-first remedy that `execute` gives. - An `origin` removed after `plan`, or `execute --source` a copy with no `origin`: the plan's recorded `source:` repository is not consulted, so a plan hand-edited to another name passes, and `execute` acts on that name. -- `--project ATLAS` for `acme/Atlas`: accepted, as ruled, and recorded as typed, so the legs are `ATLAS-spec` and `ATLAS-code`. -- A clone whose `origin` is a fork: `plan` refuses it, and, as ruled, the way on is `--source /` or a clone of the upstream, as the remediation says. - A path `origin` such as `…/straße/Atlas.git` with `--org strasse`: the source check takes them for one repository, though GitHub folds ASCII case only. - A hand-edited `org:` that is null, deleted, `false` or `0`, for a source whose repository is literally `None/` (or `False/`, `0/`): `check` says `plan ok` where a text `org:` finds `plan-split-branch-taken`. `execute` still refuses. -- A file the root keeps at `contracts/shape-pin.yaml`, or a link there to a declared pin's file: `check` passes, `execute` fails `adopt-shape-pin-unreadable` after both legs are pushed, and the first bootstrap fails. +- A file the root keeps at `contracts/shape-pin.yaml`, or a link there to a declared pin's file: `check` passes, `execute` fails `adopt-shape-pin-unreadable` after both legs are pushed, and the first bootstrap fails (#524). - A declared pin's file reached through a root-kept link, such as `contracts -> governance` with a `governance/openglass-pin.yaml`: `check` and `execute` pass, and the first bootstrap fails `yaml-unparsable` or `neutral-pin-unreadable`. - A link at a path the shape writes to a name the clone's `.git` does not hold under `objects/`, `refs/`, `logs/` or `modules/`, such as `CLAUDE.md -> .git/refs/remotes/origin/main`: `check` passes, and `execute` writes the shape's file into the clone's `.git` and says `adoption verified`, or, under `objects/`, fails after both legs exist. - A link at a path the shape writes whose target climbs back out of `.git` or a directory in it, such as `CLAUDE.md -> .git/refs/../HEAD`: it is refused `plan-root-symlink` before any leg, saying `.git/refs` is not a directory in the assembly root, though the shape's copy could go beside it. -- A kept link above a path the shape writes, where git checks a symlink out as a file: `check`'s `plan-root-symlink` says where the link leads, though the clone holds it as a file, and its "change where it points" leads on to `execute`'s `adopt-symlinks-off`. +- A kept link above a path the shape writes, where git checks a symlink out as a file: `check`'s `plan-root-symlink` says where the link leads, though the clone holds it as a file, and its "change where it points" leads on to `execute`'s `adopt-symlinks-off` (#531). - A link at a path the shape writes to `.git/reftable`, on git 2.45 or later with the reftable backend: it is refused `plan-root-symlink`, though the shape's copy could go beside it. Untested: read from the code. - A hand-edited plan with a bare number or boolean in a text field (`id: 007`, `tracking_branch: true`): it is turned into text (`"7"`, `"True"`) without a word, and `check` and `execute` pass. - A hand-edited pin whose product holds a tab: `check` reports two findings about the one value, `plan-pin-malformed` and `plan-unwritable-value`. No leg is made. - A root-kept link at a shape copy's path, such as `shape/README.md -> ../README.md`: `check`'s `plan-root-blocks-shape-copy` says `execute` would write through it after both legs are pushed, but `execute` refuses it before any leg exists. -- A `--work-dir` the user cannot write: `check` passes, and `execute` refuses `extracting the spec leg` after both leg repositories are pushed. +- A `--work-dir` the user cannot write: `check` passes, and `execute` refuses `extracting the spec leg` after both leg repositories are pushed (#524). - On Windows, a source path holding a character NTFS refuses (a TAB, a control character): it cannot be checked out there, so adopting it is untested on Windows. - On Windows, a source path that is not UTF-8: it cannot be created there, so adopting it is untested on Windows. - On Windows, a source tree name holding a newline, a TAB or a backslash: its submodule plan is untested on Windows. -- On Windows, a source that tracks symlinks, without `core.symlinks=true`: the links check out as plain files, so adopting it is untested on Windows. +- On Windows, a source that tracks symlinks, without `core.symlinks=true`: the links check out as plain files, so adopting one with a root-kept link the shape writes through, or a mount through a kept link, is untested on Windows. +- A source whose root `.gitattributes` is a symlink: the split appends no line-ending block, and git reads no `.gitattributes` through a link, so on Windows the pinned copies at the root can still check out with CRLF. The plan's follow-up lists the lines a regular file needs. +- A nested `.gitattributes`, such as `scripts/.gitattributes`, that sets `eol` or `-text` for a pinned copy under it: it outranks the root block for that copy, so a Windows checkout can still write the copy with CRLF (#530). +- A root adopted before #511 landed, from a source that kept its own root `.gitattributes`: it has no block, so on Windows its pinned copies check out with CRLF and `make bootstrap` reports `shape-copy-drift`. `update-shape.py apply --add` only extends a block that is there. +- A pinned copy written through a root-kept symlinked directory, such as `.github -> ci`: the block names it by the link's spelling, `/.github/workflows/validate.yml`, which governs nothing, so under a source rule such as `* text=auto eol=crlf`, `validate-pins.py` reports `shape-copy-drift` for that copy. +- A source `.gitattributes` committed with CRLF line ends under a rule other than `text=auto`: the split renormalises it to LF, and `execute` fails `adopt-lost: .gitattributes` after both legs exist. +- A source `filter=` or `working-tree-encoding=` rule that matches a pinned copy: the block settles line endings only, so the rule still applies. With `*.py working-tree-encoding=UTF-16LE-BOM`, `execute` fails at `git add -A` after both legs exist. ### [update-shape.py and bump-leg.py](https://github.com/opensoft/openRepoShape/issues/458) @@ -399,20 +410,20 @@ limit stays open under its area's tracking issue. - #431: `apply --branch` started from a directory git cannot open: a good branch name is refused as `update-branch-invalid`. - #437: A trailing comment on the pin's `files:` or the manifest's `shape:` line: `apply` refuses `update-pin-unrecognised` or `update-manifest-no-shape`, though `check` sent you there. - #438: A leg pin written as `digests: {tree_sha256: …}`: `bump-leg.py --dry-run` exits 0, and the real run refuses `bump-leg-pin-unrecognised`. -- `bump-leg.py --to` an annotated tag's own id: the dry run shows the tag id and exits 0, while the real run is refused and rolled back with exit 2. -- A locally edited `scripts/repo_shape.py` kept with `apply --accept-local` while taking the new `bootstrap.py`: `make bootstrap` ends in an `ImportError`, though `apply`'s validators pass and `check` then reads everything unchanged. -- `apply` without `--branch`, moving the pin back to what HEAD already commits: it still prints a commit line, and pasted, git answers `nothing to commit`. -- A root under a path holding a space, such as `My Projects/`: the two `switch -c` remedies and the closing push line that `bump-leg.py` prints are unquoted, and paste as another command. -- A root or path holding a space or a shell character: `update-shape.py`'s `update-branch-exists` remedy, its `ADDED UPSTREAM` `--add` list, its `pass --add` note and its `update-local-drift` remedy print unquoted. -- A Windows root whose path holds an apostrophe (`C:\Users\Jane O'Neill\Atlas`): the commands every tool but the doctor prints, the shipped validators' included, are quoted for a POSIX shell, which PowerShell does not read as one argument. -- Every bump: `bump-leg.py` says `make bootstrap` re-places the leg on its tracking branch, which it does only when that branch is at or behind the pin, or ahead of it with no commit of its own. -- A commit made on a detached leg whose branch is already at the pin: `make bootstrap` checks the branch out, leaves the commit on no branch, and says nothing. -- A leg on a branch with commits of its own past the pin: bootstrap's `not moving an existing branch` line says the checkout stays at the pin, though it stays on the branch. -- A checkout git refuses while the leg is already on its branch at the pin, such as behind a stale `index.lock`: `make bootstrap` prints `checked out, and then git said:` though nothing was checked out. -- `bump-leg.py` and `apply` list the paths they staged, not the ones the commit changed, and repairing a gitlink moved by hand prints `X -> X` while the pin moves. -- An `update-conflict` refusal: its remedy says to read the upstream bytes with `git show :`, but nothing names the source path. -- A copy re-pinned with `--accept-local`: the commit `apply` writes still says the copies are not hand-edited. -- `apply --branch` without `--pr`: the `gh pr create` it prints carries no `--title`, `--body` or `--fill`, so `gh` asks for them at a prompt. +- `bump-leg.py --to` an annotated tag's own id: the dry run shows the tag id and exits 0, while the real run is refused and rolled back with exit 2 (#534). +- A locally edited `scripts/repo_shape.py` kept with `apply --accept-local` while taking the new `bootstrap.py`: `make bootstrap` ends in an `ImportError`, though `apply`'s validators pass and `check` then reads everything unchanged (#536). +- `apply` without `--branch`, moving the pin back to what HEAD already commits: it still prints a commit line, and pasted, git answers `nothing to commit` (#535). +- A root under a path holding a space, such as `My Projects/`: the two `switch -c` remedies and the closing push line that `bump-leg.py` prints are unquoted, and paste as another command (#532). +- A root or path holding a space or a shell character: `update-shape.py`'s `update-branch-exists` remedy, its `ADDED UPSTREAM` `--add` list, its `pass --add` note and its `update-local-drift` remedy print unquoted (#532). +- A Windows root whose path holds an apostrophe (`C:\Users\Jane O'Neill\Atlas`): the commands every tool but the doctor prints, the shipped validators' included, are quoted for a POSIX shell, which PowerShell does not read as one argument (#532). +- Every bump: `bump-leg.py` says `make bootstrap` re-places the leg on its tracking branch, which it does only when that branch is at or behind the pin, or ahead of it with no commit of its own (#533). +- A commit made on a detached leg whose branch is already at the pin: `make bootstrap` checks the branch out, leaves the commit on no branch, and says nothing (#533). +- A leg on a branch with commits of its own past the pin: bootstrap's `not moving an existing branch` line says the checkout stays at the pin, though it stays on the branch (#533). +- A checkout git refuses while the leg is already on its branch at the pin, such as behind a stale `index.lock`: `make bootstrap` prints `checked out, and then git said:` though nothing was checked out (#533). +- `bump-leg.py` and `apply` list the paths they staged, not the ones the commit changed: `apply` names `project.yaml` beside the pin even when only the pin moved, as when it drops a merged-away row. Repairing a gitlink moved by hand prints `X -> X` while the pin moves. +- An `update-conflict` refusal: its remedy says to read the upstream bytes with `git show :`, but nothing names the source path (#535). +- A copy re-pinned with `--accept-local`: the commit `apply` writes still says the copies are not hand-edited (#535). +- `apply --branch` without `--pr`: the `gh pr create` it prints carries no `--title`, `--body` or `--fill`, so `gh` asks for them at a prompt (#535). - Merged-away rows beside a pinned copy kept as a symlink: the doctor's `pins` row names `update-shape.py check` and says its `apply` drops their rows, though that `apply` is refused `update-rewrite-symlink`. - A root with `copy-is-link`, `copy-missing`, `upstream-removed`, `unmapped` or `already-at-target` rows: the doctor's `shape currency` counts fall short of its rows. @@ -435,45 +446,43 @@ limit stays open under its area's tracking issue. - #450: A `--pin-source` path holding a line break or a byte that is not UTF-8: it is written into the pin file as it stands, or the scaffold crashes after all three repositories exist. - #454: A family `name` holding a lone surrogate, or any run under `PYTHONIOENCODING=ascii`: `make siblings`, `park` and `resume` end in a `UnicodeEncodeError` traceback. - #455: A lone surrogate or NUL in `tracking_branch` or a leg's `branch:`: `make bootstrap` ends in a traceback, while `validate-manifest.py` says `manifest ok`. -- `family.py bump --to` a commit whose `project.yaml` `id` another row already pins: `bump` commits, and the holder's validator then refuses `member-duplicate-id`. -- An annotated tag's own id: `family.py add --at` pins the commit it tags, while `bump --to` refuses it (`member-to-not-a-commit`). One rule is still to be chosen. +- `family.py bump --to` a commit whose `project.yaml` `id` another row already pins: `bump` commits, and the holder's validator then refuses `member-duplicate-id` (#541). +- An annotated tag's own id: `family.py add --at` pins the commit it tags, while `bump --to` refuses it (`member-to-not-a-commit`). As ruled, `bump --to` is to peel it too (#541). - No git on `PATH`: the shipped `bootstrap.py` scripts end in a `FileNotFoundError` traceback. - A directory name that is not UTF-8: `validate-repository-naming.py` ends in a `UnicodeEncodeError` or `UnicodeDecodeError` traceback. -- A holder member directory the user cannot enter: `validate-family.py` refuses `git-not-runnable` and says to put git on `PATH`. +- A holder member directory the user cannot enter: `validate-family.py` refuses `git-not-runnable` and says to put git on `PATH` (#539). - Re-running before the commit, after the `git rm` that `member-gitlink-unnamed` prints: the finding repeats, and the same command then fails. - A shape-pin `files:` row with no `path:`: the shipped validators report the path `None` as missing, instead of refusing the row. - A YAML key written twice: the reader keeps the last one without a word. -- `--org OpenSoft`, or any casing of `opensoft` but the lower-case one: the upstream-organisation guard compares exactly and lets it through, though GitHub reads an organisation name in any case. -- A scaffold run without `--work-dir`, which is how `setup.sh` runs it: it leaves its three working trees in a temporary directory. +- `--org OpenSoft`, or any casing of `opensoft` but the lower-case one: the upstream-organisation guard compares exactly and lets it through, though GitHub reads an organisation name in any case (#538). - The plan a person approves: it names neither the tracking branch nor, in local mode, the visibility. -- A `--pin` passed to setup after `--`: step (4) never asks the naming check about it, so the report says no referent pin is declared, and the plan then says domain-descendant. -- Inside setup, the scaffold's own `NEXT STEPS` block prints just before setup runs those steps itself. -- `--local-remote-dir`: the clone in the scaffold's `NEXT STEPS` fails with `transport 'file' not allowed`, because it lacks the `-c protocol.file.allow=always` that setup passes. -- Step (7)'s by-hand recovery under `--local-remote-dir`: its recursive clone lacks `-c protocol.file.allow=always` too. -- A clone path or `--into` holding a space: setup's closing `cd` and step (7)'s by-hand recovery print the paths unquoted, and the shell splits them. -- `-- --dry-run` passed through setup: step (7)'s refusal says the three repositories WERE created when none were. -- A file standing where `--family` puts the clone's parent folder: step (3) passes, all three repositories are created, and the run ends in a `FileExistsError` traceback. -- `scaffold-project.py --dry-run` against remotes that already exist: it does not ask, so `scaffold-remote-exists` comes only after the yes, before anything is created. +- A `--pin` passed to setup after `--`: step (4) never asks the naming check about it, so the report says no referent pin is declared, and the plan then says domain-descendant (#538). +- Inside setup, the scaffold's own `NEXT STEPS` block prints just before setup runs those steps itself (#538). +- `--local-remote-dir`: the clone in the scaffold's `NEXT STEPS` fails with `transport 'file' not allowed`, because it lacks the `-c protocol.file.allow=always` that setup passes (#538). +- Step (7)'s by-hand recovery under `--local-remote-dir`: its recursive clone lacks `-c protocol.file.allow=always` too (#538). +- A clone path or `--into` holding a space: setup's closing `cd` and step (7)'s by-hand recovery print the paths unquoted, and the shell splits them (#538). +- `-- --dry-run` passed through setup: step (7)'s refusal says the three repositories WERE created when none were (#538). +- A file standing where `--family` puts the clone's parent folder: step (3) passes, all three repositories are created, and the run ends in a `FileExistsError` traceback (#538). +- `scaffold-project.py --dry-run` against remotes that already exist: it does not ask, so `scaffold-remote-exists` comes only after the yes, before anything is created (#537). - A printed value holding a C1 control or a direction-override character: setup escapes only C0 controls and DEL, so it reaches the terminal. -- A re-run where `gh` can see a leg that git cannot read: the scaffold refuses with the generic `a git or gh command failed`, not `scaffold-remote-exists`, and on a terminal git can stop to ask for a username. -- A re-run that reuses legs: its visibility notes say the two legs were just created, and can contradict its own per-leg warning. +- A re-run where `gh` can see a leg that git cannot read: the scaffold refuses with the generic `a git or gh command failed`, not `scaffold-remote-exists`, and on a terminal git can stop to ask for a username (#542). +- A re-run that reuses legs: its visibility notes say the two legs were just created, and can contradict its own per-leg warning (#542). - A seed found only on `seed/scaffold`, the branch the old ruleset hint made: the re-run accepts it and creates `main` at it, and says nothing about `seed/scaffold`, which on GitHub may stay the leg's default branch. -- A ruleset that refuses `adopt-project.py`'s push: its hint still offers the seed-branch-and-pull-request exit that the scaffold no longer offers. -- A `--pin-source` given as an absolute path: the committed pin file records the scaffolding machine's own directory. -- A lone `shape-copy-drift`: `validate-pins.py` follows it with THE LOCKSTEP RULE, about moving a gitlink, a pin and the workflows together. -- `family.py init --dry-run` against a holder repository that already exists: it exits 0, and the real run refuses `family-remote-exists` after leaving an empty `/` folder. -- `family.py bump --to` a commit not yet pushed to the member's remote: it fails in git's words (`reference is not a tree`), exit 2, and does not say to push first. -- A member whose `tracking_branch` is not its remote's default branch: `make siblings` clones the default branch, not the tracking branch the docs promise. -- `family.py siblings`: it runs the standard checkout's `siblings.py`, not the holder's pinned copy, so a holder pinned at an older shape runs other code than `make siblings` does, and the `siblings-script-missing` remedy says otherwise. -- `make siblings` in a holder whose folder is not named after the family: it clones the members, then prints `mv` lines for the holder alone, so a re-run after them clones the members again. -- The holder's `validate-family.py` remedies that say to run `family.py add` (`member-gitlink-absent`, `member-gitlink-unnamed`): they name no branch step, so the `add` is first refused `family-on-tracking-branch`, whose remedy names the `git switch -c`. +- A ruleset that refuses `adopt-project.py`'s push: its hint still offers the seed-branch-and-pull-request exit that the scaffold no longer offers (#523). +- A `--pin-source` given as an absolute path: the committed pin file records the scaffolding machine's own directory (#540). +- A lone `shape-copy-drift`: `validate-pins.py` follows it with THE LOCKSTEP RULE, about moving a gitlink, a pin and the workflows together (#539). +- `family.py init --dry-run` against a holder repository that already exists: it exits 0, and the real run refuses `family-remote-exists` after leaving an empty `/` folder (#537). +- `family.py bump --to` a commit not yet pushed to the member's remote: it fails in git's words (`reference is not a tree`), exit 2, and does not say to push first (#541). +- A member whose `tracking_branch` is not its remote's default branch: `make siblings` clones the default branch, not the tracking branch the docs promise (#543). +- `family.py siblings`: it runs the standard checkout's `siblings.py`, not the holder's pinned copy, so a holder pinned at an older shape runs other code than `make siblings` does, and the `siblings-script-missing` remedy says otherwise (#543). +- `make siblings` in a holder whose folder is not named after the family: it clones the members, then prints `mv` lines for the holder alone, so a re-run after them clones the members again (#543). +- The holder's `validate-family.py` remedies that say to run `family.py add` (`member-gitlink-absent`, `member-gitlink-unnamed`): they name no branch step, so the `add` is first refused `family-on-tracking-branch`, whose remedy names the `git switch -c` (#539). - The README's and the handbook's `--into` sentence: it gives the default, but not that `init` refuses that default inside a checkout of the standard (`family-landing-in-standard`). -- A `--pin` product that begins with a digit or `-`, such as `3dGlass` or `0x1F`: the scaffold may write it bare where `adopt-project.py` quotes it, so the two tools spell the same list differently. The project's own reader reads both back as typed. +- A `--pin` product that begins with a digit or `-`, such as `3dGlass` or `0x1F`: the scaffold may write it bare where `adopt-project.py` quotes it, so the two tools spell the same list differently. The project's own reader reads both back as typed (#540). ### [shape-doctor.py](https://github.com/opensoft/openRepoShape/issues/460) - #342: A `.pth` file in the operator's Python setup that reaches a `repo_shape.py`: the `manifest` row passes for a root that has lost its own copy, and CI then fails. -- #408: A standard whose own `contracts/path-classification.yaml` is not UTF-8: the `placement` row prints the reader's prose remedy as its NEXT command. - #410: A crash of the standard's own naming script, or a directory name that is not UTF-8: the doctor reports `INVALID (naming)` about the project. - #413: A leg or member whose detached HEAD holds the only copy of a commit: pasting the `legs` or `members` NEXT moves it, so only the reflog keeps the commit. - #414: A mount with no `.git`, an unborn HEAD, no `.gitmodules` section, or a pin that is not 40 hex: the `legs` NEXT is the wrong command, or leaves the row red. @@ -481,22 +490,21 @@ limit stays open under its area's tracking issue. - #439: A registered-kind file at a path no check reads (such as `contracts/project.yaml` in an assembly root): the doctor counts it validated and answers `COMPLIANT`. - #447: A validator killed by a signal, or one that exits with nothing printed: the doctor reports a finding about the project, not a crash. - #448: A `WARNING` from the project's own `validate-manifest.py`, such as an unreachable referent chain: the `manifest` row says `ok`, and the warning is not shown. -- An ordinary git repository: `NOT A SHAPE ROOT` names only the `adopt-project.py plan` command, though AGENTS.md says to report both ways in. -- A machine that is not ready: the `machine` row quotes the preflight's `(see above)` with nothing above it, because the preflight's lines are kept only in `--json`. +- An ordinary git repository: `NOT A SHAPE ROOT` names only the `adopt-project.py plan` command, though AGENTS.md says to report both ways in (#545). +- A machine that is not ready: the `machine` row quotes the preflight's `(see above)` with nothing above it, because the preflight's lines are kept only in `--json` (#549). - The README's `validate-pins.py` paragraph: it no longer says that a digest that recomputes differently is the finding `neutral-pin-digest-mismatch`, exit 1. -- A pinned copy edited and left uncommitted: the `apply --branch` the `shape currency` row names after its `check` is refused `update-branch-uncommitted`, and the row does not say to commit or revert the edit first. - A merge left unfinished inside a leg: the `merge` row reads only the root's own index, so it is not reported. -- A hand-edited `@` in a workflow, or a leg pin whose digest no longer matches: `validate-pins.py` ends the finding at THE LOCKSTEP RULE and names no command. +- A hand-edited `@` in a workflow, or a leg pin whose digest no longer matches: `validate-pins.py` ends the finding at THE LOCKSTEP RULE and names no command (#539). - A passing `validate-pins.py` that prints a note, such as `neutral-pin-source-collides`: the `pins` row says `ok` and drops the note. -- A NEXT argument that begins with `=`, such as a branch named `=x`: the doctor leaves it bare, and zsh expands a bare `=word`. -- A conflicted shape pin whose conflicted copies neither side edited: each copy goes to a person to merge by hand, and keeping both sides of a hunk, then `--accept-local`, pins a stale line as the project's own edit. -- A leg pin broken in the bump's own commit: the `manifest kinds` NEXT restores the pin from before the bump, and the doctor then reads `DRIFTED`. Nothing says to commit it and run the `pins` row's `bump-leg.py` line. -- An unregistered kind no gate here runs, or a `family.yaml` beside an assembly root: the `manifest kinds` NEXT is prose, with `family.py` commands in backticks that an interactive zsh would run if the line were pasted. +- A NEXT argument that begins with `=`, such as a branch named `=x`: the doctor leaves it bare, and zsh expands a bare `=word` (#532). +- A conflicted shape pin whose conflicted copies neither side edited: each copy goes to a person to merge by hand, and keeping both sides of a hunk, then `--accept-local`, pins a stale line as the project's own edit (#547). +- A leg pin broken in the bump's own commit: the `manifest kinds` NEXT restores the pin from before the bump, and the doctor then reads `DRIFTED`. Nothing says to commit it and run the `pins` row's `bump-leg.py` line (#547). +- An unregistered kind no gate here runs, or a `family.yaml` beside an assembly root: the `manifest kinds` NEXT is prose, with `family.py` commands in backticks that an interactive zsh would run if the line were pasted (#548). - The test that holds every NEXT to a bare command checks each string constant alone, so it would miss a ` # ` tail split across a `+`, or one with no blank after the `#`. -- A git repository with no `origin`: the doctor's adoption NEXT carries no `--org`, so pasted, it is refused `adopt-no-org`. -- `openRepoShape --doctor --placement-plan a.yaml --placement-plan b.yaml`: only the last plan is written, without a word. -- A relative `--placement-plan ` given from inside the repository: it writes that file there, though `openRepoShape`'s usage and refusal lines, the README and the handbook say the doctor writes nothing into the repository. -- The kept clone's last line: it says any NEXT above runs from the clone, but the `pins` row's NEXT names no file in it, and the `placement` row's relative `--placement-plan`, run from the clone, writes the plan into the directory the line calls safe to delete. +- A git repository with no `origin`: the doctor's adoption NEXT carries no `--org`, so pasted, it is refused `adopt-no-org` (#545). +- `openRepoShape --doctor --placement-plan a.yaml --placement-plan b.yaml`: only the last plan is written, without a word (#546). +- A relative `--placement-plan ` given from inside the repository: it writes that file there, though `openRepoShape`'s usage and refusal lines, the README and the handbook say the doctor writes nothing into the repository (#546). +- The kept clone's last line: it says any NEXT above runs from the clone, but the `pins` row's NEXT names no file in it, and the `placement` row's relative `--placement-plan`, run from the clone, writes the plan into the directory the line calls safe to delete (#546). - A TERM sent to the installed doctor's shell alone, mid-run: it deletes the kept clone under the still-running doctor. - An interrupted `openRepoShape --doctor` (INT, TERM, HUP): it deletes its clone, but no test holds that. - The installed doctor's NEXT lines: only a drifted project's are tested. A plain repository's were run by hand. @@ -506,20 +514,19 @@ limit stays open under its area's tracking issue. - #415: A Windows root whose path holds a space: no test runs the PowerShell form of the leg NEXT line, and its quoting test passes only because temporary paths need no quoting. - #429: Looking up `apply --add`'s refusals in the README or the handbook: `update-add-symlink` and `update-add-not-a-directory` are not listed. - #430: A directory the user cannot search, on Python 3.14: the shipped tools treat it as absent, and skip it or misreport it instead of refusing. -- A test fixture that clones a just-pushed local remote by plain path (about a dozen of them, and `siblings.py`): it can race git's background maintenance. None has failed in CI. -- An unreadable root pin in the doctor's tests: one test still accepts exit 3, where only exit 1 (`INVALID`) is correct. -- Two test modules that load the family template's scripts: they leave `.pyc` files in the template tree, so a test that reads every template file could pass or fail by order. -- README's by-hand version of what setup runs: its dry run omits the real run's `--visibility` and `--elected-by`, so the plan it shows can name a different elector than the real run writes. -- The adopt suites run without `--work-dir` and set no `TMPDIR` of their own, so every test run leaves `openreposhape-adopt-*` directories in the system temp folder. -- No test holds that a bare or absent `source:` or `legs:` in a plan is no type finding: two mutants survive that file's tests. +- A test fixture that clones a just-pushed local remote by plain path (about a dozen of them, and `siblings.py`): it can race git's background maintenance. None has failed in CI (#544). +- An unreadable root pin in the doctor's tests: one test still accepts exit 3, where only exit 1 (`INVALID`) is correct (#544). +- Two test modules that load the family template's scripts: they leave `.pyc` files in the template tree, so a test that reads every template file could pass or fail by order (#544). +- README's by-hand version of what setup runs: its dry run omits the real run's `--visibility` and `--elected-by`, so the plan it shows can name a different elector than the real run writes (#550). +- The adopt suites run without `--work-dir` and set no `TMPDIR` of their own, so every test run leaves `openreposhape-adopt-*` directories in the system temp folder (#526). +- No test holds that a bare or absent `source:` or `legs:` in a plan is no type finding: two mutants survive that file's tests (#544). ## Before tagging (remove at tag time) -Open `v1.0.0` milestone issues at drafting time (2 open, 45 closed): +Open `v1.0.0` milestone issues at drafting time (1 open, 46 closed): -- #471: The Windows CI job never runs an adoption, because `git-filter-repo` is not installed there, so adoption on Windows is unverified. - #472: Nothing in the repository yet says what the release covers or lists its known limits. #181 is not in the milestone. It is listed above as a known limit. From 90dfaff6334edda8ff731f54162c75ffb750e497 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Wed, 14 Oct 2026 19:07:38 +0000 Subject: [PATCH 7/7] docs/release-notes/v1.0.0.md: brought up to main b8b7062, so section 5 cites #518, section 6 drops #181's line, which #518 closed, adds the three limits lane 4's reviews posted on #458 and #460 after the triage and extends two #544 lines with what #461's body now says, and section 7 no longer says #181 is outside the milestone (#472) Refresh round 3c, the last before the v1.0.0 tag. Main is b8b706275aa2 (#518, merged 2026-10-14T18:50:03Z, closing #181), the only merge since round 3b's 5ed3e5de. All 227 merge commits are ancestors of it. The notes describe nothing on an unlanded pull request. Section 5: one adopt-project.py bullet, "Keywords as typed", written from #518's squash message with its body beside it. Read in main's code: adopt-project.py `_PLAIN_RE` (:580), `loader_reading` (:583), `_reads_back_as_text` (:603), `y` (:625-637), `MANIFEST_MISREAD_REMEDIATION` (:2216), `_misread_findings` (:2587, `plan-manifest-misread`), `_refuse_misread_values` (:2939, `adopt-manifest-misread`), `_entry_findings`' return (:5354), `_refuse_what_check_finds` (:7576, called :8206); scripts/shape_materialize.py `leg_path_problem` (:3492, a null leg path is "not text"); templates/assembly-root/project.yaml quotes each placeholder the gate asks about (:31, :42, :46, :47, :62, :67, :159, :163). Run offline, with a stub `gh` that was never called, at b8b70627 and at 5ed3e5de: `--spec-path null` mounts the leg at `null/` (at 5ed3e5de, at `spec`), `--id null` writes `id: "null"` (`"northwind"`), `--tracking-branch true` on a source with that branch puts both legs on `true` (refused `plan-tracking-branch-missing`), a hand-edited `spec_path: null` is `plan-bad-leg-path` (`plan ok`), and the default flags adopt as before. The gate's "names none today" rests on the templates' quotes and on lane 1's fuzz (286 values, 0 rows), not on a run. Section 6: #181's line is dropped (a source with files named `null` and `false` now plans them quoted, asks the ordinary `plan-unresolved` question, and adopts once it is answered; probed). #457 has no new comment and an unchanged body since 16:09:31Z. L1-J1, J2, J3, K1 and K8 hold on main (`check` halves probed; K8 read at adopt-project.py:5861), K3 (`id: 007` written as `"7"`) and K7 (two findings for a tab in a pin's product) probed again. K4 and K6 stay out: nothing a person meets. Added from lane 4's 2026-10-14T18:32Z posts, which are untriaged and so carry no number: on #458 a quoted or spaced `files:` or `shape:` key (read at update-shape.py:1378 and :1502, and run in process for the pin; `check`'s exit and the manifest half rest on R558's measurement) and `bump-leg.py --dry-run` over a pin its validators refuse (read at bump-leg.py:1475 against :1388-1396; the tab-line example rests on R561); on #460 an unparsable standard policy file (shape-doctor.py:1723 read; the exit 2 rests on R557). Left out: #458's two R556 lines (a wording imprecision; and a refusal that is by design on main, bootstrap.py:275), #459's two R557 lines (each repeats a line the notes already hold, #443's and the `UnicodeEncodeError` one), and #461's optional hygiene-test line. #461's other two new lines extend the two #544 lines: four `git submodule update --init` runs in two test files and test_adopt_plan_yaml_quoting.py:2036 (read), and tests/test_cli_reference.py's `--help` children (render-cli-reference.py's `child_env` sets no PYTHONDONTWRITEBYTECODE; the `.pyc` itself rests on #554's review). Section 7: the sentence about #181 is gone. The v1.0.0 milestone stands at 1 open (#472) and 46 closed. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Sonnet 5.5 --- docs/release-notes/v1.0.0.md | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/docs/release-notes/v1.0.0.md b/docs/release-notes/v1.0.0.md index 12e10442..3f6e7ee1 100644 --- a/docs/release-notes/v1.0.0.md +++ b/docs/release-notes/v1.0.0.md @@ -100,6 +100,17 @@ pin. (`adopt-not-the-source`). `check` and `execute` refuse a plan edited to name one (`plan-not-the-source`), and a source whose own name the naming policy refuses is told to rename it first (#513). +- **Keywords as typed.** `plan` writes a value bare only where the project's + own YAML loader reads it back as that text, so `--spec-path null`, + `--id null` and `--tracking-branch true`, on a source with a branch `true`, + adopt as typed. Before, the first mounted the leg at `spec`, the second made + the id `northwind`, and the third was refused about a value nobody typed. An + explicit null leg path in a plan (`spec_path: null`) is a + `plan-bad-leg-path` finding, not the default `spec`. A backstop + (`adopt-manifest-misread`, `plan-manifest-misread`) refuses, before any leg + exists, a value the loader would read back as something else from the line + `execute` writes. The shipped templates quote those lines, so it names none + today (#518). - **Refused before any leg exists.** `execute` refuses everything `check` finds before it creates a leg. That covers bad mount paths, root paths that clash with the shape's files, the source's own `project.yaml` kept in the @@ -343,7 +354,6 @@ with others for one pull request. ### [adopt-project.py](https://github.com/opensoft/openRepoShape/issues/457) -- #181: A source holding a file or directory named `null`, `true` or `false`: `plan` writes the name unquoted, so `check` finds the path in no entry, and an entry that covers nothing, until the plan is fixed by hand. - #388: A source symlink whose target begins with a NUL byte: `check` passes, and `execute` fails after both legs exist, with nothing on stderr. - #389: A plan that keeps the source's own `project.yaml` in the root: `check` and `execute` now refuse it before any leg exists, but verification never checks the root manifest itself, so nothing behind that refusal would catch it. - #417: A relative submodule URL under `--local-remote-dir`: `check` and `execute` resolve it from different remotes, so one can refuse what the other passes. @@ -426,6 +436,8 @@ with others for one pull request. - `apply --branch` without `--pr`: the `gh pr create` it prints carries no `--title`, `--body` or `--fill`, so `gh` asks for them at a prompt (#535). - Merged-away rows beside a pinned copy kept as a symlink: the doctor's `pins` row names `update-shape.py check` and says its `apply` drops their rows, though that `apply` is refused `update-rewrite-symlink`. - A root with `copy-is-link`, `copy-missing`, `upstream-removed`, `unmapped` or `already-at-target` rows: the doctor's `shape currency` counts fall short of its rows. +- The shape pin's `files:` key written quoted (`"files":`) or with a space before the colon, and likewise the manifest's `shape:` key: the validators and `check` accept it, and `apply` refuses `update-pin-unrecognised`, or `update-manifest-no-shape` saying the manifest has no `shape:` block. +- `bump-leg.py --dry-run` over a pin that its own validators already refuse, such as one with a stray tab line: it exits 0 and prints the `old -> new` line, and the real run exits 2 `bump-leg-validators-red` and rolls back. ### [setup, scaffold, family and the shared helpers](https://github.com/opensoft/openRepoShape/issues/459) @@ -508,15 +520,16 @@ with others for one pull request. - A TERM sent to the installed doctor's shell alone, mid-run: it deletes the kept clone under the still-running doctor. - An interrupted `openRepoShape --doctor` (INT, TERM, HUP): it deletes its clone, but no test holds that. - The installed doctor's NEXT lines: only a drifted project's are tested. A plain repository's were run by hand. +- A checkout of the standard whose own `contracts/repository-naming.yaml` does not parse: a compliant project reads `INVALID (naming)`, exit 1, and the `naming` row says `the naming policy is not satisfied (exit 2)` over a `REFUSED yaml-unparsable` that is the standard's file, not the project's. ### [The test suite, CI and the docs](https://github.com/opensoft/openRepoShape/issues/461) - #415: A Windows root whose path holds a space: no test runs the PowerShell form of the leg NEXT line, and its quoting test passes only because temporary paths need no quoting. - #429: Looking up `apply --add`'s refusals in the README or the handbook: `update-add-symlink` and `update-add-not-a-directory` are not listed. - #430: A directory the user cannot search, on Python 3.14: the shipped tools treat it as absent, and skip it or misreport it instead of refusing. -- A test fixture that clones a just-pushed local remote by plain path (about a dozen of them, and `siblings.py`): it can race git's background maintenance. None has failed in CI (#544). +- A test fixture that clones a just-pushed local remote by plain path (about a dozen of them, `siblings.py`, four `git submodule update --init` runs in two test files, and the recursive clone in the adopt quoting test): it can race git's background maintenance. None has failed in CI (#544). - An unreadable root pin in the doctor's tests: one test still accepts exit 3, where only exit 1 (`INVALID`) is correct (#544). -- Two test modules that load the family template's scripts: they leave `.pyc` files in the template tree, so a test that reads every template file could pass or fail by order (#544). +- Three test modules run the family template's scripts, two by loading them and `tests/test_cli_reference.py` through `--help` children that do not set `PYTHONDONTWRITEBYTECODE`: they leave `.pyc` files in the template tree, so a test that reads every template file could pass or fail by order (#544). - README's by-hand version of what setup runs: its dry run omits the real run's `--visibility` and `--elected-by`, so the plan it shows can name a different elector than the real run writes (#550). - The adopt suites run without `--work-dir` and set no `TMPDIR` of their own, so every test run leaves `openreposhape-adopt-*` directories in the system temp folder (#526). - No test holds that a bare or absent `source:` or `legs:` in a plan is no type finding: two mutants survive that file's tests (#544). @@ -529,8 +542,6 @@ Open `v1.0.0` milestone issues at drafting time (1 open, 46 closed): - #472: Nothing in the repository yet says what the release covers or lists its known limits. -#181 is not in the milestone. It is listed above as a known limit. - ## Full list - Merged pull requests: