From 9605d40ca624fc3cb20ce1053c12b2cbd61233d3 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Mon, 12 Oct 2026 19:43:17 +0000 Subject: [PATCH 1/2] README.md, docs/handbook.html and the comments in bump-leg.py and update-shape.py say when a project's own gate reaches a network: `validate-pins.py` asks `gh api` for a neutral product's pin with the operator's `gh` credentials, and `make bootstrap`, `bump-leg.py` and `update-shape.py apply` run it in the operator's environment, so none of them says any more that a project runs its own gate offline or that `apply` is otherwise offline (#411) `templates/assembly-root/scripts/validate-pins.py` looks for a declared neutral product's tree in `--pin-source`, then `SHAPE_PIN_SOURCE_`, then a checkout beside the assembly root, and only then runs `gh api repos///git/trees/?recursive=1`; with no `gh`, no login or no network that is a `skip` line and exit 0. `make bootstrap`, `bump-leg.py` and `update-shape.py apply` start it with their own environment and none takes `--pin-source`, so for a project declaring `neutral_product_pins:` with no local checkout of the product each makes that call with whatever `gh` is logged in as. Re-measured on dd8d0ea with a `gh` stub first on PATH, GH_TOKEN=bogus and an empty GH_CONFIG_DIR: the same call, with GH_TOKEN, from all three; no call with a sibling checkout, with `SHAPE_PIN_SOURCE_OPENGLASS` exported, or for a project declaring no neutral product. Nothing in the call is new. Only the words claimed otherwise. - README.md, in the paragraph that documents `validate-pins.py`'s referent recheck: the call is `gh api` with the operator's `gh` credentials, a skip is exit 0, the three tools run the gate in the operator's environment and so make it too, and none takes `--pin-source`. - README.md, "Keeping a project's shape current": "a project runs its own gate offline" is "a project runs its own gate, which never consults this standard", naming the one possible network read; the paragraph's 2026-09-03 story loses its list of manual steps, which the `apply` paragraph below already gives. - README.md, the `make bootstrap` paragraph: "It adds no network call" is "That line adds no network call", said of the upstream-moved notice, with a clause that the pins gate bootstrap runs may ask `gh api`. - docs/handbook.html: the "offline" sentence in "Keeping a project's shape current" and the bootstrap sentence get the same words; the legs section gains the referent-recheck paragraph the page lacked; the footer's blob is the new README.md blob. - scripts/bump-leg.py, `VALIDATORS`' comment: drops "offline" and says the child gets this process's environment and what that costs. - update-shape.py: `open_upstream`'s docstring says the bare clone is the one place the tool fetches the shape, not the only place `apply` touches a network, and names the other two; its refusal's remediation says "the one step that fetches the shape from a network" and "nothing after it fetches the shape again" where it said "needs a network" and "everything after it is offline"; `tracking_branch_of` no longer says `apply` is otherwise offline. - tests/test_update_shape_pr_base.py: its module docstring's "`apply` is otherwise offline" is "the base comes from the manifest". Unchanged: every tool's behaviour, exit codes and printed lines, except the one remediation sentence above; `--help` text (docs/cli.md is not regenerated); README.md's line count (1419, the cap); AGENTS.md; every file under `templates/`, so no digest a project records moves. `bootstrap.py`'s comment that "NO NETWORK CALL IS ADDED HERE" is about its upstream notice, which is true, so the pinned copy is left alone. Left out: a test that pins the callers' `gh api` call (it cannot fail on main); starting the three children offline as #357 did the doctor's; passing a known source to them. Lane: openreposhape-3 (openRepoShape-3) Co-Authored-By: Claude Sonnet 5.5 --- README.md | 40 +++++++++++++++--------------- docs/handbook.html | 34 +++++++++++++++++++------ scripts/bump-leg.py | 10 ++++++-- tests/test_update_shape_pr_base.py | 2 +- update-shape.py | 21 ++++++++++------ 5 files changed, 68 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index e8da9381..d25081fd 100644 --- a/README.md +++ b/README.md @@ -693,17 +693,17 @@ link that broke. `LedgerxWallet` -> `openXwallet` -> `openWallet` is the same shape, and `openWallet` need not exist anywhere for that to be the answer. **`validate-pins.py` rechecks the referent too, not only the declaration.** -Every `neutral_product_pins:` entry's `contracts/-pin.yaml` -has its `commit`, `revision_kind` and `sorted-ls-tree-r-v1` digest recomputed -exactly the way `scaffold-project.py --pin` computed them: OFFLINE from a -local checkout when one can be found — `--pin-source [PRODUCT=]`, -`SHAPE_PIN_SOURCE_` in the environment, or a checkout sitting beside -the assembly root and named for the pinned repository — else from `gh api` -when `gh` can read it. Neither answering is a named SKIP, never a finding and -never a failure: a project with no local checkout of its referent and no -network is not thereby lying about its descent. A digest that recomputes -differently, or a pin file the declaration names but that is not there, is -reported as drift like every other pin in this file. +Each `neutral_product_pins:` entry's `contracts/-pin.yaml` +has its `commit`, `revision_kind` and `sorted-ls-tree-r-v1` digest recomputed as +`scaffold-project.py --pin` computed them: OFFLINE from a local checkout when +one is found — `--pin-source [PRODUCT=]`, `SHAPE_PIN_SOURCE_` in +the environment, or a checkout beside the assembly root named for the pinned +repository — else by `gh api`, with the operator's `gh` credentials, when `gh` +can read it. Neither answering is a named SKIP, exit 0, never a finding or a +failure: no checkout and no network does not make a descent a lie. `make +bootstrap`, `bump-leg.py` and `update-shape.py apply` run this gate in the +operator's environment, so each makes that call too; none takes `--pin-source`. +A differing digest or missing pin file is drift, as everywhere. ### A neutral product may elect the shape and be its own assembly root @@ -1022,12 +1022,12 @@ the rest is the project's own, which an upstream fix must never overwrite. ## Keeping a project's shape current -The copies are the trade: a project runs its own gate offline, and an upstream -FIX to a copied file reaches it never. On 2026-09-03 that bill came due — one -change to the assembly-root `validate.yml`, and both projects carrying the -shape were updated BY HAND: re-copy, recompute the file's `sha256` row, move -`commit` and `digests.tree_sha256`, mirror both into `project.yaml`, re-run the -validators. `update-shape.py` is that, as one command: +The copies are the trade: a project runs its own gate, which never consults this +standard (its one possible network read is `validate-pins.py`'s `gh api` recheck +of a neutral product's pin, above), and an upstream FIX to a copied file reaches +it never. On 2026-09-03 that bill came due — one change to the assembly-root +`validate.yml`, and both projects with the shape were updated BY HAND: re-copy, +re-pin, re-validate. `update-shape.py` is that, as one command: ```sh ./update-shape.py check --root ../MedxEHR # writes nothing @@ -1089,9 +1089,9 @@ by every `check` until it is taken or a file arrives at that path. `owner/repo` it bare-clones itself; it defaults to the repository the pin names. `make bootstrap` prints one line when the upstream has moved past the pin AND this machine can already tell offline — `SHAPE_UPSTREAM_PATH` points at -a clone, or a `.shape-upstream-tip` file carries a commit. It adds no network -call: a bootstrap that paused to ask GitHub a question would have made the -shape a dependency again. +a clone, or a `.shape-upstream-tip` file carries a commit. That line adds no +network call: a bootstrap that paused to ask GitHub a question would have +made the shape a dependency again. (Its pins gate may ask `gh api`; see above.) ### Is this repository compliant? `openRepoShape --doctor` diff --git a/docs/handbook.html b/docs/handbook.html index 5eddf825..722654a9 100644 --- a/docs/handbook.html +++ b/docs/handbook.html @@ -1107,6 +1107,19 @@

The three legs, and naming

project’s own project.yaml, so classification stays OFFLINE and never asks GitHub whether open<Product> exists. The referent may also be reached through a RECORDED chain of pins.

+

validate-pins.py rechecks the referent too, not only the declaration. Each + neutral_product_pins: entry’s + contracts/<product lowercased>-pin.yaml has its commit, + revision_kind and sorted-ls-tree-r-v1 digest recomputed as + scaffold-project.py --pin computed them: OFFLINE from a local checkout when one + is found — --pin-source [PRODUCT=]<path>, + SHAPE_PIN_SOURCE_<PRODUCT> in the environment, or a checkout beside the + assembly root named for the pinned repository — else by gh api, with the + operator’s gh credentials, when gh can read it. Neither + answering is a named SKIP, exit 0, never a finding or a failure: no checkout and no network + does not make a descent a lie. make bootstrap, bump-leg.py and + update-shape.py apply run this gate in the operator’s environment, so each + makes that call too; none takes --pin-source.

A NEUTRAL product may elect the shape and be its own assembly root as well: openDox mounting openDox-spec and openDox-code is a fact about LAYOUT and not a claim about neutrality, because electing confers nothing. @@ -1357,9 +1370,11 @@

Bootstrap is COPIED into the project, not fetched

Reference

Keeping a project’s shape current

-

The copies are the trade: a project runs its own gate offline, and an upstream FIX to a - copied file reaches it never. update-shape.py is the re-sync, as one - command.

+

The copies are the trade: a project runs its own gate, which never consults this standard + (its one possible network read is validate-pins.py’s gh api + recheck of a neutral product’s pin, described with the legs), and an + upstream FIX to a copied file reaches it never. update-shape.py is the re-sync, + as one command.

./update-shape.py check --root ../MedxEHR          # writes nothing
 ./update-shape.py apply --root ../MedxEHR --at <commit> --yes \
     --branch shape/update-<sha>                    # then open a pull request
@@ -1394,8 +1409,9 @@

Keeping a project’s shape current

bare-clones itself; it defaults to the repository the pin names. make bootstrap prints one line when the upstream has moved past the pin AND this machine can already tell offline — SHAPE_UPSTREAM_PATH points at a clone, or a - .shape-upstream-tip file carries a commit. It adds no network call: a bootstrap - that paused to ask GitHub a question would have made the shape a dependency again.

+ .shape-upstream-tip file carries a commit. That line adds no network call: a + bootstrap that paused to ask GitHub a question would have made the shape a dependency again. + (Its pins gate may ask gh api; see the legs.)

Full text — keeping the shape current
@@ -1790,9 +1806,11 @@

For AI assistants

refusing an untracked or ignored one it would rewrite, added (#207); the assistant rules saying that some scaffold refusals name a value to change, added (#288); bump-leg.py refusing a workflow file or the leg’s pin that is not UTF-8, by - name, added (#289) — a page cannot name the commit that contains it, and the blob below - is the fact a test checks. - README.md blob e8da93817b0c1a195300800373a2895c0e0280e5. Every command, flag, exit code + name, added (#289); the one network read a project’s own gate can make, the + gh api recheck of a neutral product’s pin, said where this page and the + README had said “offline”, added (#411) — a page cannot name the commit + that contains it, and the blob below is the fact a test checks. + README.md blob d25081fd5675d608aa9f362cb901b8db819481ef. Every command, flag, exit code and file name on this page is quoted from that README and from AGENTS.md.

diff --git a/scripts/bump-leg.py b/scripts/bump-leg.py index d00af077..6ef97022 100755 --- a/scripts/bump-leg.py +++ b/scripts/bump-leg.py @@ -106,8 +106,14 @@ MANIFEST = "project.yaml" #: The root's OWN validators, run after the rewrite and before the commit. -#: Copies, not this checkout's: a project runs its own gate offline, and the -#: only opinion that matters here is the one its pull request will report. +#: Copies, not this checkout's: a project runs its own gate, and the only +#: opinion that matters here is the one its pull request will report. +#: NOT NETWORK-FREE, and the child gets this process's environment (#411): for +#: a project declaring `neutral_product_pins:` whose product has no local +#: checkout (a sibling of the root or `SHAPE_PIN_SOURCE_`; this tool +#: takes no `--pin-source`), `validate-pins.py` asks `gh api` for the pinned +#: tree with the operator's `gh` credentials. Without `gh`, a login or a +#: network that is its `skip` line, exit 0, and the bump goes on. VALIDATORS = ("scripts/validate-pins.py", "scripts/validate-manifest.py") #: A local-path remote is a `file://` fetch, which git has refused for diff --git a/tests/test_update_shape_pr_base.py b/tests/test_update_shape_pr_base.py index 90d309d2..c895b4b1 100644 --- a/tests/test_update_shape_pr_base.py +++ b/tests/test_update_shape_pr_base.py @@ -16,7 +16,7 @@ manifest records NO tracking branch at all — which `validate-manifest.py` accepts and which every other tool (`bootstrap.py`, `bump-leg.py`, `family.py`, `commit_on_branch`'s own refusal) reads as `main`. The remote's -HEAD is never consulted for that last case: `apply` is otherwise offline. +HEAD is never consulted for that last case: the base comes from the manifest. THE UPSTREAM IS A CLONE OF THIS REPOSITORY, the project is scaffolded out of it into bare repositories in a temporary directory, and `gh` is stubbed at the diff --git a/update-shape.py b/update-shape.py index 3dae6df0..e1ffdfd0 100755 --- a/update-shape.py +++ b/update-shape.py @@ -323,8 +323,14 @@ def open_upstream(spec: str) -> Upstream: A PATH IS USED IN PLACE AND NOTHING IS FETCHED. That is deliberate: it is what lets the tests run with no network, and it is also the honest answer for an organisation whose fork is the upstream. `owner/repo` is bare-cloned - into a temporary directory, which is the one place this tool touches a - network, and it refuses loudly rather than degrading to a guess. + into a temporary directory, which is the one place this tool fetches the + shape, and it refuses loudly rather than degrading to a guess. It is not + the only place `apply` can touch a network (#411): the project's own + `validate-pins.py` runs in this process's environment, so for a project + declaring `neutral_product_pins:` whose product has no local checkout it + asks `gh api` for the pinned tree with the operator's `gh` credentials + (`skip` line and exit 0 without `gh`, a login or a network), and `--push` + and `--pr` push the branch and open its pull request. """ candidate = Path(spec).expanduser() if candidate.exists(): @@ -356,9 +362,9 @@ def open_upstream(spec: str) -> Upstream: "update-upstream-unresolvable", f"could not clone {url} (git exit {proc.returncode}): " f"{(proc.stderr or '').strip()}", - "Remediation: this is the one step that needs a network. Clone " - "the shape once by hand and pass `--upstream ` instead; " - "everything after it is offline.") + "Remediation: this is the one step that fetches the shape from a " + "network. Clone the shape once by hand and pass `--upstream " + "` instead; nothing after it fetches the shape again.") return Upstream(temporary / "shape.git", spec, temporary=temporary) @@ -1731,9 +1737,8 @@ def tracking_branch_of(manifest: dict | None) -> str: `main` where the manifest records none: the default `bootstrap.py`, `bump-leg.py` and `family.py` read the field with. The remote's own HEAD - is never asked: `apply` is otherwise offline, and a base taken from the - network could disagree with the one the `--branch` refusal and `--pr` - read from the manifest. + is never asked: a base taken from the network could disagree with the + one the `--branch` refusal and `--pr` read from the manifest. """ return str((manifest or {}).get("tracking_branch") or "main") From 892ec7d2450ca06454d0f106b74f1730bd21f727 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Mon, 12 Oct 2026 20:04:02 +0000 Subject: [PATCH 2/2] README.md, docs/handbook.html and `bump-leg.py --local-remote-dir`'s help say that the validators, not the whole gate, make the one possible network read, that a checkout found beside the root that cannot answer is the finding `neutral-pin-source-unreadable` and not a skip, and that the test path's leg needs no network while `validate-pins.py` may still ask `gh api`, so none of them claims more than the code does (#411) The review of the first commit found three words that said more than they could. - "its one possible network read" was said of the project's gate. The same paragraph names `validate.yml`, which is part of the gate and reads the network in the root checkout, the App-token mint and `git submodule update` of the legs, none of it through `gh api`. It is now "its validators' one possible network read" in README.md and in the handbook's paragraph, and the handbook's footer clause says "validators" too. The footer's README blob is the new `git hash-object README.md`. - "Neither answering is a named SKIP, exit 0, never a finding or a failure" held only when no source is found. `../` is taken whenever it has a `.git`, and one that lacks the pinned commit is the finding `neutral-pin-source-unreadable`, exit 1 (`bootstrap.py` prints `bootstrap FAILED: pins (exit 1)`; `bump-leg.py` and `apply` refuse), with no `gh` call. README.md and the handbook now say "Finding neither is a named SKIP, exit 0", and name that finding. To keep README.md at 1419 lines, the paragraph drops the sentence that a differing digest or a missing pin file is drift, which every validator finding already is. - `bump-leg.py --local-remote-dir` said "(the TEST path; no network)", yet the project's own `validate-pins.py` runs in the same process's environment and may still call `gh api`. The help now says "the leg needs no network, but the project's own validate-pins.py may still ask `gh api`", and docs/cli.md is regenerated with `scripts/render-cli-reference.py`. Unchanged: every tool's behaviour, exit codes and printed lines; README.md's line count (1419, the cap); AGENTS.md (at its cap; the nit that it never says the call exists stays with the issue author); every file under `templates/`. Lane: openreposhape-3 (openRepoShape-3) Co-Authored-By: Claude Sonnet 5.5 --- README.md | 20 ++++++++++---------- docs/cli.md | 4 +++- docs/handbook.html | 23 ++++++++++++----------- scripts/bump-leg.py | 5 +++-- 4 files changed, 28 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index d25081fd..4ce951da 100644 --- a/README.md +++ b/README.md @@ -699,11 +699,11 @@ has its `commit`, `revision_kind` and `sorted-ls-tree-r-v1` digest recomputed as one is found — `--pin-source [PRODUCT=]`, `SHAPE_PIN_SOURCE_` in the environment, or a checkout beside the assembly root named for the pinned repository — else by `gh api`, with the operator's `gh` credentials, when `gh` -can read it. Neither answering is a named SKIP, exit 0, never a finding or a -failure: no checkout and no network does not make a descent a lie. `make -bootstrap`, `bump-leg.py` and `update-shape.py apply` run this gate in the -operator's environment, so each makes that call too; none takes `--pin-source`. -A differing digest or missing pin file is drift, as everywhere. +can read it. Finding neither is a named SKIP, exit 0: no checkout and no network +does not make a descent a lie. A checkout that cannot answer is the finding +`neutral-pin-source-unreadable`. `make bootstrap`, `bump-leg.py` and +`update-shape.py apply` run this validator in the operator's environment, so +each makes that call too; none takes `--pin-source`. ### A neutral product may elect the shape and be its own assembly root @@ -1023,11 +1023,11 @@ the rest is the project's own, which an upstream fix must never overwrite. ## Keeping a project's shape current The copies are the trade: a project runs its own gate, which never consults this -standard (its one possible network read is `validate-pins.py`'s `gh api` recheck -of a neutral product's pin, above), and an upstream FIX to a copied file reaches -it never. On 2026-09-03 that bill came due — one change to the assembly-root -`validate.yml`, and both projects with the shape were updated BY HAND: re-copy, -re-pin, re-validate. `update-shape.py` is that, as one command: +standard (its validators' one possible network read is `validate-pins.py`'s `gh +api` recheck of a neutral product's pin, above), and an upstream FIX to a copied +file reaches it never. On 2026-09-03 that bill came due — one change to the +assembly-root `validate.yml`, and both projects with the shape were updated BY +HAND: re-copy, re-pin, re-validate. `update-shape.py` is that, as one command: ```sh ./update-shape.py check --root ../MedxEHR # writes nothing diff --git a/docs/cli.md b/docs/cli.md index 94b40038..a6a26ba1 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -579,7 +579,9 @@ options: writes; repeatable, kept in the order given --local-remote-dir LOCAL_REMOTE_DIR resolve the leg's remote to a bare repository here - instead of its origin (the TEST path; no network) + instead of its origin (the TEST path; the leg needs no + network, but the project's own validate-pins.py may + still ask `gh api`) --dry-run print what would move and change nothing ``` diff --git a/docs/handbook.html b/docs/handbook.html index 722654a9..ec426119 100644 --- a/docs/handbook.html +++ b/docs/handbook.html @@ -1115,11 +1115,12 @@

The three legs, and naming

is found — --pin-source [PRODUCT=]<path>, SHAPE_PIN_SOURCE_<PRODUCT> in the environment, or a checkout beside the assembly root named for the pinned repository — else by gh api, with the - operator’s gh credentials, when gh can read it. Neither - answering is a named SKIP, exit 0, never a finding or a failure: no checkout and no network - does not make a descent a lie. make bootstrap, bump-leg.py and - update-shape.py apply run this gate in the operator’s environment, so each - makes that call too; none takes --pin-source.

+ operator’s gh credentials, when gh can read it. Finding + neither is a named SKIP, exit 0: no checkout and no network does not make a descent a lie. A + checkout that cannot answer is the finding neutral-pin-source-unreadable. + make bootstrap, bump-leg.py and update-shape.py apply + run this validator in the operator’s environment, so each makes that call too; none + takes --pin-source.

A NEUTRAL product may elect the shape and be its own assembly root as well: openDox mounting openDox-spec and openDox-code is a fact about LAYOUT and not a claim about neutrality, because electing confers nothing. @@ -1371,10 +1372,10 @@

Bootstrap is COPIED into the project, not fetched

Reference

Keeping a project’s shape current

The copies are the trade: a project runs its own gate, which never consults this standard - (its one possible network read is validate-pins.py’s gh api - recheck of a neutral product’s pin, described with the legs), and an - upstream FIX to a copied file reaches it never. update-shape.py is the re-sync, - as one command.

+ (its validators’ one possible network read is validate-pins.py’s + gh api recheck of a neutral product’s pin, + described with the legs), and an upstream FIX to a copied file reaches it + never. update-shape.py is the re-sync, as one command.

./update-shape.py check --root ../MedxEHR          # writes nothing
 ./update-shape.py apply --root ../MedxEHR --at <commit> --yes \
     --branch shape/update-<sha>                    # then open a pull request
@@ -1806,11 +1807,11 @@

For AI assistants

refusing an untracked or ignored one it would rewrite, added (#207); the assistant rules saying that some scaffold refusals name a value to change, added (#288); bump-leg.py refusing a workflow file or the leg’s pin that is not UTF-8, by - name, added (#289); the one network read a project’s own gate can make, the + name, added (#289); the one network read a project’s own validators can make, the gh api recheck of a neutral product’s pin, said where this page and the README had said “offline”, added (#411) — a page cannot name the commit that contains it, and the blob below is the fact a test checks. - README.md blob d25081fd5675d608aa9f362cb901b8db819481ef. Every command, flag, exit code + README.md blob 4ce951da2a58bd100b4323281549d25314c61ea4. Every command, flag, exit code and file name on this page is quoted from that README and from AGENTS.md.

diff --git a/scripts/bump-leg.py b/scripts/bump-leg.py index 6ef97022..73ff2d20 100755 --- a/scripts/bump-leg.py +++ b/scripts/bump-leg.py @@ -1314,8 +1314,9 @@ def build_parser() -> argparse.ArgumentParser: "order given") parser.add_argument("--local-remote-dir", type=Path, default=None, help="resolve the leg's remote to a bare repository " - "here instead of its origin (the TEST path; no " - "network)") + "here instead of its origin (the TEST path; the " + "leg needs no network, but the project's own " + "validate-pins.py may still ask `gh api`)") parser.add_argument("--dry-run", action="store_true", help="print what would move and change nothing") return parser