@orkestrel/scaffold 0.0.55 → 0.0.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -161,9 +161,12 @@ when the role file already pins it.
161
161
  - `implementer` names the harness's native implementation lane, so the token means Opus in Claude
162
162
  Code and Sol in Codex. An engine-named bridge — `sol`, `opus` — names the other engine. Read a
163
163
  role name against the harness you are running in, and state the engine anyway.
164
- - Give every role a file on both sides. The role file is where engine, effort, tools, permissions,
165
- and charter are pinned, and the tool allowlist is what makes the read-only floor real. A role
166
- with no file has nowhere to pin either.
164
+ - Give every role a file in the scaffold checkout, under `.claude/agents/` and under
165
+ `.codex/agents/`. The role file is where engine, effort, tools, permissions, and charter are
166
+ pinned, and the tool allowlist is what makes the read-only floor real. A role with no file has
167
+ nowhere to pin either. The requirement is the canon repository's alone: a fleet target holds the
168
+ catalog agent and no other role, and a session that dispatches roles starts on scaffold and
169
+ attaches the target.
167
170
  - Reach every role by its own name. Do not rely on a remembered route.
168
171
  - `researcher`, `scout`, and `checker` are native lanes for jobs that belong to Grok first.
169
172
  Dispatch `grok` with their brief before using them, and use the native role only once the ladder
@@ -12,7 +12,24 @@ step that writes it.
12
12
 
13
13
  1. Re-pin the target's `@orkestrel/scaffold` devDependency and install, so the overwrite runs the
14
14
  current vendored host.
15
- 2. Run `scaffold overwrite`.
15
+ 2. Run `scaffold overwrite`. One run repairs the `AGENTS.md` and `CLAUDE.md` pointers and deletes
16
+ every tracked copy the target still holds at an instruction-canon path. Prove the sweep with a
17
+ second `scaffold audit` that exits `0`.
18
+ - Where the target's `.claude/agents/orkestrel.md` carries a body outside the marker-bounded
19
+ table that differs from the floor copy the installed scaffold stages, delete the file and
20
+ commit the deletion before the run. `repair` restores the floor body and `catalog` refills the
21
+ table, so one visit leaves the current file and the committed deletion keeps the
22
+ uncommitted-work refusal from firing. Presence ownership never replaces present bytes and the
23
+ table rewrite touches only the marker-bounded region, which is why the deletion is the
24
+ migration.
25
+ - The deletion draws on what git tracks, so an untracked copy survives it, and the verb refuses
26
+ the whole run as uncommitted work while an unignored one stands. Commit that copy or delete it
27
+ by hand before re-running. `--dirty` clears the refusal and leaves the copy standing, and a
28
+ kept `.claude/rules` copy then reddens the target's own policy sweep: the pointer `AGENTS.md`
29
+ carries no rule map, so the copy has no row there and the sweep reports it. Delete the copy
30
+ rather than waiving past it.
31
+ - A copy the target git-ignores stays a `foreign` finding, so that target never reaches exit `0`
32
+ again. Keep a local MCP server registration outside the repository rather than at `.mcp.json`.
16
33
  3. Force-verify every `@orkestrel` range against a registry sweep taken after the previous layer
17
34
  published.
18
35
  4. Run the full install.
@@ -22,7 +39,16 @@ step that writes it.
22
39
 
23
40
  Restore any unpublished tarball the target is holding before the quality gates run, per
24
41
  `.agents/orchestration.md` § Fixing a dependency before it publishes. A distribution proof run
25
- against a local tarball proves the local tarball.
42
+ against a local tarball proves the local tarball. Stage an unpublished tarball with
43
+ `npm install --no-save`, because a `file:` pin refuses the blueprint and the manifest keeps a
44
+ registry range.
45
+
46
+ Where a visit runs before scaffold itself publishes, run `scaffold overwrite --offline` and prove
47
+ the sweep with `scaffold audit --offline`. The online verbs read vendored bytes from the published
48
+ package, so an unpublished scaffold's visit otherwise writes the registry's older floor, and an
49
+ online audit reports the floor-restored files as stale until the release. The `--offline` overwrite
50
+ skips the catalog step and exits `1` with a note naming that refusal, so run the full
51
+ `scaffold overwrite` after the release.
26
52
 
27
53
  Run visits in parallel slices of disjoint repositories, each slice strictly serial inside itself,
28
54
  reporting per target. Refuse a failed target, name it, repair it, and re-run it alone.
@@ -9,8 +9,12 @@ permissionMode: dontAsk
9
9
 
10
10
  You are the read-only Orkestrel ecosystem reconciler. Spawn nothing and edit nothing.
11
11
 
12
- Read `.agents/orchestration.md` first. It owns the role set, the routing, and the
13
- dispatch contract. Then read `AGENTS.md`, applicable rules, the dispatch-named skill and
12
+ Read the orchestration contract first. It owns the role set, the routing, and the
13
+ dispatch contract. Resolve it against scaffold. In the scaffold checkout it sits at
14
+ `.agents/orchestration.md`. A repository that installs scaffold reads it at
15
+ `node_modules/@orkestrel/scaffold/dist/host/agents/orchestration.md`, or in a scaffold
16
+ checkout beside that repository, as that repository's own `AGENTS.md` pointer names. Then
17
+ read `AGENTS.md` itself, the applicable rules it names, the dispatch-named skill and its
14
18
  references, and the governing guides.
