@orkestrel/scaffold 0.0.48 → 0.0.50

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.
@@ -12,11 +12,11 @@ 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
- Every code fence below is illustrative. [`tests/guides.test.ts`](../tests/guides.test.ts)
16
- transcribes the pure blueprint-default, compile-refusal, and error-narrowing fences and checks their
17
- literal verdicts. A trailing comment in another fence is this guide's claim rather than a measured
18
- answer; the driven examples are the ones the shipped declarations print. Limits states what that
19
- leaves unproven and what covers it instead.
15
+ Every code fence below is illustrative. [`tests/guides.test.ts`](../tests/guides.test.ts) keeps the
16
+ command reference aligned with the executable and transcribes the pure blueprint-default,
17
+ compile-refusal, and error-narrowing fences. A trailing comment in another fence is this guide's
18
+ claim rather than a measured answer; the driven examples are the ones the shipped declarations
19
+ print. Limits states what that leaves unproven and what covers it instead.
20
20
 
21
21
  ```sh
22
22
  npm install --save-dev @orkestrel/scaffold
@@ -44,11 +44,12 @@ Exported from `@orkestrel/scaffold`, and reachable from
44
44
  | `CatalogEntry` | type | One package row of the fleet catalog. |
45
45
  | `CompileStage` | type | The compile phases, in the order they run. |
46
46
  | `CompilerEventMap` | type | The compiler's observation channel. |
47
+ | `HostFile` | type | One vendored file read from the repository, beside the target bytes it answers for. |
47
48
  | `Drift` | type | How one target path compares to the artifact planned for it. |
48
49
  | `Environment` | type | One environment a generated workspace selects on its `src` or `app` axis. |
49
50
  | `Finding` | type | One drift verdict against a target path. |
50
51
  | `Group` | type | The artifact group a plan selects over. |
51
- | `Lookup` | type | Whether an upstream lookup produced an answer. |
52
+ | `Lookup` | type | How an upstream lookup resolved: found, missing, unmatched, or failed. |
52
53
  | `Mirror` | type | One dependency guide fetched from upstream, beside the local mirror it answers for. |
53
54
  | `Origin` | type | How an artifact's content is produced. |
54
55
  | `Ownership` | type | What scaffold claims at an artifact's path. |
@@ -58,27 +59,31 @@ Exported from `@orkestrel/scaffold`, and reachable from
58
59
 
59
60
  #### Interfaces
60
61
 
61
- | Name | Kind | Summary |
62
- | ------------------- | --------- | --------------------------------------------------------------------------------------- |
63
- | `AppDefinition` | interface | The configuration and runtime-entry settings one private `app` environment contributes. |
64
- | `ArtifactBase` | interface | The fields every planned file carries. |
65
- | `Audit` | interface | The whole comparison of a plan against a target's current content. |
66
- | `Blueprint` | interface | The closed, JSON-serializable workspace specification. |
67
- | `CompileFailure` | interface | The coded reason one compile stage failed. |
68
- | `CompileRecord` | interface | The input and output snapshot of one compile stage. |
69
- | `CompilerInterface` | interface | The compilation contract: pure, synchronous, and host-independent. |
70
- | `CompilerOptions` | interface | Options for the compiler. |
71
- | `ContentArtifact` | interface | A text file produced by the template or computed compilation path. |
72
- | `Dependency` | interface | One runtime `@orkestrel/*` dependency of a generated workspace. |
73
- | `HostArtifact` | interface | A file byte-copied from the vendored data root, planned before its bytes are read. |
74
- | `HydratedArtifact` | interface | A vendored file whose exact bytes have been read, so its content can be compared. |
75
- | `Override` | interface | One artifact override. |
76
- | `Plan` | interface | The compiled, ordered artifact list and the selection it covers. |
77
- | `PlanSummary` | interface | The tally of one plan by artifact origin. |
78
- | `Question` | interface | One validation issue raised against a blueprint or a plan. |
79
- | `Scaffolding` | interface | The replayable outcome of one compile. |
80
- | `SrcDefinition` | interface | The build and export settings one published `src` environment contributes. |
81
- | `ViteMachinery` | interface | Which host-specific pipelines a generated root Vite configuration carries. |
62
+ | Name | Kind | Summary |
63
+ | ----------------------- | --------- | --------------------------------------------------------------------------------------- |
64
+ | `AppDefinition` | interface | The configuration and runtime-entry settings one private `app` environment contributes. |
65
+ | `ArtifactBase` | interface | The fields every planned file carries. |
66
+ | `Audit` | interface | The whole comparison of a plan against a target's current content. |
67
+ | `Blueprint` | interface | The closed, JSON-serializable workspace specification. |
68
+ | `CompileFailure` | interface | The coded reason one compile stage failed. |
69
+ | `CompileRecord` | interface | The input and output snapshot of one compile stage. |
70
+ | `CompilerInterface` | interface | The compilation contract: pure, synchronous, and host-independent. |
71
+ | `CompilerOptions` | interface | Options for the compiler. |
72
+ | `ContentArtifact` | interface | A text file produced by the template or computed compilation path. |
73
+ | `Dependency` | interface | One runtime `@orkestrel/*` dependency of a generated workspace. |
74
+ | `DependencyPinSet` | interface | The runtime and development dependency sections a range writer may change. |
75
+ | `HostArtifact` | interface | A file byte-copied from the vendored data root, planned before its bytes are read. |
76
+ | `HydratedArtifact` | interface | A vendored file whose exact bytes have been read, so its content can be compared. |
77
+ | `ManifestDependencySet` | interface | The runtime, development, and peer declarations read from an existing manifest. |
78
+ | `ManifestRegionSet` | interface | The manifest regions a writing operation may change. |
79
+ | `ManifestScript` | interface | One manifest script a region-writing operation may replace. |
80
+ | `Override` | interface | One artifact override. |
81
+ | `Plan` | interface | The compiled, ordered artifact list and the selection it covers. |
82
+ | `PlanSummary` | interface | The tally of one plan by artifact origin. |
83
+ | `Question` | interface | One validation issue raised against a blueprint or a plan. |
84
+ | `Scaffolding` | interface | The replayable outcome of one compile. |
85
+ | `SrcDefinition` | interface | The build and export settings one published `src` environment contributes. |
86
+ | `ViteMachinery` | interface | Which host-specific pipelines a generated root Vite configuration carries. |
82
87
 
83
88
  #### Constants
84
89
 
@@ -100,7 +105,7 @@ Exported from `@orkestrel/scaffold`, and reachable from
100
105
  | `DEFAULT_ENGINES` | const | The `engines.node` range a workspace starts with. |
101
106
  | `DEFAULT_VERSION` | const | The version a workspace starts at. |
102
107
  | `DEPENDENCY_NAME_PATTERN` | const | The runtime dependency name syntax: the `@orkestrel` scope and a bare name. |
103
- | `DISTRIBUTION_TEST_PATH` | const | The packed-package proof whose presence makes a workspace `distribution`. |
108
+ | `DISTRIBUTION_TEST_PATH` | const | The generated packed-package proof every publishing workspace is planned at. |
104
109
  | `ENGINES_PATTERN` | const | The minimum-Node engine syntax a blueprint declares. |
105
110
  | `ENVIRONMENTS` | const | The `Environment` values, frozen. |
106
111
  | `EXECUTABLE_PATHS` | const | The vendored paths a target receives with its executable bit set, frozen. |
@@ -112,8 +117,10 @@ Exported from `@orkestrel/scaffold`, and reachable from
112
117
  | `GUIDES_TEST_PATH` | const | The guide-parity proof whose presence selects the planned `guides` project. |
113
118
  | `HEX_PATTERN` | const | Exact lowercase hexadecimal bytes: two digits per byte, and empty content is valid. |
114
119
  | `HOST_PATHS` | const | The paths byte-copied from the vendored data root, frozen. |
120
+ | `HOST_INVENTORY_PATH` | const | The repository-relative path where the committed vendored-file inventory is served. |
115
121
  | `INTEGRATION_TEST_PATH` | const | The cross-environment composition proof whose presence makes a workspace `integration`. |
116
122
  | `INVALID_PATH_CHARACTER_PATTERN` | const | Visible characters a target-relative path and a Markdown path cell both forbid. |
123
+ | `MANIFEST_PATH` | const | The manifest path every compiler plan emits with birth ownership. |
117
124
  | `MAX_ARTIFACT_BYTES` | const | Maximum bytes accepted for one artifact. |
118
125
  | `MAX_ARTIFACT_HEX_LENGTH` | const | Maximum length of the hexadecimal string carrying one artifact's bytes. |
119
126
  | `MAX_AUDIT_FINDINGS` | const | Maximum findings one audit can produce from a bounded plan and snapshot. |
@@ -124,6 +131,7 @@ Exported from `@orkestrel/scaffold`, and reachable from
124
131
  | `MAX_PATH_LENGTH` | const | Maximum length of one path, matching the longest a supported filesystem accepts. |
125
132
  | `MAX_RANGE_LENGTH` | const | Maximum length of one declared package range. |
126
133
  | `MAX_REGISTRY_BYTES` | const | Maximum decoded bytes accepted from one registry response. |
134
+ | `MAX_SCRIPT_LENGTH` | const | Maximum length of one manifest script name or command. |
127
135
  | `MAX_TOTAL_ARTIFACT_BYTES` | const | Maximum bytes retained across one whole plan or audit. |
128
136
  | `MAX_TOTAL_REGISTRY_BYTES` | const | Maximum decoded bytes accepted across one registry-reading call. |
129
137
  | `MINIMUM_NODE_VERSION` | const | The oldest Node version the generated toolchain supports. |
@@ -132,6 +140,7 @@ Exported from `@orkestrel/scaffold`, and reachable from
132
140
  | `ORCHESTRATION_PATH_PREFIXES` | const | The path prefixes whose contents instruct or wire an agent, frozen. |
133
141
  | `ORKESTREL_RANGE_PATTERN` | const | The exact caret-pinned pre-1.0 range accepted for an `@orkestrel/*` runtime dependency. |
134
142
  | `PRINT_WIDTH` | const | Columns one emitted line may occupy, matching `printWidth` in `.oxfmtrc.json`. |
143
+ | `RELEASE_PROOF_COMMAND` | const | The `prepublishOnly` row that runs the packed-package proof against a real registry. |
135
144
  | `SERVICE_SCRIPT_PATH` | const | The provisioner skeleton a workspace with declared service vendors is given once. |
136
145
  | `SERVICE_SETUP_PATH` | const | The live-service readiness module whose presence makes a workspace `service`. |
137
146
  | `SERVICE_TEST_INCLUDE` | const | The include the live-service project covers, which is a directory rather than one proof. |
@@ -162,6 +171,7 @@ Exported from `@orkestrel/scaffold`, and reachable from
162
171
  | `isGroup` | const | Narrow a value to one `Group` a plan selects over. |
163
172
  | `isGroups` | const | Narrow a value to a bounded group selection. |
164
173
  | `isHex` | const | Narrow a value to exact lowercase hexadecimal bytes within one artifact's limit. |
174
+ | `isManifestScript` | const | Narrow a value to a `ManifestScript`. |
165
175
  | `isMirror` | const | Narrow a value to a `Mirror`. |
166
176
  | `isOverride` | const | Narrow a value to an `Override`. |
167
177
  | `isPath` | function | Narrow a value to a logical target-relative path. |
@@ -195,7 +205,8 @@ Exported from `@orkestrel/scaffold`, and reachable from
195
205
  | `extractVersion` | function | Extract the major, minor, and patch components of an exact version. |
196
206
  | `inferDrift` | function | Infer how one target path compares to the artifact planned for it. |
197
207
  | `inferGroup` | function | Infer the `Group` a path belongs to. |
198
- | `manifestToDependencies` | function | Project a package manifest's text to the `@orkestrel/*` packages it declares. |
208
+ | `isDeferredPath` | function | Test whether another surface owns the vendored bytes at a path. |
209
+ | `manifestToDependencies` | function | Project a manifest's `@orkestrel/*` declarations into separate section lists. |
199
210
  | `manifestToName` | function | Project a package manifest's text to its own name. |
200
211
  | `matchesDriftReachability` | function | Test whether `inferDrift` could have produced a finding for an ownership. |
201
212
  | `matchesEngines` | function | Test whether a declared engines floor is at or above the supported minimum. |
@@ -229,14 +240,16 @@ Exported from `@orkestrel/scaffold`, and reachable from
229
240
  | `blueprintToScripts` | function | Project a blueprint into the scripts its manifest declares. |
230
241
  | `blueprintToSourceArtifacts` | function | Compile every artifact in the `source` group. |
231
242
  | `blueprintToTestArtifacts` | function | Compile every artifact in the `tests` group that is not vendored from the host. |
243
+ | `blueprintToWritableScripts` | function | Project a blueprint into the manifest scripts a region write may replace. |
232
244
  | `dependenciesToQuestions` | function | Measure one declared package list against the name and range syntax it accepts. |
233
245
  | `nameToHostArtifacts` | function | Compile the vendored host artifacts a named workspace plans. |
234
246
  | `overridesToQuestions` | function | Measure a blueprint's overrides against the artifacts drafted for it. |
235
247
  | `pathToCondition` | function | Build one `exports` condition block for a built environment. |
236
248
  | `planToFindings` | function | Compare a plan against a target's current content. |
237
249
  | `planToHash` | function | Compute a plan's content identity. |
238
- | `replaceManifestRanges` | function | Replace declared dependency ranges in package manifest text. |
239
- | `replacePlanRanges` | function | Replace dependency ranges in a plan's manifest and recompute its identity. |
250
+ | `replaceManifestRanges` | function | Replace runtime and development ranges without reading or writing peer fields. |
251
+ | `replaceManifestScripts` | function | Replace named script values, refusing a value the region does not accept. |
252
+ | `replacePlanRanges` | function | Replace writable ranges in a plan's manifest and recompute its identity. |
240
253
  | `srcToEntry` | function | Project a published selection into the manifest's entry fields. |
241
254
  | `srcToExports` | function | Project a published selection into the manifest's `exports` map. |
242
255
  | `srcToRoot` | function | Select the single published environment a package root points at. |
@@ -270,12 +283,13 @@ Exported from `@orkestrel/scaffold/server`, and reachable from
270
283
 
271
284
  | Name | Kind | Summary |
272
285
  | ----------------------- | --------- | ------------------------------------------------------------------------------------ |
286
+ | `Host` | interface | A whole vendored host supplied as a value rather than read from a directory. |
273
287
  | `HostManifest` | interface | The complete vendored-host inventory. |
274
- | `ManifestEntry` | interface | One file record of the vendored host's manifest. |
288
+ | `ManifestEntry` | interface | One file record of the vendored host's manifest, including its exact-byte digest. |
275
289
  | `MaterializeResult` | interface | The outcome of one mutation of a target. |
276
290
  | `MaterializerInterface` | interface | The mutation contract: the package's only filesystem writer. |
277
291
  | `MaterializerOptions` | interface | Options for the materializer. |
278
- | `Repository` | interface | What git reports about a target's working tree. |
292
+ | `Worktree` | interface | What git reports about a target's working tree. |
279
293
  | `UpstreamInterface` | interface | The upstream contract: the package's only network reader, and it never writes. |
280
294
  | `UpstreamOptions` | interface | Options for the upstream reader. |
281
295
  | `WriteAnchor` | interface | One physical directory identity captured across a write transaction. |
@@ -287,12 +301,12 @@ Exported from `@orkestrel/scaffold/server`, and reachable from
287
301
 
288
302
  | Name | Kind | Summary |
289
303
  | ----------------------------------- | ----- | ---------------------------------------------------------------------------------------- |
290
- | `BRANCH_PATTERN` | const | The Git branch syntax the guide endpoint accepts. |
304
+ | `BRANCH_PATTERN` | const | The Git branch syntax the repository endpoint accepts. |
291
305
  | `DIGEST_PATTERN` | const | The exact SHA-256 syntax a digest is stated in: sixty-four lowercase hexadecimal digits. |
292
306
  | `DRIVE_PATTERN` | const | The drive prefix a Windows host path may open with. |
293
307
  | `INVALID_SEGMENT_CHARACTER_PATTERN` | const | Visible characters no host path segment may carry. |
294
308
  | `MANIFEST_NAME` | const | The reserved metadata name a staged vendored host writes at its own root. |
295
- | `MAX_BRANCH_LENGTH` | const | Maximum characters one guide branch may carry. |
309
+ | `MAX_BRANCH_LENGTH` | const | Maximum characters one repository branch may carry. |
296
310
  | `MAX_ENDPOINT_LENGTH` | const | Maximum characters one caller-supplied upstream endpoint may carry. |
297
311
  | `MAX_INVENTORY_PATHS` | const | Maximum paths one target's working-tree inventory may report. |
298
312
  | `MAX_PATH_DEPTH` | const | Maximum segments one host path may carry. |
@@ -306,20 +320,23 @@ Exported from `@orkestrel/scaffold/server`, and reachable from
306
320
 
307
321
  | Name | Kind | Summary |
308
322
  | ----------------------- | -------- | ---------------------------------------------------------------------------------- |
309
- | `isBranch` | const | Narrow a value to a Git branch the guide endpoint accepts. |
323
+ | `isBranch` | const | Narrow a value to a Git branch the repository endpoint accepts. |
310
324
  | `isCatalogEntries` | const | Narrow a value to a bounded list of fleet catalog rows. |
311
325
  | `isDependencies` | const | Narrow a value to a bounded list of declared runtime dependencies. |
312
326
  | `isDependencyNames` | const | Narrow a value to a bounded list of `@orkestrel` package names. |
313
327
  | `isDigest` | const | Narrow a value to one exact SHA-256 digest. |
314
328
  | `isEndpoint` | const | Narrow a value to a bounded upstream endpoint. |
315
329
  | `isFilesystemPath` | function | Narrow a value to a path naming a location on this host. |
330
+ | `isHost` | const | Narrow a value to one whole vendored host supplied as a value. |
316
331
  | `isHostManifest` | const | Narrow a value to one `HostManifest`. |
317
332
  | `isInventory` | function | Narrow a value to a working-tree inventory within the limit one target may report. |
318
333
  | `isManifestEntry` | const | Narrow a value to one `ManifestEntry`. |
334
+ | `isManifestRegionSet` | const | Narrow a value to one `ManifestRegionSet`. |
319
335
  | `isMaterializerHooks` | const | Narrow a value to the materializer's initial listener record. |
320
336
  | `isMaterializerOptions` | const | Narrow a value to `MaterializerOptions`. |
321
337
  | `isMirrors` | const | Narrow a value to a bounded list of fetched guide mirrors. |
322
- | `isRepository` | const | Narrow a value to a `Repository`. |
338
+ | `isPaths` | const | Narrow a value to a bounded list of target-relative paths. |
339
+ | `isWorktree` | const | Narrow a value to a `Worktree`. |
323
340
  | `isTimeout` | const | Narrow a value to a per-request timeout in milliseconds. |
324
341
  | `isUpstreamHooks` | const | Narrow a value to the upstream reader's initial listener record. |
325
342
  | `isUpstreamOptions` | const | Narrow a value to `UpstreamOptions`. |
@@ -331,6 +348,8 @@ Exported from `@orkestrel/scaffold/server`, and reachable from
331
348
  | `computeDigest` | function | Compute the SHA-256 digest of text. |
332
349
  | `computeFileDigest` | function | Compute the SHA-256 digest of one file's exact bytes. |
333
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. |
334
353
  | `isExactCaseFile` | function | Test whether a physical file's path matches every on-disk segment exactly. |
335
354
  | `isPhysicalDirectory` | function | Test whether a path is a physical directory this package will read or write into. |
336
355
  | `isPhysicalFile` | function | Test whether a path is a physical file this package will read or replace. |
@@ -350,12 +369,15 @@ Exported from `@orkestrel/scaffold/server`, and reachable from
350
369
  | `readExpectation` | function | Capture what one destination holds before a write. |
351
370
  | `readFileHex` | function | Read one contained file as its exact bytes in lowercase hexadecimal. |
352
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. |
353
373
  | `readHostManifest` | function | Read a vendored host's manifest, when it carries one. |
354
374
  | `readManifestEntry` | function | Derive one vendored-host manifest entry from a file in a checkout. |
355
375
  | `readSnapshot` | function | Read a target's current bytes at the paths a plan claims. |
356
376
  | `resolveContainedPath` | function | Resolve a root-relative path and refuse one that leaves its root. |
357
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. |
358
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
381
 
360
382
  #### Classes
361
383
 
@@ -389,18 +411,19 @@ no interface and is documented directly.
389
411
  | `repair` | Write a plan into an existing target, guided by an audit of it. |
390
412
  | `mirror` | Write fetched dependency guides to their local mirrors. |
391
413
  | `catalog` | Rewrite the marker-bounded package table in the target's catalog agent file. |
392
- | `declare` | Rewrite the `@orkestrel/*` range set in the target's manifest. |
414
+ | `declare` | Rewrite the manifest regions the caller names: the ranges and the scripts. |
393
415
  | `remove` | Re-derive and delete the tracked files the plan does not own. |
394
416
  | `destroy` | Tear the materializer down. Every later call throws, and teardown is idempotent. |
395
417
 
396
418
  #### `UpstreamInterface`
397
419
 
398
- | Method | Summary |
399
- | --------- | -------------------------------------------------------------------------- |
400
- | `lookup` | Look up the newest release each declared range admits. |
401
- | `fetch` | Fetch each named package's guide, beside the local mirror it answers for. |
402
- | `catalog` | Catalog the published fleet from the registry's organization package list. |
403
- | `destroy` | Tear the reader down, aborting every request in flight. |
420
+ | Method | Summary |
421
+ | --------- | ------------------------------------------------------------------------------------------ |
422
+ | `lookup` | Look up the newest release each declared range admits. |
423
+ | `fetch` | Fetch each named package's guide, beside the local mirror it answers for. |
424
+ | `read` | Read each named vendored file from the repository, beside the target bytes it answers for. |
425
+ | `catalog` | Catalog the published fleet from the registry's organization package list. |
426
+ | `destroy` | Tear the reader down, aborting every request in flight. |
404
427
 
405
428
  #### `WriteTransaction`
406
429
 
@@ -418,42 +441,70 @@ no interface and is documented directly.
418
441
  Authority is the verb's: every verb except `audit` writes when it is typed, and no
419
442
  option grants a write.
420
443
 
421
- | Verb | Writes |
422
- | ----------- | ------------------------------------------------------------------------------- |
423
- | `new` | A whole workspace, into a target that holds nothing the plan would collide with |
424
- | `audit` | Nothing |
425
- | `repair` | Each planned path the target is missing or has let drift, and the ranges |
426
- | `catalog` | The package table, the guide mirrors, and the ranges |
427
- | `overwrite` | Everything `repair` and `catalog` write, plus deletions |
444
+ | Verb | Writes |
445
+ | ----------- | ------------------------------------------------------------------------------------------ |
446
+ | `new` | A whole workspace, into a target that holds nothing the plan would collide with |
447
+ | `audit` | Nothing |
448
+ | `repair` | Each planned path the target is missing or has let drift, and the range and script regions |
449
+ | `catalog` | The package table, the guide mirrors, and the range region |
450
+ | `overwrite` | Everything `repair` and `catalog` write, plus deletions |
451
+
452
+ ### Baselines
453
+
454
+ Every remote surface reads its live source first and falls back, whole, to the copy the installed
455
+ package distributes; each operation reports one baseline word per surface. A surface can select
456
+ `floor` only where the package distributes a copy. The registry's organization membership ships
457
+ nowhere, so `catalog` refuses when that read fails.
458
+
459
+ For `new`, `repair`, `catalog`, and `overwrite`, authoritative absence never selects `floor`. A
460
+ registry `404` or a packument with no admitted version stays a `FETCH` refusal, because writing a
461
+ version the registry says is absent produces an uninstallable manifest. `audit` turns release
462
+ absence into questions and returns its audit result. Transport faults, timeouts, rate refusals,
463
+ byte-bound refusals, and integrity refusals can select the floor.
464
+
465
+ The guide surface is the per-row exception to whole-surface fallback. A failed foreign guide keeps
466
+ the target's existing mirror as its floor, while the other guide rows can still update. When at
467
+ least one selected guide keeps its mirror, `provenance.guides` is `floor` for the result; it is
468
+ `live` only when every selected guide resolved live.
469
+
470
+ A value `Host` can carry live host-owned bytes beside installed floor bytes for deferred guide and
471
+ catalog paths. Each surface still contributes one baseline. Deferred paths are presence-only, and
472
+ repair never writes their floor bytes.
473
+
474
+ Every verb's machine-readable result carries `provenance`. The record names only the remote
475
+ surfaces that the verb read. A host supplied by the `--from` option is absent because it comes from
476
+ a local path.
428
477
 
429
478
  `scaffold --help` prints the whole reference:
430
479
 
431
480
  ```text
432
481
  scaffold <verb> [options]
433
482
 
434
- scaffold new <name> [--src <list>] [--app <list>] [--bin] [--deps <list>] [--from <path>] [--target <path>] [--json]
483
+ scaffold new <name> [--src <list>] [--app <list>] [--bin] [--deps <list>] [--offline] [--from <path>] [--target <path>] [--json]
435
484
  scaffold a workspace
436
- scaffold audit [--groups <list>] [--from <path>] [--target <path>] [--json]
485
+ scaffold audit [--groups <list>] [--offline] [--from <path>] [--target <path>] [--json]
437
486
  report how the target compares to its plan, writing nothing
438
- scaffold repair [--groups <list>] [--from <path>] [--target <path>] [--json]
487
+ scaffold repair [--groups <list>] [--offline] [--from <path>] [--target <path>] [--json]
439
488
  write each planned path the target is missing or has let drift
440
489
  scaffold catalog [--all] [--from <path>] [--target <path>] [--json]
441
490
  regenerate the package table and refresh the guide mirrors
442
- scaffold overwrite [--groups <list>] [--dirty] [--from <path>] [--target <path>] [--json]
491
+ scaffold overwrite [--groups <list>] [--dirty] [--offline] [--from <path>] [--target <path>] [--json]
443
492
  do everything repair and catalog do, then delete what the plan does not own and re-declare the dependency ranges
444
493
 
445
494
  options
446
- --src <list> the published library environments to build: core, browser, server
447
- --app <list> the private application environments to build: core, browser, server
448
- --bin scaffold a command-line executable at src/bin/main.ts
449
- --deps <list> the @orkestrel/* packages the workspace depends on
450
- --groups <list> the artifact groups to cover; every group when absent
451
- --all fetch a guide for every package the organization publishes, not the declared ones alone
452
- --dirty delete from a tree carrying uncommitted changes
453
- --from <path> read the data root from a local path instead of the bundled one; catalog alone accepts it more than once
454
- --target <path> the directory the verb operates on; the working directory when absent
455
- --json emit one machine-readable value instead of a report
456
- ORKESTREL_SCAFFOLD_REGISTRY the registry base mapped to upstream.registry.base
495
+ --src <list> the published library environments to build: core, browser, server
496
+ --app <list> the private application environments to build: core, browser, server
497
+ --bin scaffold a command-line executable at src/bin/main.ts
498
+ --deps <list> the @orkestrel/* packages the workspace depends on
499
+ --groups <list> the artifact groups to cover; every group when absent
500
+ --all fetch a guide for every package the organization publishes, not the declared ones alone
501
+ --dirty delete from a tree carrying uncommitted changes
502
+ --offline use the distributed dependency and vendored-host floors without reading upstream
503
+ --from <path> read the data root from a local path instead of the bundled one; catalog alone accepts it more than once
504
+ --target <path> the directory the verb operates on; the working directory when absent
505
+ --json emit one machine-readable value instead of a report
506
+ ORKESTREL_SCAFFOLD_REGISTRY the registry base mapped to upstream.registry.base
507
+ ORKESTREL_SCAFFOLD_REPOSITORY the repository base mapped to upstream.repository.base
457
508
 
458
509
  exit codes
459
510
  0 clean
@@ -464,24 +515,30 @@ exit codes
464
515
  An option a verb does not list is refused by name rather than parsed and ignored. `--help` is the
465
516
  one exception, because it replaces the run rather than modifying it: a command line carrying
466
517
  `--help` anywhere prints the whole reference and exits `0` before the line is read as a command, so
467
- no verb has to list it. Every verb reaches the registry, and none of them invents a range when the
468
- read produces no answer. Dependency floors states what each verb reads and what it does then.
518
+ no verb has to list it. Without the `--offline` option, every verb reaches the registry, and none of
519
+ them invents a range when the read produces no answer. Dependency floors states what each verb reads
520
+ and what it does then.
469
521
 
470
- Every read addresses the published registry. Set `ORKESTREL_SCAFFOLD_REGISTRY` to address a loopback
471
- or private one instead. The process entry maps it to `upstream.registry.base` and nothing else, so
472
- the seam changes which host answers a read and grants no verb any write authority it did not already
473
- have.
522
+ At their defaults, online runs contact `registry.npmjs.org` for scoped package packuments and the
523
+ `/-/org/orkestrel/package` membership path. They contact `raw.githubusercontent.com` for guide
524
+ files on `main`, the scaffold repository's `host.json` file, and changed vendored paths.
525
+ `ORKESTREL_SCAFFOLD_REGISTRY` replaces the registry base, and
526
+ `ORKESTREL_SCAFFOLD_REPOSITORY` replaces the repository base. These settings change which host
527
+ answers a read and grant no verb write authority that it did not already have.
474
528
 
475
529
  `new --bin` creates the executable entry, its test, and its scoped Vite and TypeScript wrappers. The
476
530
  other structural facts do not need creation flags. Add a root `tests/setup*.test.ts` proof for
477
- `setup`, `tests/guides.test.ts` for `guides`,
478
- `tests/distribution.test.ts` for `distribution`, `tests/integration.test.ts` for `integration`,
531
+ `setup`, `tests/guides.test.ts` for `guides`, `tests/integration.test.ts` for `integration`,
479
532
  `tests/conformance.test.ts` for `conformance`, `tests/setupService.ts` for `service`,
480
533
  `tests/setupGlobal.ts` for `global`, and `configs/app/vite.showcase.config.ts` for `showcase`;
481
534
  reading verbs detect each exact-case file and register its fixed machinery. Add `scripts/service.sh`
482
535
  for `vendors`. Reading verbs preserve and protect that birth-owned script, but do not infer its
483
536
  vendor list from edited text.
484
537
 
538
+ `distribution` is not on that list. Publishing at least one `src` environment is its whole
539
+ condition, and scaffold writes `tests/distribution.test.ts` itself rather than waiting for you to.
540
+ Limits states what makes that one proof generable when the others are not.
541
+
485
542
  ### Reading a target
486
543
 
487
544
  `audit`, `repair`, `catalog`, and `overwrite` derive the blueprint from the target itself. The name
@@ -489,11 +546,11 @@ and the declared `@orkestrel/*` packages come from `package.json`. The environme
489
546
  from the directories the target actually ships, because a directory is the fact and a declaration
490
547
  beside it could disagree. The remaining facts come from exact-case files: `src/bin/main.ts` selects
491
548
  `bin`, each root `tests/setup*.test.ts` match selects `setup`, `tests/guides.test.ts` selects
492
- `guides`, `tests/distribution.test.ts` selects `distribution`,
493
- `tests/integration.test.ts` selects `integration`, `tests/conformance.test.ts` selects
549
+ `guides`, `tests/integration.test.ts` selects `integration`, `tests/conformance.test.ts` selects
494
550
  `conformance`, `tests/setupService.ts` selects `service`, `tests/setupGlobal.ts` selects `global`,
495
551
  and `configs/app/vite.showcase.config.ts` selects `showcase`. A containing directory does not select
496
- the fact by itself.
552
+ the fact by itself. `tests/distribution.test.ts` selects nothing: the published `src` axis the
553
+ target ships already decides the `distribution` project, and the file is planned from that.
497
554
 
498
555
  `vendors` is not reconstructed. Its only artifact, `scripts/service.sh`, is birth-owned, so edited
499
556
  script text is not a trustworthy declaration of a vendor list. A present script remains in the
@@ -525,16 +582,34 @@ question instead of licensing a write. The classifier is deliberately bounded to
525
582
  text that names `vitest`; an external wrapper whose name does not identify its runner supplies no
526
583
  static Vitest fact to infer.
527
584
 
528
- `audit` still completes the comparison and reports one non-blocking `projects` question. For a
585
+ `audit` still completes the comparison and reports one non-blocking `projects` question when its
586
+ selection includes `configs`. A scoped audit that excludes `configs` omits that question. For a
529
587
  literal absent project, its advisory tells the developer to register the project or remove the
530
588
  script. For a planned project absent from the gate chains, the advisory checks the direct
531
589
  `test:<project>` script. When the script is absent, the advisory gives the exact line to add to
532
590
  `package.json`. When the script is declared but ungated, the advisory names the script and the gate
533
- chain that must invoke it, without repeating a script line. `repair` and `overwrite` refuse either
534
- mismatch and do not write the manifest or configuration. Their absent-project refusal tells the
535
- developer to remove the script or not use scaffold writing verbs for a workspace that needs custom
536
- Vitest projects. It does not recommend editing the content-owned configuration that the refusing
537
- verb would restore. An advisory alone does not make an aligned target drift.
591
+ chain that must invoke it, without repeating a script line. When `configs` is selected, `repair` and
592
+ `overwrite` refuse either mismatch before writing. Their refusal names the `configs` group, the
593
+ manifest and planned `vite.config.ts` conflict, and the option to exclude `configs` from
594
+ `--groups`. A selection that excludes `configs` proceeds. An advisory alone does not make an aligned
595
+ target drift.
596
+
597
+ Scaffold writes one part of the manifest rather than advising on it: the script region a publishing
598
+ workspace needs for the generated distribution proof. `repair` and `overwrite` write
599
+ `test:distribution` and `prepublishOnly` there. A declared value is overwritten only when it is
600
+ already the value being written, or, for `prepublishOnly`, the one predecessor the region accepts —
601
+ the same gate chain without the release row. The overwrite happens in place, so every byte outside
602
+ the replaced ranges survives, and a description, a keyword, a key order, and a script the workspace
603
+ added are untouched. A script the manifest does not declare is appended after the last declared one,
604
+ copying that section's indentation. `catalog` writes no script region; it names the ranges alone.
605
+
606
+ A value matching neither is a chain the workspace author wrote. The region is then refused whole,
607
+ without a byte moving, and the range region is still written. The `projects` advisory reads the
608
+ manifest as a write would leave it, so a region scaffold writes for itself raises no question, while
609
+ a refused region leaves the target's own text standing and the advisory reports whichever half is
610
+ outstanding: the exact `test:distribution` line to paste, or the gate chain that must invoke a
611
+ script the manifest already declares. The refusal covers the region rather than one script, so a
612
+ manifest never holds one written value beside one refused one.
538
613
 
539
614
  The same plan-reading verbs compare the tooling set the derived blueprint plans against
540
615
  `dependencies` and `devDependencies` together. A missing planned package produces one non-blocking
@@ -542,9 +617,42 @@ The same plan-reading verbs compare the tooling set the derived blueprint plans
542
617
  order. The comparison measures membership: a workspace-owned extra is outside it, a planned tool may
543
618
  live in either section, and how current a declared range is belongs to the registry evidence
544
619
  Dependency floors describes rather than to this question. A present section that is not an object
545
- produces a question instead of a crash. `audit` reports the question without changing its exit
546
- semantics. `repair` and `overwrite` refuse before writing configuration, and no verb edits the
547
- birth-owned `package.json`.
620
+ produces a question instead of a crash. This question belongs to `configs` and `tests`. `audit`
621
+ reports it only when its selection includes either group, without changing its exit semantics.
622
+ `repair` and `overwrite` refuse before writing a selected `configs` or `tests` group. A selection
623
+ that excludes those groups proceeds, and no verb adds the declaration for you: `package.json` is
624
+ birth-owned, and the range and script regions are the only parts of it a verb rewrites.
625
+
626
+ `audit` reports one further non-blocking question, on the `setup` field, and it alone reports it.
627
+ The question fires when the target carries a filled root `tests/setup*.ts` module that is neither a
628
+ proof itself nor one of the vendored modules every target receives, while no proof of the same stem
629
+ covers it. A module counts as filled when its text differs from the seed this blueprint plans at
630
+ that same path.
631
+
632
+ The comparison reads the module and the seed trimmed, so surrounding whitespace decides nothing: a
633
+ trailing newline is not authorship, and a module holding whitespace alone reads as empty rather than
634
+ as filled. It is seed-relative rather than a test for emptiness, because the seeds differ by path:
635
+ `tests/setup.ts` is seeded with the empty string and `tests/setupGlobal.ts` is seeded with a `setup`
636
+ function body. A test for emptiness therefore raises the question against a freshly materialized
637
+ workspace. Holding each module to the seed the same blueprint plans at its own path reports what a
638
+ maintainer wrote rather than what scaffold seeded.
639
+
640
+ That reading carries a release-skew limit. A seeded setup module is birth-owned, so `repair` reports
641
+ it aligned and never rewrites it. A target keeps the seed of the release that materialized it. When
642
+ a release moves a planned seed, scaffold raises the question on every target materialized before it,
643
+ against a module scaffold wrote and no maintainer touched.
644
+ `tests/setupGlobal.ts` is the module that can meet it, because it is the one seeded with more than
645
+ the empty string. A maintainer meeting that question closes it by writing the proof it asks for, or
646
+ by taking the seed the installed release plans.
647
+
648
+ Coverage is read per module: `tests/<name>.ts` is covered by `tests/<name>.test.ts` and by nothing
649
+ else, which is the pairing the vendored policy proof resolves. Writing one proof retires that module
650
+ and leaves every other uncovered module named, and the message pairs each module it names with the
651
+ proof that module wants. The question belongs to the `tests` group, so a scoped audit that excludes
652
+ `tests` omits it. Scaffold does not write the proof it asks for, and no writing verb raises the
653
+ question: a writing verb refuses the advisories it reports, and refusing `repair` over this one
654
+ would block every write on a gap no write can close. Run across a fleet, the question is the list of
655
+ packages carrying a filled setup module that no proof covers.
548
656
 
549
657
  ### Exit codes
550
658
 
@@ -565,13 +673,13 @@ unless `--dirty` waives that refusal. A target that is not a git repository is r
565
673
  `--json` replaces the report with one JSON value on standard output. Warnings and refusals go to
566
674
  standard error, so a piped value is never polluted.
567
675
 
568
- | Verb | Value |
569
- | ----------- | ------------------------------------------------------------------------------------------ |
570
- | `new` | `MaterializeResult` — `target`, `written`, `skipped`, `removed` |
571
- | `audit` | `Audit` — `findings` and `questions` — plus `releases`; findings carry `ownership` |
572
- | `repair` | `MaterializeResult` plus `audit`, the terminal audit taken after the write, and `releases` |
573
- | `catalog` | `MaterializeResult` plus `entries`, `mirrors`, `dropped`, and `releases` |
574
- | `overwrite` | The `catalog` value plus `audit` and `note` on a partial run |
676
+ | Verb | Value |
677
+ | ----------- | -------------------------------------------------------------------------------------------------------- |
678
+ | `new` | `MaterializeResult` — `target`, `written`, `skipped`, `removed` — plus `provenance` |
679
+ | `audit` | `Audit` — `findings` and `questions` — plus `releases` and `provenance`; findings carry `ownership` |
680
+ | `repair` | `MaterializeResult` plus `audit`, the terminal audit taken after the write, `releases`, and `provenance` |
681
+ | `catalog` | `MaterializeResult` plus `entries`, `mirrors`, `dropped`, `releases`, and `provenance` |
682
+ | `overwrite` | The `catalog` value plus `audit` and `note` on a partial run |
575
683
 
576
684
  Every failure reports the same envelope instead: `{ "error": { "code": …, "message": … } }`. The
577
685
  code is a `ScaffoldErrorCode`, or `USAGE` for a command line that never became a command, or
@@ -635,10 +743,10 @@ because the shape is chosen once and read afterwards: `new` refuses the advisory
635
743
  `repair` need the plan to describe and restore a target that already has that shape. A library
636
744
  caller creating a workspace holds the same refusal, and the Compile section below states it.
637
745
 
638
- `bin`, `setup`, `guides`, `distribution`, `integration`, `conformance`, `service`, `vendors`,
639
- `global`, and `showcase` are structural facts. Each is set only when the workspace physically ships
640
- the directory or exact-case file that defines it, never because of the workspace's name and never
641
- because a sibling fact is set.
746
+ `bin`, `setup`, `guides`, `integration`, `conformance`, `service`, `vendors`, `global`, and
747
+ `showcase` are structural facts. Each is set only when the workspace physically ships the directory
748
+ or exact-case file that defines it, never because of the workspace's name and never because a
749
+ sibling fact is set.
642
750
 
643
751
  `setup` registers every root `tests/setup*.test.ts` proof in one Node project that loads
644
752
  `tests/setup.ts`. A nested or wrong-case match does not set the fact. The generated manifest emits
@@ -659,9 +767,12 @@ Adding a structural proof is therefore these steps, in order: write the file; de
659
767
  regenerates the root configuration and registers the project. `audit` reports whichever piece is
660
768
  still outstanding at each step.
661
769
 
662
- `distribution` projects only when the workspace also publishes at least one `src` environment. It
663
- packs and installs the published artifact, so without that axis there is nothing to pack, and the
664
- declared flag alone adds no project, no `test:distribution` script, and no gate entry.
770
+ `distribution` is not a field at all. A published `src` environment is its whole condition, read
771
+ from the `src` axis the blueprint already carries. The proof packs and installs the published
772
+ artifact, so a workspace publishing none has nothing for it to read and gets no project, no
773
+ `test:distribution` script, and no gate entry. A workspace publishing any gets the project, the
774
+ script, the `prepublishOnly` entry, and `tests/distribution.test.ts` itself. Limits states why this
775
+ is the one proof scaffold generates from the workspace's own shape.
665
776
 
666
777
  `service` says the workspace runs a live-service Vitest project over `tests/service`, and it alone
667
778
  registers that project, its `test:service` script, and the `tests/setupService.ts` readiness module
@@ -743,7 +854,8 @@ place, and the caller that picked the shape is the one holding it.
743
854
  Off-contract input is different. A value that is not the exact shape raises `ScaffoldError` coded
744
855
  `INVALID`, because it is not a question anyone can answer. Each entry point snapshots the caller's
745
856
  value first and then guards the snapshot, so a property backed by an accessor is refused rather than
746
- read.
857
+ read. `isPlan` refuses an artifact at `package.json` unless it carries `birth` ownership, because a
858
+ plan claiming `content` or `presence` there contradicts the compiler-produced plan.
747
859
 
748
860
  Overrides replace a drafted artifact's content whole. The gate checks each override against the
749
861
  blueprint's full draft before a group selection narrows the returned plan, so an override outside a
@@ -785,10 +897,23 @@ content is produced. `Ownership` says what scaffold claims at the path.
785
897
 
786
898
  Presence ownership has separate mechanisms, and a reader needs to know which applies:
787
899
 
788
- | Mechanism | Paths | Bytes belong to | Cost |
789
- | --------------- | ------------------------------------------------- | --------------------- | ------------------------------------------------------- |
790
- | Verb-owned | `CATALOG_AGENT_PATH` and dependency guide mirrors | `catalog` or `mirror` | The owning verb is the only route for a later update. |
791
- | Workspace-owned | `WORKSPACE_OWNED_PATHS`, which holds `.gitignore` | The target workspace | Present bytes receive no later canonical ignore update. |
900
+ | Mechanism | Paths | Bytes belong to | Cost |
901
+ | --------------- | ------------------------------------------------- | -------------------------------------------- | ----------------------------------------------------------- |
902
+ | Verb-owned | `CATALOG_AGENT_PATH` and dependency guide mirrors | `catalog` or `mirror` | The owning verb is the only route for a later update. |
903
+ | Workspace-owned | `WORKSPACE_OWNED_PATHS`, which holds `.gitignore` | The target workspace | Present bytes receive no later canonical ignore update. |
904
+ | Plan-owned | `DISTRIBUTION_TEST_PATH` | The plan, until the workspace writes its own | A deleted file is restored from the plan on the next write. |
905
+ | Unhydrated | Every vendored path a core-compiled plan carries | Scaffold, after a hydrating face reads them | Hydration restores the byte claim the core plan omits. |
906
+
907
+ The unhydrated row is the one a core-only caller meets most, and reading it as a claim about the
908
+ path is the mistake it invites. `Compiler` runs in the pure core face, which cannot read the
909
+ vendored data root, so every host artifact it plans carries `presence`: a claim over bytes nobody
910
+ has read is a claim no comparison could check. A `src: ['core']` plan therefore reports `presence`
911
+ for `AGENTS.md`, `.claude/settings.json`, `tests/policy.test.ts`, and every other vendored path, and
912
+ a consumer concluding from that reading that scaffold never replaces those bytes is wrong.
913
+ `Materializer` hydrates the plan before it audits or writes: hydration reads the vendored root and
914
+ turns each path scaffold owns the bytes of into a content-owned artifact, leaving `presence` on the
915
+ workspace-owned paths and the mirror pointers the preceding rows name. What a verb claims at a
916
+ vendored path is the hydrated ownership, and `HostArtifact` carries the same narrowing on the type.
792
917
 
793
918
  Birth ownership is what makes a generated workspace the consumer's. `materialize` writes a
794
919
  birth-owned path into a vacant target. A later `repair` or `overwrite` call treats that path as
@@ -799,16 +924,29 @@ You own `tests/setup.ts`, the selected `tests/setupBrowser.ts`, `tests/setupServ
799
924
  `tests/setupService.ts`, and `tests/setupGlobal.ts` modules, each root `tests/setup*.test.ts` proof,
800
925
  the selected environment entry tests under `tests/src` and `tests/app`, the
801
926
  `tests/src/bin/main.test.ts` file, and the `tests/integration.test.ts` seed. Scaffold writes those
802
- planned files only during materialize and leaves later edits or deletions alone. You also own the `tests/guides.test.ts`,
803
- `tests/distribution.test.ts`, `tests/conformance.test.ts`, and `tests/service/**/*.test.ts` proof
804
- files that you add to select their projects. Scaffold content-owns `tests/setupPolicy.ts`,
927
+ planned files only during materialize and leaves later edits or deletions alone. You also own the
928
+ `tests/guides.test.ts`, `tests/conformance.test.ts`, and `tests/service/**/*.test.ts` proof files,
929
+ each of which selects its project by being written. Scaffold content-owns `tests/setupPolicy.ts`,
805
930
  `tests/policy.test.ts`, and `tests/config.test.ts`; `repair` and `overwrite` restore those files when
806
931
  their bytes drift or the files are missing.
807
932
 
808
- Content ownership does not preserve an arbitrary custom Vitest project. Fixed optional proofs are
809
- selected by their defining paths, as `guides`, `distribution`, `integration`, `conformance`, and
810
- `service` are. A workspace that needs other local configuration must keep those edits outside a
811
- content-owned file; `repair` restores that file to the canonical project set.
933
+ `tests/distribution.test.ts` is the one proof scaffold generates, and the one test artifact it
934
+ claims by presence. Generation is the line, not writing: scaffold writes the vendored
935
+ `tests/policy.test.ts` and `tests/config.test.ts` proofs too, and restores them, but those are the
936
+ shared file set's own bytes copied into the target. The distribution proof is derived from the
937
+ workspace's own shape instead, which is why it is the one proof a generated file can be. A
938
+ publishing workspace missing that file reports `missing` drift, and `repair` or `overwrite` writes
939
+ the generated proof there. A workspace that replaced the generated proof with a better one keeps its
940
+ own bytes exactly: presence compares existence, so no verb reads what is already at the path, none
941
+ replaces it, and none ever reports it stale. Deleting the file is how you ask for the generated
942
+ proof back; editing it is how you keep your own. Limits states what makes this proof generable when
943
+ the others are not.
944
+
945
+ Content ownership does not preserve an arbitrary custom Vitest project. The optional proof projects
946
+ are fixed: `guides`, `integration`, `conformance`, and `service` are selected by their defining
947
+ paths, and `distribution` by a published `src` environment. A workspace that needs other local
948
+ configuration must keep those edits outside a content-owned file; `repair` restores that file to the
949
+ canonical project set.
812
950
 
813
951
  An audit reports one `Finding` per planned path, followed by any foreign path beneath the groups
814
952
  the plan covers. Every planned finding carries its artifact's `ownership`. A foreign finding has
@@ -895,12 +1033,18 @@ lookup verdict records why the registry row could not enter a layer.
895
1033
 
896
1034
  ## Dependency floors
897
1035
 
898
- Every scaffold-owned range from its dependency tables is a floor: a caret over a whole
1036
+ Every scaffold-owned runtime or development range is a floor: a caret over a whole
899
1037
  `major.minor.patch` version. The triple is the newest release the registry served when that floor
900
1038
  was last raised, so a workspace generated with no network still receives the latest floor scaffold
901
- knew rather than a bare `major.0.0`. Caller extras and peers pass through unchanged. Extras follow
902
- `EXTRA_RANGE_PATTERN`; fleet peers follow `ORKESTREL_RANGE_PATTERN`; foreign peers follow
903
- `FLOOR_RANGE_PATTERN`.
1039
+ knew rather than a bare `major.0.0`. A `Blueprint.peers` row is written during creation into a
1040
+ vacant target. After creation, peer declarations and `peerDependenciesMeta` are caller-owned:
1041
+ `audit`, `repair`, `catalog`, and `overwrite` do not invent, rewrite, insert, or remove them. Caller
1042
+ extras also pass through unchanged. Extras follow `EXTRA_RANGE_PATTERN`; fleet peers follow
1043
+ `ORKESTREL_RANGE_PATTERN`; foreign peers follow `FLOOR_RANGE_PATTERN`.
1044
+
1045
+ The distribution project packs a caller-owned peer beside a co-peer witness that requires an exact
1046
+ version. The real npm resolver accepts the preserved range. Its narrowed-range control reports
1047
+ `ERESOLVE`.
904
1048
 
905
1049
  The floors live in scaffold's own `package.json`. `BASE_DEV_DEPENDENCIES` and the tables beside it
906
1050
  derive each row scaffold installs from that manifest, and the self-pin from its `version` field, so
@@ -925,18 +1069,23 @@ alike.
925
1069
 
926
1070
  ### What each verb reads
927
1071
 
928
- | Verb | Reads | Writes on a complete answer | With no answer |
929
- | ----------- | ----------------------------------------------------------- | ----------------------------------------------- | -------------------------------------------------------- |
930
- | `new` | Every `@orkestrel/*` row the compiled manifest declares | The plan's ranges, before the target is opened | `FETCH`, exit `1`, nothing written |
931
- | `audit` | Every declared fleet row and planned foreign row | Nothing | Failed verdicts reported, exit `1`, nothing written |
932
- | `repair` | Every declared fleet row and planned foreign row | The manifest's ranges, beside the repair | `FETCH`, exit `1`, nothing written |
933
- | `catalog` | The organization package list and every packument behind it | The manifest's ranges, beside the package table | `FETCH`, exit `1`, nothing written |
934
- | `overwrite` | Everything `repair` and `catalog` read | Everything `repair` and `catalog` write | The offline half stands, `note` names the step, exit `1` |
1072
+ `new`, `audit`, and `repair` read declared versions and the vendored host. `catalog` reads
1073
+ organization membership, its packuments, and the selected guides. `overwrite` reads every surface
1074
+ that `repair` and `catalog` read. A network-forced floor is drift except for a successful `new` run;
1075
+ an explicit `--offline` floor is intentional, and the verb's result decides its exit. `catalog` has
1076
+ no offline form. `overwrite` commits repair and removal before it starts the catalog step.
1077
+
1078
+ Each verb resolves a surface's complete answer before it opens that surface's write transaction, so
1079
+ a partial answer never becomes a partial pin set. `overwrite` keeps the repair and removal work it
1080
+ committed before a later catalog refusal and records that refusal in `note`.
935
1081
 
936
- Each verb resolves the whole set before it opens a write transaction, so a partial answer never
937
- becomes a partial pin set. `overwrite` is the exception, and deliberately: its offline repair and
938
- deletion have already landed by the time the network half runs, so a step that produces no answer is
939
- collected into `note` and reported rather than discarding work that succeeded.
1082
+ | Verb | Reads live | When the network forces a floor | With `--offline` |
1083
+ | ----------- | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
1084
+ | `new` | Declared versions and the vendored host | Writes the distributed version and host floors; exits `0` after creating the workspace | Reads no upstream surface, writes the same floors, and exits `0` after creating the workspace |
1085
+ | `audit` | Declared versions and the vendored host | Compares through the distributed floors and exits `1` | Compares through the floors; exits `0` for an aligned target or `1` for drift |
1086
+ | `repair` | Declared versions and the vendored host | Repairs from the distributed floors and exits `1`, even when the terminal audit is aligned | Repairs from the floors; the terminal audit decides exit `0` or `1` |
1087
+ | `catalog` | Organization membership, its packuments, and the selected guides | Refuses a membership or version failure with `FETCH` and exit `1`; preserves each failed guide's local mirror and exits `1` | Is a usage error; exits `2` and writes nothing |
1088
+ | `overwrite` | Everything `repair` and `catalog` read | Keeps completed repair and deletion work, names each floor or refused catalog step in `note`, and exits `1` | Repairs, deletes, and writes version floors; skips `catalog`, records that refusal in `note`, and exits `1` |
940
1089
 
941
1090
  A fleet row is compared exactly — `^0.1.0` is stale the moment the registry serves `0.1.2` — and
942
1091
  that inequality alone raises `audit` to exit `1`. A foreign row is compared inside its declared
@@ -964,6 +1113,23 @@ directories, the bench scripts, the shared policy register, the byte-identical r
964
1113
  the guide mirrors a generated workspace starts from. `HOST_PATHS` is the candidate list; a plan
965
1114
  carries the subset its target selects, because a workspace never mirrors its own guide.
966
1115
 
1116
+ The `host.json` file at the repository root is the committed live inventory. Each entry carries the
1117
+ SHA-256 digest of its file content, and the inventory carries a membership digest over its declared
1118
+ paths and file digests. Run `npm run build:inventory` whenever a vendored byte or path changes; the
1119
+ `config` project refuses a stale inventory. Run that gate against a quiescent checkout: its fresh and
1120
+ committed reads cannot distinguish stale data from a source edit made while the gate runs.
1121
+
1122
+ The installed release fixes which paths a target owns. A live inventory can update bytes only for
1123
+ those paths; it can neither introduce a path nor delete one. A path added upstream is invisible
1124
+ until a release adds it to the installed manifest. Remove a vendored path in the same change that
1125
+ ships the release which removes it from that manifest.
1126
+
1127
+ At the default `UpstreamOptions.retries` value, an aligned target spends one request on `host.json`,
1128
+ and each installed path whose live digest differs from the target adds one request for its bytes. A
1129
+ positive `retries` value can repeat a request after a transport fault. Raw-host propagation lag after
1130
+ a commit is a property of the content host. Scaffold neither creates that lag nor presents a stale
1131
+ response as fresher than the host served it.
1132
+
967
1133
  `.claude/settings.json` is in that set, and the artifact planned for it is content-owned. `repair`
968
1134
  and `overwrite` restore its bytes, so an edit made to it inside a target is reverted at the next
969
1135
  visit and reported as drift until then. Put an operator grant in `.claude/settings.local.json`
@@ -982,9 +1148,10 @@ stageHost(process.cwd(), 'dist/host') // one ManifestEntry per file staged
982
1148
  Each vendored path is copied to a storage name, and every dot that opens a segment comes off,
983
1149
  because npm's own ignore rules would drop a leading-dot entry from the tarball. A dotted file at the
984
1150
  root moves under `dotfiles/` so it cannot collide with an undotted sibling. `manifest.json` is
985
- written last and declares the whole membership: one entry per file, the sorted directory inventory,
986
- and a SHA-256 digest over both. The digest is what detects a membership edit that did not update
987
- it, and the directory inventory is what makes a declared empty directory survive a file walk.
1151
+ written last and declares the whole membership: one entry per file with a digest computed from the
1152
+ staged destination after its copy, the sorted directory inventory, and a SHA-256 digest over both.
1153
+ The membership digest detects an edit that did not update the manifest, and the directory inventory
1154
+ makes a declared empty directory survive a file walk.
988
1155
 
989
1156
  A missing vendored path is refused rather than staged around, and the refusal names every missing
990
1157
  path at once. That is why `guides/scaffold.md` — this file — must exist before `npm run build`
@@ -996,6 +1163,21 @@ module's own location rather than from the caller's working directory. `--from`
996
1163
  else. A root carrying no manifest at all is read as a raw checkout, and artifact paths map onto it
997
1164
  one to one.
998
1165
 
1166
+ ### Integrity
1167
+
1168
+ HTTPS supplies Transport Layer Security (TLS) for each fetched response, and the reader applies its
1169
+ per-response and per-call byte budgets before it accepts content. It carries each fetched vendored
1170
+ response's decoded content as hexadecimal before any character decoding and verifies that content
1171
+ against the digest in `host.json`, then verifies the inventory against its membership digest.
1172
+ Transport encoding is transparent and does not enter the comparison. The path never character-
1173
+ decodes and re-encodes the content.
1174
+
1175
+ This posture supplies integrity, not authenticity. An attacker who can serve the files can also
1176
+ serve a matching inventory. The residual is direct: fetched bytes govern agent behavior in a target
1177
+ that has no release gate. Run `audit` to preview the change, use the `--offline` option to pin the
1178
+ distributed floors, and keep operator grants in `.claude/settings.local.json`; scaffold does not
1179
+ read or write that file.
1180
+
999
1181
  ## Generated workspace
1000
1182
 
1001
1183
  A workspace's file set is a function of its axes plus its structural facts. Nothing is fixed
@@ -1026,6 +1208,12 @@ except the manifest.
1026
1208
  integration selection also emits a birth-owned `tests/integration.test.ts` seed that imports each
1027
1209
  selected public barrel and records its initial empty exports for the consumer to replace with an
1028
1210
  observable cross-environment flow.
1211
+ - One template artifact, `tests/distribution.test.ts`, for a workspace publishing any `src`
1212
+ environment. It is the packed-package proof, and it is claimed by presence rather than birth, so a
1213
+ workspace that replaces it keeps its replacement. A published browser environment adds the
1214
+ real-browser stage to it: the stage bundles the installed package with the workspace's own
1215
+ `configs/browsers.ts` resolution, serves the bundle over a loopback server, and drives it in
1216
+ Playwright Chromium.
1029
1217
  - One template artifact each for `README.md` and `guides/README.md`.
1030
1218
  - One host artifact per vendored path the workspace selects. A vendored directory is one planned
1031
1219
  path that expands into the files the data root stores beneath it.
@@ -1082,6 +1270,10 @@ result.written // every path created
1082
1270
  materializer.destroy()
1083
1271
  ```
1084
1272
 
1273
+ When a `Materializer` uses a value `Host`, each mutating call stages the host under a private
1274
+ `#fill` root in the operating system's temporary directory and removes that root in a `finally`
1275
+ block. A process killed during the mutation can leave the temporary root behind.
1276
+
1085
1277
  `resolveContainedPath` refuses a lexical escape, a physical link out of the root, and a dangling
1086
1278
  link whose raw target contains a `..` segment. It returns the lexical join of `root` and `path` — an
1087
1279
  absolute path under `root` — after checking the namespace, not an open filesystem handle. Its
@@ -1096,7 +1288,7 @@ in what survives that collapse. A `..` the caller wrote cancels the segment befo
1096
1288
  what `hop` points at. The collapse only ever shortens the path, so nothing reaches outside it this
1097
1289
  way; the answer is a lexical location resolved through links, not a physical one.
1098
1290
 
1099
- Read the registry and the guide host:
1291
+ Read the registry and the repository host:
1100
1292
 
1101
1293
  ```ts
1102
1294
  import { Upstream } from '@orkestrel/scaffold/server'
@@ -1188,10 +1380,10 @@ What a reader will look for and not find.
1188
1380
  **Guide parity has a bounded reach.** [`tests/guides.test.ts`](../tests/guides.test.ts) proves that
1189
1381
  the Surface tables match the core and server barrels in each direction, the method tables match the
1190
1382
  behavioral declarations, relative links resolve, and named imports in TypeScript fences resolve. It
1191
- does not resolve arbitrary backticked prose spans or typecheck a whole fence. The same suite
1192
- executes the transcribed pure examples for blueprint defaults, compile refusal, and error-code
1193
- narrowing. Other trailing comments remain guide claims rather than build answers. The verdicts that
1194
- are measured are the ones a consumer hovers:
1383
+ does not resolve arbitrary backticked prose spans or typecheck a whole fence. The same suite keeps
1384
+ the command reference aligned with the executable and executes the transcribed pure examples for
1385
+ blueprint defaults, compile refusal, and error-code narrowing. Other trailing comments remain guide
1386
+ claims rather than build answers. The verdicts that are measured are the ones a consumer hovers:
1195
1387
  [`tests/distribution.test.ts`](../tests/distribution.test.ts) drives every `@example` the built
1196
1388
  declarations print against the installed package, scores each verdict it can read as a value, and
1197
1389
  names exactly the ones it cannot. Executing the remaining fences would require fixtures for each
@@ -1232,32 +1424,157 @@ nothing. This is deliberate: a generated sample entity is repeatedly mistaken fo
1232
1424
  implementation. What a consumer does first is write the module's `types.ts`, then the
1233
1425
  implementation that conforms to it, then export both from the barrel — the order `AGENTS.md` fixes.
1234
1426
 
1235
- **A selected distribution, conformance, or live-service proof is registered, but none is written for
1236
- you.** Scaffold registers `conformance` and `service` when their structural facts are set, and
1237
- registers `distribution` only when the workspace also publishes `src`. In a publishing workspace,
1238
- `distribution` and `service` run from `prepublishOnly` and `conformance` stays in `test`. In a
1239
- `private: true` workspace, `distribution` is absent, `service` runs from `test`, and there is no
1240
- `prepublishOnly` at all. Scaffold emits no proof into any registered project, because each names
1241
- something only the package knows: the behavior its own packed artifact must hold after it is
1242
- installed, the official artifact a conformance check measures against, and the service a live proof
1243
- drives. A generated placeholder would read as a proof while measuring nothing, so the file a
1244
- consumer writes is the file that selects the project.
1245
-
1246
- A distribution proof carries one contract scaffold does enforce from the outside. The generated
1247
- `prepublishOnly` invokes it as `npm run test:distribution -- --mode release`, and a proof that reads
1248
- `import.meta.env.MODE === 'release'` must **fail** on an unreachable registry rather than skip. An
1249
- ordinary local run may skip that case, because a developer offline is not a defect; a release run
1250
- may not, because skipping there passes the publish gate without ever proving the artifact installs.
1251
- Scaffold writes no proof, so honouring the flag is the consumer's, and a proof that ignores it
1252
- reports green on exactly the runs that matter.
1253
-
1254
- The consequence is one empty-project case per registered proof. A publishing blueprint carrying
1255
- `distribution` with no `tests/distribution.test.ts`, or any blueprint carrying `conformance` with no
1256
- `tests/conformance.test.ts`, registers a project whose include resolves to nothing, and Vitest exits
1257
- non-zero on it. A blueprint carrying `service` gets `tests/setupService.ts` the root configuration
1258
- names that module by path, so an absent one fails the project's load rather than its run — and still
1259
- no suite beneath `tests/service`, so `test:service` reports no test files until the consumer writes
1260
- the first one. Every case is visible the first time the script runs, which is why none is silent.
1427
+ **Scaffold generates the distribution proof and refuses to generate every other one.** The subject
1428
+ is what separates them, and it is the whole rule. Generating a file is not the same as writing one:
1429
+ scaffold also writes `tests/policy.test.ts` and `tests/config.test.ts` into a target, byte for byte
1430
+ from the shared file set, and the distribution proof is the one it derives from the workspace it is
1431
+ writing into.
1432
+
1433
+ A distribution proof's every assertion derives from the artifact the workspace installs: the
1434
+ `exports` map the packed tarball declares, the built declarations beside it, and the module objects
1435
+ a Node import, a CommonJS require, and a real browser hand back from that installed tree. Nothing
1436
+ there has to be named, so one generated file measures every publishing workspace, and it stays true
1437
+ as that workspace's published surface moves.
1438
+
1439
+ A guide, conformance, live-service, or setup proof asserts something scaffold cannot read: the API a
1440
+ guide fence claims, the official runner a conformance check measures against, the service a live
1441
+ proof drives, and what a setup module does. That subject is what no generated file can reach, and
1442
+ the claim here is about the subject rather than about every property those files have. A structural
1443
+ property of the same files can be derivable whether each root `tests/setup*.ts` module is
1444
+ reachable from the root configuration is one — and a file asserting it would still leave the
1445
+ module's behavior unmeasured. A generated file there would read as a proof while measuring nothing,
1446
+ which is worse than an absent one. So the file a consumer writes is what selects each of those
1447
+ projects, and `tests/distribution.test.ts` is the one proof scaffold generates for you.
1448
+
1449
+ Registration follows the same split. Scaffold registers `conformance` and `service` when their
1450
+ structural facts are set, and registers `distribution` whenever the workspace publishes at least one
1451
+ `src` environment. In a publishing workspace, `distribution` and `service` run from `prepublishOnly`
1452
+ and `conformance` stays in `test`. In a `private: true` workspace, `distribution` is absent,
1453
+ `service` runs from `test`, and there is no `prepublishOnly` at all. One gap the project set cannot
1454
+ show, `audit` reports directly: a filled `tests/setup*.ts` module that no `tests/setup*.test.ts`
1455
+ proof covers raises the non-blocking `setup` question, which names the modules and the proof to add.
1456
+ Scaffold generates nothing there either, because what that proof asserts is those modules' own
1457
+ behavior, which only the workspace that wrote them can state.
1458
+
1459
+ The generated proof partitions the installed `exports` map rather than sampling it. Every published
1460
+ subpath lands in exactly one of driven, undeclared, or excluded, and a totality assertion holds that
1461
+ partition against the map's own subpath list, so a subpath the proof classifies into none of them
1462
+ reddens instead of disappearing. Another assertion beside it names every driven subpath whose entry
1463
+ resolves no Node target and no browser target: each drive retires itself for such a subpath, so
1464
+ membership of the partition alone would leave one measured by nothing. Together they hold that every
1465
+ published subpath is driven, or is named where the proof cannot drive it.
1466
+
1467
+ A subpath is driven when its entry resolves a declaration, and each measurement resolves that entry
1468
+ under the conditions of the driver taking it rather than under one shared set. The Node ESM runtime
1469
+ resolves `node-addons`, `node`, `import`, and `module-sync`; the Node CommonJS runtime resolves
1470
+ `node-addons`, `node`, `require`, and `module-sync`; Vite's production client build resolves
1471
+ `module`, `browser`, `production`, and `import`. A subpath whose Vite resolution lands under
1472
+ `dist/src/browser/` is driven in a real browser and the Node drives retire for it; every other
1473
+ subpath is imported where the Node ESM set resolves a target and required where the Node CommonJS
1474
+ set resolves one.
1475
+
1476
+ The CommonJS compile probe resolves the declaration under `types`, `node`, and `require`, then reads
1477
+ that declaration's format. TypeScript accepts an existing declaration target directly. Otherwise it
1478
+ substitutes beside the resolved JavaScript target: `.cjs` maps to `.d.cts`, `.mjs` maps to `.d.mts`,
1479
+ and `.js` maps to `.d.ts`. A missing target under `types` leaves that condition unresolved, so the
1480
+ walk continues through the remaining conditions or fallback members. A `.d.cts` declaration admits
1481
+ the subpath. A `.d.mts` declaration refuses it. A `.d.ts` declaration takes the nearest enclosing
1482
+ physical `package.json` file from its own directory: a `"type": "module"` field refuses, while
1483
+ `"type": "commonjs"`, an omitted field, or a manifest that cannot supply a readable field admits.
1484
+ A directory named `package.json` starts no scope, so the walk continues outward. The runtime target
1485
+ does not decide compile membership. The runtime drive separately resolves under `node-addons`,
1486
+ `node`, `require`, and `module-sync`, then loads the subpath whatever declaration format the compile
1487
+ probe found. An invalid non-list target beside a valid CommonJS declaration therefore reaches Node's
1488
+ `ERR_INVALID_PACKAGE_TARGET` failure instead of being dropped during classification.
1489
+
1490
+ The mirror assertion reports a CommonJS typing defect only when the entry's own mapping declares an
1491
+ explicit `require` condition, the Node `require` resolver reaches the entry, and the selected
1492
+ declaration refuses a CommonJS consumer. A `default` branch that merely resolves under the require
1493
+ condition set makes no CommonJS claim.
1494
+
1495
+ Each drive compares against the declaration its own consumer reads, resolved under the conditions
1496
+ TypeScript applies for that resolution and importing format: `types` first and the format's own
1497
+ condition after it, with `node` between them for the `node16` and `nodenext` resolutions and left
1498
+ out for `bundler`, which is the set the browser drive compares against. The import declaration and
1499
+ the require declaration are resolved independently and kept separately on the entry, so a dual
1500
+ subpath is compiled against the declaration each consumer format reads rather than against whichever
1501
+ one answered first. That is what drives a `require`-only subpath declaring its types inside
1502
+ `require`, and what admits a conventional subpath that publishes no `types` condition but ships the
1503
+ adjacent declaration TypeScript substitutes from its runtime target.
1504
+
1505
+ A subpath is undeclared when it resolves no declaration and names a runtime target, which is a
1506
+ defect, because a consumer importing it compiles against nothing under `node16`. A target is a
1507
+ runtime target when its own file name carries no extension at all, or carries `.js`, `.mjs`,
1508
+ `.cjs`, or `.node`. An extensionless target loads, because `require` reads such a file through its
1509
+ JavaScript handler; a `.wasm` target does not, because it carries an extension that is not a
1510
+ JavaScript one. A
1511
+ `.node` target is named beside them because `require` loads a native addon through its own handler
1512
+ rather than the JavaScript one, and the addon publishes names to whatever loads it. The test is the
1513
+ extension the name carries rather than a denylist of asset extensions. Reading the
1514
+ file name rather than the whole path is load-bearing: `./dist/bundle.js/feature` and
1515
+ `./dist/v1.2/index` are modules, and reading the path gets each of them wrong. What the rule
1516
+ excludes is stated where the proof is emitted: an extensionless file published for a reader, such as
1517
+ a `LICENSE` at a subpath, reports undeclared until it is given an extension or a declaration. That
1518
+ is the safe direction: the proof names a subpath it cannot vouch for rather than passing one it
1519
+ never measured.
1520
+
1521
+ A subpath is excluded when it resolves no declaration and names no runtime target: the
1522
+ `./package.json` pointer, a published stylesheet, and a WebAssembly binary are read rather than
1523
+ imported, and the proof names them where it excludes them.
1524
+
1525
+ Classification reads every target the entry names under every condition, and the members of a
1526
+ fallback list with them: Node reads an array in an exports entry as a list of fallbacks, and a
1527
+ reader taking a later member takes a file the installed tree still owes. Node's package-target rules
1528
+ apply inside that list, so a member naming a path outside the package or carrying a `.`, `..`, or
1529
+ `node_modules` segment is skipped rather than resolved or collected: Node falls through to the next
1530
+ member, and no reader can take the one it passed. A standalone target Node rejects the same way is
1531
+ still read and reported, because Node throws on one rather than falling through to anything. So a
1532
+ `require`-only CommonJS subpath carrying no declaration reddens too, and a `.d.ts` or `.d.cts`
1533
+ target never satisfies the runtime-target test, so a types-only condition cannot stand in for a
1534
+ missing declaration.
1535
+
1536
+ A `./*` subpath pattern is read as an ordinary subpath, with no expansion of the `*`, so a pattern
1537
+ naming a runtime target reddens rather than landing in excluded. That is deliberate: excluding the
1538
+ pattern would account for a whole family of published subpaths and measure none of them, which is
1539
+ the silence this partition exists to close. Measuring the family means expanding the pattern against
1540
+ the installed tree and driving each match, which this proof does not do. A maintainer meeting that
1541
+ red is reading the honest answer — the proof does not measure that family — rather than a defect in
1542
+ the proof.
1543
+
1544
+ The proof generated for a workspace publishing no browser face asserts that no browser face exists.
1545
+ That variant drives a Node import and a Node require and carries no browser branch, because the
1546
+ branch follows a published browser face on the `src` axis: the browser drive measures the packed
1547
+ artifact, and only a published face is packed. A workspace that selects `browser` on its `app` axis
1548
+ alone declares `playwright` and `@vitest/browser-playwright` and gets `configs/browsers.ts` emitted,
1549
+ and its proof still carries no branch: the selector reads the `src` axis, and a generated manifest
1550
+ packs `dist/src`, so an application face is neither selected nor packed. The imports the branch
1551
+ needs are declared by either axis, so they do not select the branch. The `vite` import selects
1552
+ nothing either: every workspace declares `vite`, whatever it publishes. In a core-only workspace,
1553
+ those imports resolve to nothing, and emitting the branch there would fail its own `check` and
1554
+ `lint:check` gates. Its Node cases retire themselves for a browser face, so a face published after
1555
+ the file was written would leave nothing measuring it. The assertion reddens instead, and names the
1556
+ subpath a browser branch is owed for. The remedy it carries is to delete the file and run `repair`,
1557
+ which writes the variant that carries the branch — the same route presence ownership already gives
1558
+ you for asking for the generated proof back.
1559
+
1560
+ The generated distribution proof takes its release contract from the outside. The generated
1561
+ `prepublishOnly` invokes it as `npm run test:distribution -- --mode release`, and the proof reads
1562
+ `import.meta.env.MODE === 'release'` and **fails** on an unreachable registry rather than skipping.
1563
+ An ordinary local run skips that case, because a developer offline is not a defect; a release run
1564
+ does not, because skipping there passes the publish gate without ever proving the artifact installs.
1565
+ A workspace that replaces the generated proof takes that contract with it: presence ownership leaves
1566
+ a replacement alone, so a replacement that ignores the flag reports green on exactly the runs that
1567
+ matter.
1568
+
1569
+ The consequence is one empty-project case per refused proof. A blueprint carrying `conformance` with
1570
+ no `tests/conformance.test.ts` registers a project whose include resolves to nothing, and Vitest
1571
+ exits non-zero on it. A blueprint carrying `service` gets `tests/setupService.ts` — the root
1572
+ configuration names that module by path, so an absent one fails the project's load rather than its
1573
+ run — and still no suite beneath `tests/service`, so `test:service` reports no test files until the
1574
+ consumer writes the first one. The `distribution` project no longer has that case: `new` writes the
1575
+ proof into the workspace it registers the project in, and a target that later lost the file reports
1576
+ drift that `repair` closes. Every remaining case is visible the first time the script runs, which is
1577
+ why none is silent.
1261
1578
 
1262
1579
  None of those folds into `integration`, which measures a different axis rather than a smaller
1263
1580
  one: the workspace's selected environments compose through their public barrels. The generated seed