diff --git a/README.md b/README.md
index 12c3e498..2dc61e44 100644
--- a/README.md
+++ b/README.md
@@ -476,8 +476,10 @@ manifest, sets the topic `xf-project-atlas` on all three, clones the root as
> created. A failed push says so in as many words — *NOTHING has been rolled
> back. What already exists is listed above; delete it by hand if you want a
> clean re-run* — and it is no less true of a failure anywhere else. A second
-> run is refused while they exist, and there is no `--force`: delete what was
-> created — `gh repo delete /`, which needs the `delete_repo` scope
+> run is refused while they exist, at the plan in (3), before the `yes`: the
+> plan is the scaffold's `--dry-run`, which asks whether each of the three
+> already exists. There is no `--force`: delete what was created —
+> `gh repo delete /`, which needs the `delete_repo` scope
> (`gh auth refresh -s delete_repo`) — or scaffold under a different
> `--project`, or re-run with `--reuse-empty-repo`. That reuses each of the
> three that has ZERO commits, a reserved name rather than a project, and a
@@ -510,9 +512,9 @@ commits, not on a detached HEAD.
**If the legs are private**, CI needs a credential to fetch them: a GitHub
App with Contents read on the legs, registered as `SHAPE_LEGS_APP_ID` and
`SHAPE_LEGS_APP_PRIVATE_KEY`, or the `SHAPE_LEGS_TOKEN` fallback — as
-REPOSITORY secrets on the GitHub Free plan, where organisation secrets do not
-reach private repositories (see "Reading private legs in CI" below). Without
-either, `validate` degrades with a warning instead of failing.
+REPOSITORY secrets on the assembly root when it is private or internal and the
+organisation is on the GitHub Free plan (see "Reading private legs in CI"
+below). Without either, `validate` degrades with a warning instead of failing.
**Daily work.** Dana works in `spec/` and `code/` as ordinary repositories:
branches, commits, pull requests in `Atlas-spec` and `Atlas-code`. Committing
@@ -626,9 +628,12 @@ Both `scaffold-project.py` and `adopt-project.py execute` print a one-line
reminder — App first — when they create a private or internal leg.
**On the Free plan, use `--repo`, not `--org`.** GitHub delivers org secrets
-only to PUBLIC repositories, so on a private one `secrets.SHAPE_LEGS_APP_ID`
-is silently empty and `validate` goes GREEN with the pin check skipped. Set
-both per repository, or upgrade to Team; the creating tools check and say so.
+only to PUBLIC repositories, and `validate` runs in the root, so on a private
+or internal root `secrets.SHAPE_LEGS_APP_ID` is silently empty and `validate`
+goes GREEN with the pin check skipped. Set both on the root, or upgrade to
+Team; a public root takes org secrets registered with `--visibility selected`
+and the root in `--repos`, or `all`, which shows them to every repository (the
+commands above default to `private`). The creating tools check and say so.
The root repository is always readable by the workflow's own default token, so
`actions/checkout` never carries a `token:` override. Putting a legs credential
@@ -784,8 +789,9 @@ The contracts decision costs something, and the plan says so instead of letting
it be discovered later: `scripts/validate.py` reads `contracts/*.yaml` from
beside it and must now read ACROSS the root. `plan` finds every such file with
`git grep` and lists up to eight per directory in `follow_ups:`, counting the
-rest; the root Makefile it materializes exports `CONTRACTS_DIR ?=
-$(CURDIR)/spec/contracts`. The tool does not make the edit — naming it is the
+rest; the root Makefile it materializes exports `CONTRACTS_DIR`, pointing at the
+`contracts/` of the spec leg where the plan mounts it (`$(CURDIR)/spec/contracts`
+by default). The tool does not make the edit — naming it is the
honest half, making it silently would be the dangerous one. And `.specify/` and
the ten identical assistant stubs stay at the root because an assistant reading
`CLAUDE.md` from inside the code leg would be told about specifications it
@@ -798,7 +804,11 @@ ambiguous`, first match wins, each with a reason. The fourth class is not a
failure: a classifier that guessed at `examples/golden-run/` would be wrong
about half the time and silent about it. Those entries get `leg: null`,
`review_required: true` and the QUESTION; `execute` refuses while any remain,
-because an unanswered question is never an implicit `root`.
+because an unanswered question is never an implicit `root`. One question comes
+answered: a source's own `single-repository.yaml` record is written `leg: drop`,
+its rule's default, still `review_required: true` with its question beside it,
+and `execute` takes it as written, so it drops the record unless a person
+changes `leg:` (the question offers `root`).
A shape file whose name the source already holds — `README.md`, `Makefile` — is
written under `shape/` as a follow-up to merge, and a kept root `.gitattributes`
diff --git a/docs/handbook.html b/docs/handbook.html
index 1f6e76d1..02c5188e 100644
--- a/docs/handbook.html
+++ b/docs/handbook.html
@@ -472,9 +472,9 @@
-
+
-
+
@@ -610,7 +610,7 @@ The one thing to read before electing it
-
+
Setup
Start here
@@ -1048,7 +1048,7 @@ What you have afterwards — the same on every platform
-
+
Setup
If it fails part-way
@@ -1063,7 +1063,9 @@ If it fails part-way
NOTHING has been rolled back. What already exists is listed above; delete it by hand if you want a clean re-run
— and it is no less true of a failure anywhere else.
- - A second run is refused while they exist, and there is no
--force.
+ - A second run is refused while they exist, at the plan, before the
yes: the
+ plan is the scaffold’s --dry-run, which asks whether each of the three
+ already exists. There is no --force.
- Delete what was created with
gh repo delete <org>/<name>, which
needs the delete_repo scope (gh auth refresh -s delete_repo). Or
scaffold under a different --project, or re-run with
@@ -1083,9 +1085,9 @@ If it fails part-way
-
+
-
+
Setup
Reading private legs in CI
@@ -1106,10 +1108,14 @@ Reading private legs in CI
SHAPE_LEGS_TOKEN PAT, contents:read on the LEGS ONLY.
gh secret set SHAPE_LEGS_TOKEN --org <your-org> --body '<token>'
On the Free plan, use --repo, not --org. GitHub
- delivers org secrets only to PUBLIC repositories, so on a private one
- secrets.SHAPE_LEGS_APP_ID is silently empty and validate goes GREEN
- with the pin check skipped. Set both per repository, or upgrade to Team; the creating tools
- check and say so. Both scaffold-project.py and
+ delivers org secrets only to PUBLIC repositories, and validate runs in the root,
+ so on a private or internal root secrets.SHAPE_LEGS_APP_ID is silently empty and
+ validate goes GREEN with the pin check skipped. Set both on the root, or
+ upgrade to Team; a public root takes org secrets registered with
+ --visibility selected and the root in --repos, or
+ all, which shows them to every repository (the commands above default to
+ private). The creating tools check and say so. Both
+ scaffold-project.py and
adopt-project.py execute print a one-line reminder — App first — when
they create a private or internal leg.
@@ -1141,9 +1147,9 @@ Reading private legs in CI
-
+
-
+
Reference
The three legs, and naming
@@ -1221,7 +1227,7 @@ The three legs, and naming
-
+
Reference
Adopting an existing repository
@@ -1244,7 +1250,12 @@ Adopting an existing repository
spec | code | root | ambiguous, first match wins, each with a reason. The fourth
class is not a failure: those entries get leg: null,
review_required: true and the QUESTION, and execute refuses while
- any remain, because an unanswered question is never an implicit root. A shape
+ any remain, because an unanswered question is never an implicit root. One
+ question comes answered: a source’s own single-repository.yaml record is
+ written leg: drop, its rule’s default, still
+ review_required: true with its question beside it, and execute
+ takes it as written, so it drops the record unless a person changes leg: (the
+ question offers root). A shape
file whose name the source already holds — README.md,
Makefile, .gitignore — is written under shape/ and
the collision becomes a follow-up for a human to merge, and a root
@@ -1377,7 +1388,7 @@ The InkRouter example
-
+
@@ -1674,7 +1685,7 @@ Is this repository compliant? openRepoShape --doctor
-
+
diff --git a/tests/readme-cap.d/606.txt b/tests/readme-cap.d/606.txt
new file mode 100644
index 00000000..40966065
--- /dev/null
+++ b/tests/readme-cap.d/606.txt
@@ -0,0 +1,43 @@
++10
+2026-10-15
+Ten lines for four sentences: three that #566, #572 and #573 left false or
+incomplete in the README once those pull requests landed, and one that #621
+(#602) will leave false once it lands (#606). Each is the words that pull
+request's own body held, as far as the code bore them out, on main or, for
+#621, on its branch.
+One: "Ambiguous is an answer" said every entry the policy cannot call gets
+`leg: null` and that `execute` refuses while any remain. #566 gives one rule,
+a source's own `single-repository.yaml` record, a default: `plan` writes
+`leg: drop`, still `review_required: true` with its question beside it, and
+`execute` takes it as written. A reader who answers every `leg: null` and
+meets that entry would not know it is already answered, and that leaving it
+drops a file. It cannot be shorter: what is written, why it still asks, and
+what `execute` does with it are three things and the sentence says each once
+(+4, five lines where there was one).
+Two: the worked example said the Makefile exports `$(CURDIR)/spec/contracts`,
+which is true of the default mount only; after #572 it is the `contracts/` of
+the spec leg where the plan mounts it, and a reader who mounts it elsewhere
+would look for a directory that is not there. The clause costs one line (+1).
+Three: the "If it fails part-way" box said a second run is refused while the
+repositories exist, with no word on when. After #573 it is the plan, before
+the `yes`, because the plan is the scaffold's `--dry-run` and asks whether
+each of the three exists; a reader who expects the refusal after typing `yes`
+would not look at the plan. The sentence has to say where, and why, and that
+takes two lines (+2).
+Four, written 2026-10-16: once #621 (#602) lands, the Free-plan hint goes by the
+assembly root, where `validate` reads the secrets, and only when the root is
+private or internal; a public root over private legs gets the per-leg note and
+no hint, because on Free the organisation's secrets reach public repositories.
+The README said "REPOSITORY secrets on the GitHub Free plan" of any project
+whose legs are private, which a public root makes false, and "on a private one"
+without saying which repository. The sentence under "Starting a project in a new
+organisation" is reworded in the six lines it had. The paragraph under "Reading
+private legs in CI" has to say that it is the root, that a public root is the
+exception, and on what condition: the `gh secret set --org` commands above it
+default to `--visibility private`, which on Free reaches no repository, so a
+public root told only that it "takes org secrets" would keep the silent green
+with the pin check skipped. The sentence names `selected` with the root in
+`--repos` first, the least-privilege form, and `all` after it with its cost,
+that every repository of the organisation sees the App key (rv627n N1). Naming
+the root and the exception takes one line more than the paragraph had (+1), the
+condition two (+2); that is +3 of the ten. #627 lands after #621.