15
19
 
16
20
  Your job is reconciliation over supplied evidence, never collection. You have no shell
@@ -60,6 +60,7 @@ A review that reads a diff finds what the diff shows. A review that tries to bre
60
60
 
61
61
  - An instrument is not evidence until it has failed. Pair every probe, comparison, or matrix with a negative control that must report failure, run under the same conditions. An identity check whose control reports "same" has measured nothing.
62
62
  - When a question about a TypeScript edit can supply a workspace project, a case of workspace files with a test, and a negative control naming its files, its test, the stage it must fail at, and why, call the `prove` tool the `probe` MCP server registers before relying on the answer. When the question supplies no project, no case, or no control, follow `.claude/rules/tests.md` § Probes and report the fallback instrument's own control and coverage.
63
+ - When no `probe` server is registered in the session, register one outside the repository, in the harness's own local or user MCP scope — in Claude Code, `claude mcp add` outside project scope — naming the installed `node_modules/@orkestrel/probe/dist/bin/main.js` entry, and start it in the repository whose projects the question names, because the server fixes its workspace from its own working directory. The registration cannot live in the tree: a scaffold target holds no `.mcp.json` file, that path is instruction canon, and a copy at it reports as foreign drift on every `scaffold audit` run. Where the harness registers no server at all, treat the question as supplying no project and take the preceding rule's fallback.
63
64
  - Quote the closing line of the `prove` answer verbatim in every report, brief, and audit verdict that rests on the claim: the `receipt probe:<digest>:…` line when the case ran clean and the control broke exactly where the claim declared it would, and the `no receipt` line otherwise. A `no receipt` line leaves the claim unproved — report it with the stage that refused.
64
65
  - Read a receipt as evidence about its claim, never as a gate result. The gate chain still runs, and `verifier` still owns its result.
65
66
  - Draw the negative control from outside the population the instrument covers. Name the instrument's membership rule first, then pick a control that rule excludes. A control sampled from constructs the instrument already handles proves only that it discriminates among those constructs, and says nothing about the class it silently cannot reach.
@@ -12,6 +12,17 @@ does not work. Scaffold makes the shared set data — a vendored data root shipp
12
12
  — and gives it verbs: create a workspace from it, report how a workspace differs from it, and
13
13
  write the difference back.
14
14
 
15
+ That root stages the vendored set and the instruction canon, and a target meets them differently.
16
+ `HOST_PATHS` names the vendored set — the toolchain, the policy proofs, the bench scripts, and the
17
+ harness permission file — and every target carries its own copy, which the verbs write and compare.
18
+ `CANON_PATHS` names the instruction canon — the coding and orchestration contracts, the rules, the
19
+ skills, the templates, the transport contracts, the agent roles, the bench configuration, and the
20
+ MCP registrations — which stays in one place and is published for reading. A target carries the
21
+ `AGENTS.md` and `CLAUDE.md` pointers that name where a reader finds it, and the catalog agent file
22
+ the `catalog` verb rewrites. It carries nothing else at a canon path: a file found at one is a
23
+ superseded copy, and `overwrite` deletes it. Vendored data root states how each set is staged and
24
+ how a pointer resolves.
25
+
15
26
  Every following code fence is illustrative. [`tests/guides.test.ts`](../tests/guides.test.ts)
16
27
  keeps the command reference aligned with the executable and transcribes the pure blueprint-default,
17
28
  compile-refusal, and error-narrowing fences. A trailing comment in another fence is this guide's
@@ -97,6 +108,7 @@ Exported from `@orkestrel/scaffold`, and reachable from
97
108
  | `BASE_DEV_DEPENDENCIES` | const | The tooling versions scaffold and every generated workspace share. |
98
109
  | `BIN_CONFIGS` | const | The configuration files a workspace that ships its own executable adds, frozen. |
99
110
  | `BIN_ENTRY_PATH` | const | The executable entry whose presence makes a workspace `bin`. |
111
+ | `CANON_PATHS` | const | The instruction-canon paths staged for reading rather than for a target, frozen. |
100
112
  | `CATALOG_AGENT_PATH` | const | The agent file whose marker-bounded package table the catalog verb alone owns. |
101
113
  | `CONFIG_TEMPLATES` | const | Formatter-stable template text for every configuration artifact. |
102
114
  | `CONFORMANCE_TEST_PATH` | const | The official-tooling drift proof whose presence makes a workspace `conformance`. |
@@ -116,7 +128,7 @@ Exported from `@orkestrel/scaffold`, and reachable from
116
128
  | `GROUPS` | const | The `Group` values in plan order, frozen. |
117
129
  | `GUIDES_TEST_PATH` | const | The guide-parity proof whose presence selects the planned `guides` project. |
118
130
  | `HEX_PATTERN` | const | Exact lowercase hexadecimal bytes: two digits per byte, and empty content is valid. |
119
- | `HOST_PATHS` | const | The paths byte-copied from the vendored data root, frozen. |
131
+ | `HOST_PATHS` | const | The paths a target receives from the vendored data root, frozen. |
120
132
  | `HOST_INVENTORY_PATH` | const | The repository-relative path where the committed vendored-file inventory is served. |
121
133
  | `INTEGRATION_TEST_PATH` | const | The cross-environment composition proof whose presence makes a workspace `integration`. |
122
134
  | `INVALID_PATH_CHARACTER_PATTERN` | const | Visible characters a target-relative path and a Markdown path cell both forbid. |
@@ -205,6 +217,7 @@ Exported from `@orkestrel/scaffold`, and reachable from
205
217
  | `extractVersion` | function | Extract the major, minor, and patch components of an exact version. |
206
218
  | `inferDrift` | function | Infer how one target path compares to the artifact planned for it. |
207
219
  | `inferGroup` | function | Infer the `Group` a path belongs to. |
220
+ | `isCanonPath` | function | Test whether a path belongs to the instruction canon a target reads. |
208
221
  | `isDeferredPath` | function | Test whether another surface owns the vendored bytes at a path. |
209
222
  | `manifestToDependencies` | function | Project a manifest's `@orkestrel/*` declarations into separate section lists. |
210
223
  | `manifestToName` | function | Project a package manifest's text to its own name. |
@@ -343,41 +356,43 @@ Exported from `@orkestrel/scaffold/server`, and reachable from
343
356
 
344
357
  #### Helpers
345
358
 
346
- | Name | Kind | Summary |
347
- | ----------------------- | -------- | ------------------------------------------------------------------------------------- |
348
- | `computeDigest` | function | Compute the SHA-256 digest of text. |
349
- | `computeFileDigest` | function | Compute the SHA-256 digest of one file's exact bytes. |
350
- | `computeManifestDigest` | function | Compute the digest of a vendored host's declared membership. |
351
- | `filesToHost` | function | Overlay host-owned live files onto the installed vendored floor. |
352
- | `hexToDigest` | function | Project exact bytes stated in hexadecimal to their SHA-256 digest. |
353
- | `isExactCaseFile` | function | Test whether a physical file's path matches every on-disk segment exactly. |
354
- | `isPhysicalDirectory` | function | Test whether a path is a physical directory this package will read or write into. |
355
- | `isPhysicalFile` | function | Test whether a path is a physical file this package will read or replace. |
356
- | `isVacant` | function | Test whether a target is safe to write a fresh workspace into. |
357
- | `listDirectories` | function | List a directory's descendant directories as sorted root-relative paths. |
358
- | `listFiles` | function | List a directory's files as sorted root-relative paths. |
359
- | `matchesAnchor` | function | Test whether a captured directory is still the same directory. |
360
- | `matchesExecutablePath` | function | Test whether a vendored path is one a target receives executable. |
361
- | `matchesExpectation` | function | Test whether a destination still holds what was captured of it. |
362
- | `matchesGitPath` | function | Test whether a path addresses a target's own repository metadata. |
363
- | `matchesMissingPath` | function | Test whether a caught filesystem error reports an absent path. |
364
- | `matchesPrecondition` | function | Test whether a destination still matches the narrower state a caller observed. |
365
- | `matchesProtectedPath` | function | Test whether a target-relative path is one no verb may delete. |
366
- | `matchesSensitivePath` | function | Test whether a path names local configuration or a credential. |
367
- | `pathToStorage` | function | Project a target-relative path to the storage name a vendored host holds it under. |
368
- | `readAnchor` | function | Capture one directory's physical identity. |
369
- | `readExpectation` | function | Capture what one destination holds before a write. |
370
- | `readFileHex` | function | Read one contained file as its exact bytes in lowercase hexadecimal. |
371
- | `readFileText` | function | Read one contained file as bounded UTF-8 text. |
372
- | `readHostFloor` | function | Read the installed vendored host floor as a verified value. |
373
- | `readHostManifest` | function | Read a vendored host's manifest, when it carries one. |
374
- | `readManifestEntry` | function | Derive one vendored-host manifest entry from a file in a checkout. |
375
- | `readSnapshot` | function | Read a target's current bytes at the paths a plan claims. |
376
- | `resolveContainedPath` | function | Resolve a root-relative path and refuse one that leaves its root. |
377
- | `resolveRealPath` | function | Resolve a path through the real filesystem, keeping the part that does not exist yet. |
378
- | `stageBytes` | function | Stage the named destinations of a value host into a private root. |
379
- | `stageHost` | function | Stage a vendored host root from a real checkout. |
380
- | `stageInventory` | function | Stage the committed vendored-file inventory from a real checkout. |
359
+ | Name | Kind | Summary |
360
+ | ------------------------- | -------- | ------------------------------------------------------------------------------------- |
361
+ | `computeDigest` | function | Compute the SHA-256 digest of text. |
362
+ | `computeFileDigest` | function | Compute the SHA-256 digest of one file's exact bytes. |
363
+ | `computeManifestDigest` | function | Compute the digest of a vendored host's declared membership. |
364
+ | `filesToHost` | function | Overlay host-owned live files onto the installed vendored floor. |
365
+ | `hexToDigest` | function | Project exact bytes stated in hexadecimal to their SHA-256 digest. |
366
+ | `isExactCaseFile` | function | Test whether a physical file's path matches every on-disk segment exactly. |
367
+ | `isPhysicalDirectory` | function | Test whether a path is a physical directory this package will read or write into. |
368
+ | `isPhysicalFile` | function | Test whether a path is a physical file this package will read or replace. |
369
+ | `isVacant` | function | Test whether a target is safe to write a fresh workspace into. |
370
+ | `listCanonPaths` | function | Lists the canon paths a target holds, filtered to a plan's groups. |
371
+ | `listDirectories` | function | List a directory's descendant directories as sorted root-relative paths. |
372
+ | `listFiles` | function | List a directory's files as sorted root-relative paths. |
373
+ | `matchesAnchor` | function | Test whether a captured directory is still the same directory. |
374
+ | `matchesExecutablePath` | function | Test whether a vendored path is one a target receives executable. |
375
+ | `matchesExpectation` | function | Test whether a destination still holds what was captured of it. |
376
+ | `matchesGitPath` | function | Test whether a path addresses a target's own repository metadata. |
377
+ | `matchesMissingPath` | function | Test whether a caught filesystem error reports an absent path. |
378
+ | `matchesPrecondition` | function | Test whether a destination still matches the narrower state a caller observed. |
379
+ | `matchesProtectedPath` | function | Test whether a target-relative path is one no verb may delete. |
380
+ | `matchesSensitivePath` | function | Test whether a path names local configuration or a credential. |
381
+ | `pathToStorage` | function | Project a target-relative path to the storage name a vendored host holds it under. |
382
+ | `pruneEmptiedDirectories` | function | Removes every directory one set of deletions emptied. |
383
+ | `readAnchor` | function | Capture one directory's physical identity. |
384
+ | `readExpectation` | function | Capture what one destination holds before a write. |
385
+ | `readFileHex` | function | Read one contained file as its exact bytes in lowercase hexadecimal. |
386
+ | `readFileText` | function | Read one contained file as bounded UTF-8 text. |
387
+ | `readHostFloor` | function | Read the installed vendored host floor as a verified value. |
388
+ | `readHostManifest` | function | Read a vendored host's manifest, when it carries one. |
389
+ | `readManifestEntry` | function | Derive one vendored-host manifest entry from a file in a checkout. |
390
+ | `readSnapshot` | function | Read a target's current bytes at the paths a plan claims. |
391
+ | `resolveContainedPath` | function | Resolve a root-relative path and refuse one that leaves its root. |
392
+ | `resolveRealPath` | function | Resolve a path through the real filesystem, keeping the part that does not exist yet. |
393
+ | `stageBytes` | function | Stage the named destinations of a value host into a private root. |
394
+ | `stageHost` | function | Stage a vendored host root from a real checkout. |
395
+ | `stageInventory` | function | Stage the committed vendored-file inventory from a real checkout. |
381
396
 
382
397
  #### Classes
383
398
 
@@ -643,11 +658,12 @@ reports it only when its selection includes either group, without changing its e
643
658
  that excludes those groups proceeds, and no verb adds the declaration for you: `package.json` is
644
659
  birth-owned, and the range and script regions are the only parts of it a verb rewrites.
645
660
 
646
- `audit` reports one further non-blocking question, on the `setup` field, and it alone reports it.
647
- The question fires when the target carries a filled root `tests/setup*.ts` module that is neither a
648
- proof itself nor one of the vendored modules every target receives, while no proof of the same stem
649
- covers it. A module counts as filled when its text differs from the seed this blueprint plans at
650
- that same path.
661
+ `audit` reports a further non-blocking question, on the `setup` field.
662
+
663
+ The `setup` question fires when the target carries a filled root `tests/setup*.ts` module that is
664
+ neither a proof itself nor one of the vendored modules every target receives, while no proof of the
665
+ same stem covers it. A module counts as filled when its text differs from the seed this blueprint
666
+ plans at that same path.
651
667
 
652
668
  The comparison reads the module and the seed trimmed, so surrounding whitespace decides nothing: a
653
669
  trailing newline is not authorship, and a module holding whitespace alone reads as empty rather than
@@ -670,24 +686,34 @@ Coverage is read per module: `tests/<name>.ts` is covered by `tests/<name>.test.
670
686
  else, which is the pairing the vendored policy proof resolves. Writing one proof retires that module
671
687
  and leaves every other uncovered module named, and the message pairs each module it names with the
672
688
  proof that module wants. The question belongs to the `tests` group, so a scoped audit that excludes
673
- `tests` omits it. Scaffold does not write the proof it asks for, and no writing verb raises the
674
- question: a writing verb refuses the advisories it reports, and refusing `repair` over this one
675
- would block every write on a gap no write can close. Run across a fleet, the question is the list of
689
+ `tests` omits it. Scaffold does not write the proof it asks for, and the question never refuses a
690
+ write: a writing verb reports it in the terminal audit it prints, because refusing `repair` over a
691
+ gap no write can close would block every write. Run across a fleet, the question is the list of
676
692
  packages carrying a filled setup module that no proof covers.
677
693
 
694
+ `audit` reads the instruction canon as findings rather than as a question. Each `CANON_PATHS` member
695
+ the target holds enters the comparison, by file where the member is a directory, and a path the plan
696
+ does not claim there reports `foreign`. Ownership and drift states that population, and Vendored data
697
+ root states what `overwrite` does with it.
698
+
678
699
  ### Exit codes
679
700
 
680
701
  `0` means the target matched its plan and every step completed. `1` means the target drifted or a
681
702
  step failed. `2` means the command line was not a command. A foreign file counts as drift: the
682
703
  target holds something the plan does not own, whether or not the verb that found it was allowed to
683
- remove it.
704
+ remove it. A superseded instruction copy is such a file, so a target generated before the canon
705
+ split exits `1` until the copy goes.
684
706
 
685
707
  ### Git
686
708
 
687
709
  `overwrite` is the only verb that reads git, and it needs a repository. It asks git for the tracked
688
710
  set and the dirty set, deletes only tracked paths, and refuses a tree carrying uncommitted changes
689
711
  unless `--dirty` waives that refusal. A target that is not a git repository is refused under
690
- `TARGET`, because deletion there would have no recovery mechanism. The other verbs never ask.
712
+ `TARGET`, because deletion there would have no recovery mechanism. The other verbs never ask. A
713
+ git-ignored file sits outside each reading: it never makes the tree dirty and it is never deleted.
714
+ Limits states what that costs a target that keeps one at a canon path. The sweep prunes the
715
+ directories its deletions emptied, so a swept target does not keep the shape of the set it no longer
716
+ holds, and git records no directory to report that shape with.
691
717
 
692
718
  ### Machine-readable output
693
719
 
@@ -887,15 +913,24 @@ than a silent no-op.
887
913
  A plan selects over the following groups, and a compile that names none covers all of them. Their
888
914
  order is the order a plan lists its artifacts in.
889
915
 
890
- | Group | Holds |
891
- | --------------- | ------------------------------------------------------------------ |
892
- | `manifest` | `package.json` |
893
- | `configs` | The root and per-target build configuration, and the root dotfiles |
894
- | `source` | The selected environment barrels and entries |
895
- | `tests` | The shared setup modules, the entry tests, and the policy sweep |
896
- | `guides` | The guide index and the vendored guide mirrors |
897
- | `docs` | `README.md` and the root instruction documents |
898
- | `orchestration` | The harness directories, the bench scripts, and `.mcp.json` |
916
+ | Group | Holds |
917
+ | --------------- | -------------------------------------------------------------------------- |
918
+ | `manifest` | `package.json` |
919
+ | `configs` | The root and per-target build configuration, and the root dotfiles |
920
+ | `source` | The selected environment barrels and entries |
921
+ | `tests` | The shared setup modules, the entry tests, and the policy sweep |
922
+ | `guides` | The guide index and the vendored guide mirrors |
923
+ | `docs` | `README.md` beside the `AGENTS.md` and `CLAUDE.md` pointers |
924
+ | `orchestration` | The harness permission file, the bench scripts, and the catalog agent file |
925
+
926
+ The plan claims paths inside the instruction canon deliberately, and each has a reason. The `docs`
927
+ group carries the `AGENTS.md` and `CLAUDE.md` pointers that name where each contract is read,
928
+ planned at those canon destinations as this package's own template content. The `orchestration`
929
+ group carries `CATALOG_AGENT_PATH`, the host-origin artifact at a canon path, because the `catalog`
930
+ verb refuses a target that lacks the file. Every other canon path is staged for reading, so no group
931
+ selection copies a contract into a target, and a copy a target holds at one of them is foreign drift
932
+ in the group `inferGroup` gives it. A scoped audit reads the canon through that same selection, so a
933
+ run excluding a group reports nothing there.
899
934
 
900
935
  ## Ownership and drift
901
936
 
@@ -927,13 +962,19 @@ The unhydrated row is the one a core-only caller meets most, and reading it as a
927
962
  path is the mistake it invites. `Compiler` runs in the pure core face, which cannot read the
928
963
  vendored data root, so every host artifact it plans carries `presence`: a claim over bytes nobody
929
964
  has read is a claim no comparison could check. A `src: ['core']` plan therefore reports `presence`
930
- for `AGENTS.md`, `.claude/settings.json`, `tests/policy.test.ts`, and every other vendored path, and
931
- a consumer concluding from that reading that scaffold never replaces those bytes is wrong.
965
+ for `.claude/settings.json`, `scripts/codex.sh`, `tests/policy.test.ts`, and every other vendored
966
+ path, and a consumer concluding from that reading that scaffold never replaces those bytes is wrong.
932
967
  `Materializer` hydrates the plan before it audits or writes: hydration reads the vendored root and
933
968
  turns each path scaffold owns the bytes of into a content-owned artifact, leaving `presence` on the
934
969
  workspace-owned paths and the mirror pointers the preceding rows name. What a verb claims at a
935
970
  vendored path is the hydrated ownership, and `HostArtifact` carries the same narrowing on the type.
936
971
 
972
+ The `AGENTS.md` and `CLAUDE.md` pointers sit outside that row. Their bytes come from a frozen
973
+ template rather than from the data root, so the pure core face already claims them content-owned and
974
+ hydration leaves them alone. `repair` and `overwrite` restore a missing pointer and replace a drifted
975
+ one, in every face, which is what keeps a target's resolution instructions in agreement with the
976
+ release it installed.
977
+
937
978
  Birth ownership is what makes a generated workspace the consumer's. `materialize` writes a
938
979
  birth-owned path into a vacant target. A later `repair` or `overwrite` call treats that path as
939
980
  aligned whether it is present or absent, so it neither restores missing bytes nor replaces present
@@ -967,9 +1008,11 @@ paths, and `distribution` by a published `src` environment. A workspace that nee
967
1008
  configuration must keep those edits outside a content-owned file; `repair` restores that file to the
968
1009
  canonical project set.
969
1010
 
970
- An audit reports one `Finding` per planned path, followed by any foreign path beneath the groups
971
- the plan covers. Every planned finding carries its artifact's `ownership`. A foreign finding has
972
- no ownership because no artifact was planned for its path. `Ownership` says what scaffold claims at
1011
+ An audit reports one `Finding` per planned path, followed by every foreign path in the groups the
1012
+ plan covers. That second list draws on a file beneath a vendored directory the plan expands and on a
1013
+ file the target holds at a canon path the plan does not claim. Every planned finding carries its
1014
+ artifact's `ownership`. A foreign finding has no ownership because no artifact was planned for its
1015
+ path. `Ownership` says what scaffold claims at
973
1016
  a path, not what one run did there. Counting planned findings by `content`, `presence`, and `birth`
974
1017
  therefore says what audit is entitled to compare and stays the same against a vacant target and a
975
1018
  repaired one. What one run compared comes from `ownership`, `drift`, and `observed` together. A
@@ -1126,11 +1169,36 @@ generating a workspace with no network receives.
1126
1169
 
1127
1170
  ## Vendored data root
1128
1171
 
1129
- The vendored data root is the shared file set, staged into the published package as plain data. It
1130
- holds the root instruction documents, the licence, the orchestration contract, the harness
1131
- directories, the bench scripts, the shared policy register, the byte-identical root dotfiles, and
1132
- the guide mirrors a generated workspace starts from. `HOST_PATHS` is the candidate list; a plan
1133
- carries the subset its target selects, because a workspace never mirrors its own guide.
1172
+ The vendored data root is the shared file set, staged into the published package as plain data.
1173
+ Staging walks `HOST_PATHS` and `CANON_PATHS`, and a release ships what both name.
1174
+
1175
+ `HOST_PATHS` is the vendored set, and a target receives a copy of each path it selects: the licence,
1176
+ the harness permission file, the bench scripts, the shared policy register, the byte-identical root
1177
+ dotfiles, and the guide mirrors a generated workspace starts from. It is a candidate list rather than
1178
+ a plan, because a workspace never mirrors its own guide.
1179
+
1180
+ `CANON_PATHS` is the instruction canon, staged for reading instead: the `AGENTS.md` coding contract,
1181
+ the `CLAUDE.md` harness bridge, the `.agents/orchestration.md` agent-operation contract, the rules
1182
+ under `.claude/rules/` and `.cursor/rules/`, the skills under `.agents/skills/` and `.claude/skills/`,
1183
+ the templates under `.agents/templates/`, the transport contracts under `.agents/transports/`, the
1184
+ agent roles under `.claude/agents/` and `.codex/agents/`, the `.codex/config.toml` bench
1185
+ configuration, and the `.mcp.json` and `.cursor/mcp.json` server registrations. A release stages
1186
+ every one of them, and a target receives a copy only where the plan claims the path. At the
1187
+ `AGENTS.md` and `CLAUDE.md` destinations it receives the pointers: different content at the same
1188
+ paths, planned as this package's own template content. At `CATALOG_AGENT_PATH` it receives the staged
1189
+ bytes themselves, because the `catalog` verb refuses a target that lacks the file. Everywhere else in
1190
+ the canon a target holds nothing, and a reader reaches the contracts from a scaffold checkout sitting
1191
+ beside the repository, or from the `node_modules/@orkestrel/scaffold/dist/host/` root inside the
1192
+ installed package, which is what the `AGENTS.md` pointer scaffold plans into a target names.
1193
+
1194
+ `HOST_PATHS` and `CANON_PATHS` are disjoint by prefix in either direction: no member of one equals or
1195
+ sits beneath a member of the other. Staging depends on that, because the walk covers the union and a
1196
+ path it discovers twice claims one storage name twice, which refuses the stage. `isCanonPath` is the
1197
+ one reading of canon membership, matching a member and anything beneath a member that is a directory,
1198
+ so staging, the live overlay, and the executable's fetch list never disagree about what a path is.
1199
+ Membership says where a path's bytes are staged, not whether a plan claims it: `nameToHostArtifacts`
1200
+ appends `CATALOG_AGENT_PATH` to what `HOST_PATHS` selects rather than listing it there, which is what
1201
+ keeps the file planned without putting a canon path in the vendored list.
1134
1202
 
1135
1203
  The `host.json` file at the repository root is the committed live inventory. Each entry carries the
1136
1204
  SHA-256 digest of its file content, and the inventory carries a membership digest over its declared
@@ -1143,12 +1211,29 @@ those paths; it can neither introduce a path nor delete one. A path added upstre
1143
1211
  until a release adds it to the installed manifest. Remove a vendored path in the same change that
1144
1212
  ships the release which removes it from that manifest.
1145
1213
 
1214
+ Moving a path from `HOST_PATHS` to `CANON_PATHS` is not that removal. The path stays staged, stays in
1215
+ the installed manifest, and keeps being published; what changes is that no host artifact claims it. A
1216
+ target generated before the move still holds the copy it received, and that copy sits at a path the
1217
+ plan does not own, so `audit` reports it `foreign` and exits `1`. `overwrite` deletes it in the run
1218
+ that repairs the pointers — one candidate list and one transaction, whether the file is a stray
1219
+ beneath a vendored directory or a superseded copy inside the canon. Membership decides that, never
1220
+ byte identity: a copy a release behind no longer matches the bytes the canon stages, and matching
1221
+ bytes is exactly how such a copy would be spared.
1222
+
1146
1223
  At the default `UpstreamOptions.retries` value, an aligned target spends one request on `host.json`,
1147
1224
  and each installed path whose live digest differs from the target adds one request for its bytes. A
1148
1225
  positive `retries` value can repeat a request after a transport fault. Raw-host propagation lag after
1149
1226
  a commit is a property of the content host. Scaffold neither creates that lag nor presents a stale
1150
1227
  response as fresher than the host served it.
1151
1228
 
1229
+ A canon destination costs no request. The fetch list drops every canon destination and `filesToHost`
1230
+ keeps the installed floor bytes for each one, claimed or not. The rule covers the destinations a plan
1231
+ does claim as well: the `AGENTS.md` and `CLAUDE.md` pointers are written from this package's own
1232
+ templates, and the catalog agent file is claimed by presence, so no byte a target holds is taken from
1233
+ a fetched canon path. A fill carrying no row for a canon path is complete rather than spoiled, which
1234
+ is what lets one `Host` carry live bytes beside floor bytes without mixing baselines within a
1235
+ surface.
1236
+
1152
1237
  `.claude/settings.json` is in that set, and the artifact planned for it is content-owned. `repair`
1153
1238
  and `overwrite` restore its bytes, so an edit made to it inside a target is reverted at the next
1154
1239
  visit and reported as drift until then. Put an operator grant in `.claude/settings.local.json`
@@ -1164,7 +1249,7 @@ import { stageHost } from '@orkestrel/scaffold/server'
1164
1249
  stageHost(process.cwd(), 'dist/host') // one ManifestEntry per file staged
1165
1250
  ```
1166
1251
 
1167
- Each vendored path is copied to a storage name, and every dot that opens a segment comes off,
1252
+ Each staged path is copied to a storage name, and every dot that opens a segment comes off,
1168
1253
  because npm's own ignore rules would drop a leading-dot entry from the tarball. A dotted file at the
1169
1254
  root moves under `dotfiles/` so it cannot collide with an undotted sibling. `manifest.json` is
1170
1255
  written last and declares the whole membership: one entry per file with a digest computed from the
@@ -1172,7 +1257,15 @@ staged destination after its copy, the sorted directory inventory, and a SHA-256
1172
1257
  The membership digest detects an edit that did not update the manifest, and the directory inventory
1173
1258
  makes a declared empty directory survive a file walk.
1174
1259
 
1175
- A missing vendored path is refused rather than staged around, and the refusal names every missing
1260
+ The pointer reads that spelling back. The installed branch of a target's `AGENTS.md` names the
1261
+ `node_modules/@orkestrel/scaffold/dist/host/AGENTS.md` file, the
1262
+ `node_modules/@orkestrel/scaffold/dist/host/agents/orchestration.md` file, and the
1263
+ `node_modules/@orkestrel/scaffold/dist/host/claude/rules/` directory, which are where the dot-stripped
1264
+ storage names of `AGENTS.md`, `.agents/orchestration.md`, and `.claude/rules/` land. A reader
1265
+ following a canon path through the installed package types the storage spelling, not the repository
1266
+ one.
1267
+
1268
+ A missing staged path is refused rather than staged around, and the refusal names every missing
1176
1269
  path at once. That is why `guides/scaffold.md` — this file — must exist before `npm run build`
1177
1270
  completes.
1178
1271
 
@@ -1234,6 +1327,12 @@ except the manifest.
1234
1327
  `configs/browsers.ts` resolution, serves the bundle over a loopback server, and drives it in
1235
1328
  Playwright Chromium.
1236
1329
  - One template artifact each for `README.md` and `guides/README.md`.
1330
+ - One template artifact each for `AGENTS.md` and `CLAUDE.md`. They are pointers rather than contracts:
1331
+ `AGENTS.md` names the coding contract, the orchestration contract, the rules, and the skills, and
1332
+ resolves each against a sibling scaffold checkout or the installed package; `CLAUDE.md` names the
1333
+ `AGENTS.md` file beside it and imports nothing, because an `@path` import inlines the imported file
1334
+ into every context that loads it. Scaffold owns their bytes, so a release that moves the wording
1335
+ moves every target's copy at its next `repair`.
1237
1336
  - One host artifact per vendored path the workspace selects. A vendored directory is one planned
1238
1337
  path that expands into the files the data root stores beneath it.
1239
1338
 
@@ -1413,6 +1512,26 @@ drivers is separate test capability rather than name-resolution parity.
1413
1512
  question, and `materialize` writes any plan into any vacant target. The Compile section states the
1414
1513
  rule a library caller applies in its place.
1415
1514
 
1515
+ **A file a target keeps at a canon path never reports clean.** `overwrite` deletes a superseded
1516
+ instruction copy in the run that repairs the pointers, and it deletes only what git tracks, from a
1517
+ tree carrying no uncommitted work. An untracked copy is left standing, and a git-ignored one sits
1518
+ outside the dirty reading as well, so a target can carry its own file at a canon path through every
1519
+ visit. The audit reads canon membership by path, so such a copy stays a `foreign` finding and that
1520
+ target exits `1` on every run. `repair` never closes it either: that verb writes the planned paths a
1521
+ target is missing or has let drift and deletes nothing, so it restores the `AGENTS.md` and
1522
+ `CLAUDE.md` pointers and leaves every other copy where it is. A maintainer who wants a local MCP
1523
+ server registration keeps it outside the repository, in the harness's own local or user scope, rather
1524
+ than at `.mcp.json`, where the file is drift whoever wrote it.
1525
+
1526
+ **A target holds no dispatchable role beyond the catalog agent.** The canon is staged for reading, so
1527
+ a target receives the `AGENTS.md` and `CLAUDE.md` pointers and `.claude/agents/orkestrel.md`, and
1528
+ nothing else a harness reads: no other agent role, no bench configuration, and no MCP registration. A
1529
+ harness running in a target loads none of those from `node_modules` either, so a role, a bench, or a
1530
+ server that target needs is defined in the harness's own local or user scope — the seam the preceding
1531
+ registration entry already names. Fleet targets are not orchestration hosts. A session that
1532
+ dispatches roles starts on scaffold, where `.agents/orchestration.md` and the role files sit, and
1533
+ attaches the target it is working on.
1534
+
1416
1535
  **`isPath` does not prove host portability.** It proves bounded target-relative syntax and rejects
1417
1536
  traversal, separators, controls, and reserved syntax characters. It deliberately admits host-specific
1418
1537
  segment spellings such as a Windows device name, a trailing dot or space, and a segment beyond a
@@ -1648,4 +1767,4 @@ port, so the run drives nothing external and stays in `test`.
1648
1767
 
1649
1768
  - [`guides/README.md`](README.md) — the concept and directory index.
1650
1769
  - [`README.md`](../README.md) — the package front page.
1651
- - [`AGENTS.md`](../AGENTS.md) — the coding contract every generated workspace inherits.
1770
+ - [`AGENTS.md`](../AGENTS.md) — the coding contract every generated workspace points at.
@@ -22,7 +22,7 @@
22
22
  "storage": "agents/orchestration.md",
23
23
  "destination": ".agents/orchestration.md",
24
24
  "executable": false,
25
- "digest": "db4414f1dc8eb2cc9e96cb920f08946d4be2507b081866839fea4112949b7505"
25
+ "digest": "601036308ef2144a5e6e0aeb9af58127c43b25a007d194a1cfbc501f9fade1d7"
26
26
  },
27
27
  {
28
28
  "storage": "agents/skills/enterprise-bootstrap/SKILL.md",
@@ -244,7 +244,7 @@
244
244
  "storage": "agents/skills/orkestrel-publish/references/wave.md",
245
245
  "destination": ".agents/skills/orkestrel-publish/references/wave.md",
246
246
  "executable": false,
247
- "digest": "62620bd6d1d985ed14c780e161b111186e61947a245f9c5e197de3ad753e472a"
247
+ "digest": "9f8dbb293bccffbc87751d70b828a8a7e6e6f09fb60ae9fc9f66aeb9f135e906"
248
248
  },
249
249
  {
250
250
  "storage": "agents/skills/orkestrel-publish/references/window.md",
@@ -310,7 +310,7 @@
310
310
  "storage": "claude/agents/orkestrel.md",
311
311
  "destination": ".claude/agents/orkestrel.md",
312
312
  "executable": false,
313
- "digest": "eacfb94cd758ed202aba25ac759420223bae1e624a7baf208feaeb9c979e3c9f"
313
+ "digest": "c48480a671f6288d9acf70366804f2386540bc16d711cf8cc29b67a3947bcfe5"
314
314
  },
315
315
  {
316
316
  "storage": "claude/agents/planner.md",
@@ -394,7 +394,7 @@
394
394
  "storage": "claude/rules/quality.md",
395
395
  "destination": ".claude/rules/quality.md",
396
396
  "executable": false,
397
- "digest": "10adc15a1500a78c4713dc87a22860294ecffd50581e2301f031f1b19effdc88"
397
+ "digest": "bed49d37c2f97cb1b1640ee36e45a1c2a79a95fd7263513369ee53b999aa0d85"
398
398
  },
399
399
  {
400
400
  "storage": "claude/rules/styles.md",
@@ -652,7 +652,7 @@
652
652
  "storage": "guides/scaffold.md",
653
653
  "destination": "guides/scaffold.md",
654
654
  "executable": false,
655
- "digest": "4b6e503d74188118ebba5ff4c9d76d9c9a933c0aec055ae600e02540e73c8c57"
655
+ "digest": "168273a80a9bd44046d1afe472c238858b92deae528e46d73b8ff4b4b4fe73bd"
656
656
  },
657
657
  {
658
658
  "storage": "scripts/codex.sh",
@@ -688,7 +688,7 @@
688
688
  "storage": "tests/policy.test.ts",
689
689
  "destination": "tests/policy.test.ts",
690
690
  "executable": false,
691
- "digest": "6a2be29c887e90b976273f8ef9da9ac1c17ed8c1880c133eb8a67514ddf5b5c3"
691
+ "digest": "f1f9ad779a090e9921695c743068025c387ccd610a09fee411eda809475a7367"
692
692
  },
693
693
  {
694
694
  "storage": "tests/setupPolicy.ts",
@@ -742,5 +742,5 @@
742
742
  ".codex/agents",
743
743
  ".cursor/rules"
744
744
  ],
745
- "digest": "900545f6b4a0078f1ab0256867453576ade0fca62344a3fd258d249cc6853823"
745
+ "digest": "260052484e5203b91bc8542fc49870dde886f789dc4f3f2a4e48f50f4f16afb3"
746
746
  }
File without changes
File without changes
File without changes
File without changes