@orkestrel/scaffold 0.0.20 → 0.0.22

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.
Files changed (69) hide show
  1. package/dist/bin/scaffold.js +385 -148
  2. package/dist/bin/scaffold.js.map +1 -1
  3. package/dist/host/AGENTS.md +61 -42
  4. package/dist/host/CLAUDE.md +39 -387
  5. package/dist/host/agents/orchestration.md +454 -0
  6. package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +25 -7
  7. package/dist/host/agents/skills/orkestrel-align-packages/SKILL.md +2 -2
  8. package/dist/host/agents/skills/orkestrel-build-application/SKILL.md +42 -31
  9. package/dist/host/agents/skills/orkestrel-build-application/references/application.md +129 -0
  10. package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +3 -3
  11. package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +4 -4
  12. package/dist/host/agents/skills/orkestrel-debrief/references/instruction-audit.md +12 -0
  13. package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +167 -0
  14. package/dist/host/agents/skills/orkestrel-falsify/references/brief.md +98 -0
  15. package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +148 -0
  16. package/dist/host/agents/skills/orkestrel-harden-package/SKILL.md +4 -2
  17. package/dist/host/agents/skills/orkestrel-harden-package/references/centralization.md +5 -5
  18. package/dist/host/agents/skills/orkestrel-harden-package/references/research.md +2 -0
  19. package/dist/host/agents/skills/orkestrel-polish-surface/SKILL.md +11 -6
  20. package/dist/host/claude/agents/analyst.md +59 -0
  21. package/dist/host/claude/agents/application.md +1 -1
  22. package/dist/host/claude/agents/builder.md +1 -1
  23. package/dist/host/claude/agents/checker.md +5 -1
  24. package/dist/host/claude/agents/codex.md +120 -87
  25. package/dist/host/claude/agents/grok.md +44 -32
  26. package/dist/host/claude/agents/implementer.md +1 -1
  27. package/dist/host/claude/agents/orkestrel.md +3 -3
  28. package/dist/host/claude/agents/planner.md +9 -3
  29. package/dist/host/claude/agents/researcher.md +6 -5
  30. package/dist/host/claude/agents/reviewer.md +17 -6
  31. package/dist/host/claude/agents/scout.md +7 -4
  32. package/dist/host/claude/agents/verifier.md +1 -1
  33. package/dist/host/claude/rules/application.md +17 -16
  34. package/dist/host/claude/rules/architecture.md +41 -22
  35. package/dist/host/claude/rules/documentation.md +2 -1
  36. package/dist/host/claude/rules/names.md +7 -6
  37. package/dist/host/claude/rules/quality.md +71 -14
  38. package/dist/host/claude/rules/styles.md +1 -1
  39. package/dist/host/claude/rules/tests.md +18 -2
  40. package/dist/host/claude/rules/typescript.md +1 -1
  41. package/dist/host/claude/rules/workspace.md +6 -3
  42. package/dist/host/claude/settings.json +2 -0
  43. package/dist/host/claude/skills/orkestrel-falsify/SKILL.md +10 -0
  44. package/dist/host/codex/agents/analyst.toml +15 -2
  45. package/dist/host/codex/agents/checker.toml +1 -1
  46. package/dist/host/codex/agents/grok.toml +2 -1
  47. package/dist/host/codex/agents/opus.toml +1 -1
  48. package/dist/host/codex/agents/planner.toml +1 -1
  49. package/dist/host/codex/agents/researcher.toml +2 -2
  50. package/dist/host/codex/agents/reviewer.toml +4 -2
  51. package/dist/host/codex/agents/scout.toml +2 -2
  52. package/dist/host/codex/config.toml +23 -64
  53. package/dist/host/cursor/rules/orchestration.mdc +33 -0
  54. package/dist/host/guides/src/scaffold.md +419 -150
  55. package/dist/host/manifest.json +47 -1
  56. package/dist/host/tests/setupPolicy.ts +130 -12
  57. package/dist/src/core/index.cjs +2059 -913
  58. package/dist/src/core/index.cjs.map +1 -1
  59. package/dist/src/core/index.d.cts +202 -47
  60. package/dist/src/core/index.d.ts +202 -47
  61. package/dist/src/core/index.js +2050 -914
  62. package/dist/src/core/index.js.map +1 -1
  63. package/dist/src/server/index.cjs +83 -33
  64. package/dist/src/server/index.cjs.map +1 -1
  65. package/dist/src/server/index.d.cts +32 -16
  66. package/dist/src/server/index.d.ts +32 -16
  67. package/dist/src/server/index.js +84 -35
  68. package/dist/src/server/index.js.map +1 -1
  69. package/package.json +7 -5
@@ -67,11 +67,41 @@ than aspirational:
67
67
  - `app/browser` reaches server behavior only through shared `app/core` contracts and transports,
68
68
  never through a server implementation import.
69
69
 
70
- A generated `app/server` owns strict `APP_HOST`, `APP_PORT`, and `APP_START_TIMEOUT` parsing, a
71
- repeat-safe HTTP lifecycle, bounded connection behavior, and process signal cleanup. Its exported
70
+ A generated `app/server` owns strict grouped `server.host`, `server.port`, and `server.timeout`
71
+ options plus the `APP_HOST`, `APP_PORT`, and `APP_START_TIMEOUT` environment boundaries. It
72
+ composes the installed router, server, and boundary/security/deadline middleware substrates around
73
+ a fresh `GET /health` dispatcher from `createApplicationDispatcher`, supports repeated start/stop
74
+ cycles and terminal destroy of both the server and its owned dispatcher, and writes exactly one
75
+ `[READY] <name> <url>` diagnostic after process-owned readiness. The process runner owns an emitter
76
+ whose `ApplicationServerRunnerEventMap` publishes `ready(url)` and `fail(error)`; initial
77
+ `ApplicationServerRunnerOptions.on` hooks run before the runner's own announcement and reporting
78
+ listeners; a synchronous fail hook sees an otherwise-unset `process.exitCode` as `undefined` before
79
+ the default reporter sets it to `1`. Concurrent stops join one substrate shutdown. In-process tests park on those events,
80
+ while child-process tests still observe the readiness line across the process boundary. Its exported
72
81
  `reportApplicationServerError` handler writes only a stable configuration, lifecycle, or unknown
73
- failure code; process-owned diagnostics never serialize a rejected value, nested cause, stack, or
74
- other error context.
82
+ failure code; process-owned failures never serialize a rejected value, nested cause, stack,
83
+ secret, or other error context. `ApplicationState` extends middleware's `IdentifierState` and adds
84
+ only the connection fact. `ApplicationServer.url` is `undefined` until a real port is bound and
85
+ again after stop or destroy; the redundant `listening` projection is not part of the generated
86
+ interface. The runner narrows the post-start URL before writing `[READY]`, so it never announces a
87
+ stale or unbound address, and it stops the server as part of failing that narrowing rather than
88
+ leaving a bound listener without a shutdown owner. It also serializes every start and stop on one
89
+ lifecycle queue, so a stop waits for the startup it aborted to settle before closing the server,
90
+ and a restart issued during that shutdown is honoured after it rather than lost.
91
+
92
+ The health contract belongs to whichever layer both hosts can reach. While the server alone reads
93
+ it, `ApplicationRecord`, `APP_HEALTH_METHOD`, and `APP_HEALTH_PATH` stay declared in `app/server`.
94
+ The moment a blueprint declares `app/browser` beside `app/server` — a combination that already
95
+ requires `app/core` — those three declarations move to `app/core` and gain `APP_HEALTH_TIMEOUT`,
96
+ the `isApplicationRecord` guard, and `readApplicationHealth`. That one asynchronous read is the
97
+ whole browser/server boundary: it fetches the running server's health route, reads the body as
98
+ `unknown`, narrows it with the shared guard, and returns the shared `Application` identity or
99
+ `undefined` for an unreachable, slow, or off-contract answer. Nothing is duplicated by the move —
100
+ `app/server` imports the relocated contract from `@app/core`, and `app/browser` still never imports
101
+ a server module. The generated browser entry then mounts `mountBrowserApplication`, which performs
102
+ that single read before mounting and falls back to the locally configured identity when the
103
+ boundary yields `undefined`. A rejected mount reports the context-free
104
+ `[ERROR] Browser application failed`, the browser twin of that server-side discipline.
75
105
 
76
106
  Every environment barrel is an export-star barrel: `index.ts` contains only `export * from './x.js'`
77
107
  rows and nothing else. Named, default, namespace, and type-only barrel rows are absent by design,
@@ -196,7 +226,8 @@ application-browser projection. The root machinery selection never attaches a
196
226
  compilers section sets out.
197
227
 
198
228
  `ViteFacts` is the optional structural-fact slice shared by every root Vite compiler:
199
- `bin`, `integration`, and `service` each select their matching standalone project when `true`;
229
+ `bin` and `integration` each select their matching standalone project when `true`, while `services`
230
+ selects one standalone project for every listed vendor;
200
231
  `global` records the exact-case consumer-owned global-setup module and wires it into each eligible
201
232
  project; `showcase` records the exact-case consumer-owned showcase wrapper and selects only its
202
233
  generated browser machinery.
@@ -222,7 +253,7 @@ interface Blueprint {
222
253
  readonly overrides: readonly Override[]
223
254
  readonly bin: boolean
224
255
  readonly integration: boolean
225
- readonly service: boolean
256
+ readonly services: readonly string[]
226
257
  readonly global: boolean
227
258
  readonly showcase: boolean
228
259
  }
@@ -235,9 +266,10 @@ packages — a peer flagged `optional` also gets a `peerDependenciesMeta` entry.
235
266
  package-specific development dependencies merged over the generated baseline, and may carry any
236
267
  valid npm package name.
237
268
 
238
- `bin`, `integration`, `service`, `global`, and `showcase` are structural project facts. All five obey one law:
239
- each is `true` only when the workspace physically ships the directory or exact-case file that
240
- defines it — never because of the workspace's name, and never because a sibling fact is set.
269
+ `bin`, `integration`, `services`, `global`, and `showcase` are structural project facts. They obey
270
+ one law: each boolean is `true`, and each service name is present, only when the workspace physically
271
+ ships the directory or exact-case file that defines it — never because of the workspace's name, and
272
+ never because a sibling fact is set.
241
273
  `deriveBlueprint` probes those paths, so a fresh compile and an audit of a mature repository agree
242
274
  on what the workspace is.
243
275
 
@@ -249,10 +281,11 @@ on what the workspace is.
249
281
  workspace's own built output, outside the default run: the generated root configuration registers
250
282
  a standalone `integration` project including `tests/integration/**/*.test.ts`, and the manifest
251
283
  emits `test:integration`.
252
- - **`service`** — `tests/service/` exists. It records a slow, opt-in proof project against a foreign
253
- running process, outside the default run: a standalone `service` project including
254
- `tests/service/**/*.test.ts`, with `tests/setupService.ts` after the shared setup, and the
255
- isolated `test:service` script.
284
+ - **`services`** — each direct `tests/service/<vendor>/` directory that contains a `*.test.ts` at
285
+ any depth contributes its directory name to the sorted list. Each vendor gets a slow, opt-in
286
+ `service:<vendor>` proof project against its foreign process, including
287
+ `tests/service/<vendor>/**/*.test.ts`, and an isolated `test:service:<vendor>` script. The
288
+ aggregate `test:service` runs all vendor projects in one invocation.
256
289
  - **`global`** — the physical, exact-case `tests/setupGlobal.ts` file exists. It is the single
257
290
  governing setup-presence fact. A declared `src/browser` project runs that consumer-owned module
258
291
  as `globalSetup`; integration runs it only when `bin` and `integration` are also true.
@@ -263,12 +296,20 @@ on what the workspace is.
263
296
  consumer-only `vite-plugin-singlefile` development dependency. A directory, link, wrong-case
264
297
  name, absent wrapper, demo HTML, script, or installed dependency never implies this fact.
265
298
 
266
- A service workspace owes two companion files beside that directory, and derivation requires both
267
- physically present: `tests/setupService.ts` and `scripts/service.sh`. Either missing companion is a
268
- coded `TARGET` failure naming the missing path rather than a silent `service: false`. This package
269
- emits neither: both are consumer-owned seams, and the generated-workspace section sets out what
270
- each owes its workspace and which proof runs in which gate. Nothing here is inferred — the
271
- executable axis turns on neither proof project, and neither proof project turns on the other.
299
+ Each service vendor owes `tests/service/<vendor>/setup.ts`, whose module-load readiness check probes
300
+ and warms only that vendor. A service workspace also owes the shared `scripts/service.sh`
301
+ provisioner. Derivation fails with a coded `INVALID` question when a vendor's readiness module is
302
+ missing, when a vendor directory contains no test, or when a test uses the former flat
303
+ `tests/service/*.test.ts` layout. An absent shared provisioner is instead a repairable missing
304
+ artifact, so declaring the vendor directory does not deadlock the tool that supplies the skeleton.
305
+ The migration is to move each flat test into `tests/service/<vendor>/`, add that vendor's `setup.ts`,
306
+ and customize the repaired provisioner skeleton. Nothing here is inferred from a source or
307
+ application axis: a vendor serves both.
308
+
309
+ This is a published breaking change: `Blueprint.service` and `ViteFacts.service` were replaced by
310
+ their sorted `services` collections, the single `service` project became one project per vendor,
311
+ and the global `tests/setupService.ts` readiness seam was removed. There is no compatibility
312
+ boolean or declaration file.
272
313
 
273
314
  `Override` replaces a rendered artifact's content at a path, never partially merges it. `Member` is
274
315
  one declared public export of the scaffolded workspace, derived rather than authored.
@@ -286,7 +327,8 @@ by artifact-relative path.
286
327
  self-describing. `PlanSummary` is the dry-run tally by origin and carries both selections. `Finding` is one
287
328
  drift verdict with an optional bounded `observed` byte hex for a stale destination, and `Audit` is
288
329
  the whole diff plus its `clean` and `complete` flags, `questions`, and `drifted` / `missing` /
289
- `foreign` counts. `Question` is one validation issue; `blocking: true` fails the gate closed while
330
+ `foreign` counts.
331
+ `Question` is one validation issue; `blocking: true` fails the gate closed while
290
332
  `false` rides a complete result as an advisory. `Validation` is the semantic pass result and never
291
333
  throws.
292
334
 
@@ -344,8 +386,11 @@ instead of through the manifest.
344
386
 
345
387
  `ManifestEntry` is one vendored-host file record — its un-dotted `storage` name, its `destination`
346
388
  relative to a target, and an `executable` bit. `HostManifest` pairs the sorted file `entries` with
347
- the complete sorted directory `roots` inventory, so a destructive consumer can tell a
348
- declared-empty root from a truncated manifest.
389
+ the complete sorted directory `roots` inventory and a SHA-256 `digest` of that exact membership.
390
+ The independently persisted digest detects an entry/root membership edit that did not update the
391
+ digest, while roots distinguish a declared-empty directory. A self-consistent replacement manifest
392
+ remains structurally valid and defines its own smaller membership; authenticity of that complete
393
+ membership is outside the digest's checksum-only contract.
349
394
 
350
395
  The write-transaction shapes are the fail-closed mutation vocabulary. `WriteExpectation` is one
351
396
  destination snapshot captured before mutation (`absent`, `file`, or `directory`, with device,
@@ -380,9 +425,12 @@ From [`constants.ts`](../../src/core/constants.ts).
380
425
  | `BIN_CONFIGS` | const |
381
426
  | `APP_MATRIX` | const |
382
427
  | `HOST_PATHS` | const |
428
+ | `ORCHESTRATION_PATH_PREFIXES` | const |
429
+ | `ORCHESTRATION_PATH_NAMES` | const |
383
430
  | `SERVICE_SCRIPT_PATH` | const |
384
431
  | `GLOBAL_SETUP_PATH` | const |
385
432
  | `SHOWCASE_CONFIG_PATH` | const |
433
+ | `CATALOG_AGENT_PATH` | const |
386
434
  | `NAME_PATTERN` | const |
387
435
  | `MAX_NAME_LENGTH` | const |
388
436
  | `MAX_DEPENDENCY_NAME_LENGTH` | const |
@@ -416,7 +464,9 @@ From [`constants.ts`](../../src/core/constants.ts).
416
464
  | `SCAFFOLD_RANGE` | const |
417
465
  | `BASE_DEV_DEPENDENCIES` | const |
418
466
  | `SOURCE_BROWSER_DEV_DEPENDENCIES` | const |
467
+ | `APP_DEV_DEPENDENCIES` | const |
419
468
  | `APP_BROWSER_DEV_DEPENDENCIES` | const |
469
+ | `APP_SERVER_DEV_DEPENDENCIES` | const |
420
470
  | `CHECKOUT_ACTION_SHA` | const |
421
471
  | `SETUP_NODE_ACTION_SHA` | const |
422
472
  | `COMPILER_ID` | const |
@@ -432,10 +482,38 @@ produces one (`app/browser/index.html`, `app/server/main.ts`). `BIN_CONFIGS` is
432
482
  axis's computed `tsconfig` and Vite wrapper pair. `HOST_PATHS` is the ordered list of byte-copied
433
483
  host artifacts, and it is the staging manifest rather than the per-plan carried set:
434
484
  `stageHost` vendors every path on it, while each plan carries the subset `selectHostPaths` selects
435
- for that one workspace. `SERVICE_SCRIPT_PATH` names the consumer-owned provisioner a service
436
- workspace's audit expects, and `GLOBAL_SETUP_PATH` names the consumer-owned Vitest global-setup
485
+ for that one workspace. `ORCHESTRATION_PATH_PREFIXES` and `ORCHESTRATION_PATH_NAMES` are the one
486
+ membership rule behind both group classifiers: `inferGroup` reads them for a foreign target path and
487
+ `hostGroup` for a `HOST_PATHS` entry, so a new harness directory is admitted once rather than twice.
488
+ `SERVICE_SCRIPT_PATH` names the generated provisioner skeleton a service
489
+ workspace must replace with its idempotent vendor provisioning. It is birth-only while present and
490
+ repairable while absent. `GLOBAL_SETUP_PATH` names the consumer-owned Vitest global-setup
437
491
  module that independently selected projects can load. `SHOWCASE_CONFIG_PATH` names the sole
438
492
  consumer-owned regular file whose exact physical presence enables the optional app showcase.
493
+ `CATALOG_AGENT_PATH` names the one artifact `diffPlan` compares by presence even after hydration,
494
+ so a consumer can name the file the catalog operation owns rather than rediscovering it from a
495
+ finding:
496
+
497
+ ```ts
498
+ import type { Plan } from '@orkestrel/scaffold'
499
+ import { blueprint, CATALOG_AGENT_PATH, contentToHex, diffPlan } from '@orkestrel/scaffold'
500
+
501
+ const plan: Plan = {
502
+ blueprint: blueprint('router', { src: ['core'] }),
503
+ groups: ['orchestration'],
504
+ artifacts: [
505
+ {
506
+ path: CATALOG_AGENT_PATH,
507
+ group: 'orchestration',
508
+ origin: 'host',
509
+ hex: contentToHex('vendored catalog\n'),
510
+ },
511
+ ],
512
+ }
513
+
514
+ diffPlan(plan, { [CATALOG_AGENT_PATH]: contentToHex('a newer fleet table\n') }).clean // true
515
+ diffPlan(plan, {}).missing // 1 — restorable while absent, never replaced while present
516
+ ```
439
517
 
440
518
  The bounds are public because they are part of the contract, not implementation trivia.
441
519
  `MAX_ARTIFACT_BYTES` caps one artifact at 5 MiB and `MAX_TOTAL_ARTIFACT_BYTES` caps one blueprint,
@@ -464,8 +542,11 @@ form. `HEX_PATTERN` requires whole lowercase byte pairs, and `SYNC_BASELINE_PATT
464
542
  `MINIMUM_NODE_VERSION` is `22.12.0`, `DEFAULT_ENGINES` derives from it, and `DEFAULT_VERSION` is
465
543
  `0.0.1`. `BASE_DEV_DEPENDENCIES` is the host-neutral tooling baseline every generated workspace
466
544
  gets; `SOURCE_BROWSER_DEV_DEPENDENCIES` adds the real browser providers a published browser environment
467
- needs, and `APP_BROWSER_DEV_DEPENDENCIES` extends that with the Vue toolchain a private browser
468
- application needs. Vite is minor-pinned at `~8.2.0`: the generated boundary consumes the reviewed
545
+ needs; `APP_DEV_DEPENDENCIES` is the baseline every private application environment gets;
546
+ `APP_BROWSER_DEV_DEPENDENCIES` adds the Vue toolchain and `@orkestrel/html` start-tag parser a
547
+ private browser application needs;
548
+ and `APP_SERVER_DEV_DEPENDENCIES` adds the emitter, middleware, router, and server packages a private
549
+ server application needs. Vite is minor-pinned at `~8.2.0`: the generated boundary consumes the reviewed
469
550
  8.2 `CSSOptions`, `preprocessCSS`, and `isCSSRequest` surface, while the selected
470
551
  `css.transformer` / `lightningcss` path is experimental and must not float into an unreviewed minor.
471
552
  `SCAFFOLD_RANGE` is the range generated workspaces pin this package at.
@@ -513,6 +594,14 @@ From [`constants.ts`](../../src/server/constants.ts).
513
594
  `PRUNE_DIRECTORIES` is the closed set of prune-owned directories — `.claude/agents`,
514
595
  `.codex/agents`, and `scripts`. Nothing outside those roots is ever a deletion candidate, which is
515
596
  why project-owned skills under `.agents/skills` and `.claude/skills` are structurally safe.
597
+ `.cursor/rules` is vendored but deliberately not pruned, for the same reason: a workspace owns
598
+ project-specific Cursor rules beside the vendored bridge, and pruning would delete them. That
599
+ choice has a cost, and it is accepted rather than avoided: a rule file dropped from `HOST_PATHS`
600
+ stays in every consumer that already received it, no `audit` run reports it — the executable audit
601
+ reads only planned paths, and nothing outside `PRUNE_DIRECTORIES` is ever a `foreign` finding — and
602
+ a Cursor rule carrying `alwaysApply: true` keeps instructing agents there indefinitely. Retiring a
603
+ vendored rule therefore needs a deliberate consumer-side removal, not a scaffold run.
604
+ `.claude/rules` and `.claude/skills` carry the identical exposure for the identical reason.
516
605
  `HOST_MANIFEST_PATH` is the reserved `manifest.json` written at the root of every staged host.
517
606
  `SENSITIVE_HOST_PATH_PATTERN` rejects credential-like, key-store, certificate-key, and
518
607
  local-configuration paths at the staging boundary. `RESERVED_TARGET_PATH_PATTERN` protects `.git`
@@ -788,6 +877,8 @@ From [`helpers.ts`](../../src/core/helpers.ts).
788
877
  | `pascalCase` | function |
789
878
  | `escapeHtmlText` | function |
790
879
  | `serializeTypeScriptString` | function |
880
+ | `hasApplicationBoundary` | function |
881
+ | `hasApplicationShowcase` | function |
791
882
  | `blueprintToMembers` | function |
792
883
  | `catalogNames` | function |
793
884
  | `alignTable` | function |
@@ -801,6 +892,7 @@ From [`helpers.ts`](../../src/core/helpers.ts).
801
892
  | `syncToReview` | function |
802
893
  | `catalogToBlock` | function |
803
894
  | `inferGroup` | function |
895
+ | `matchesOrchestrationPath` | function |
804
896
  | `diffPlan` | function |
805
897
  | `bytesToHex` | function |
806
898
  | `contentCodePoint` | function |
@@ -824,6 +916,7 @@ From [`helpers.ts`](../../src/core/helpers.ts).
824
916
  | `renderArray` | function |
825
917
  | `renderObject` | function |
826
918
  | `renderValue` | function |
919
+ | `renderStringArray` | function |
827
920
  | `formatJson` | function |
828
921
  | `pinPlan` | function |
829
922
 
@@ -836,7 +929,9 @@ guard. `blueprint` fills the defaults: `version` and `engines` from their consta
836
929
  derives the entity name from a lowercase-hyphen package name, and `blueprintToMembers` derives the
837
930
  declared public `Member[]` — a full entity, options type, interface, and factory per published
838
931
  environment, plus the exact declaration inventory each selected application environment
839
- contributes.
932
+ contributes. `hasApplicationBoundary` recognizes exactly app/core + app/browser + app/server,
933
+ while `hasApplicationShowcase` requires showcase intent beside app/browser; plan assembly, tests,
934
+ guides, and member inventory share those predicates.
840
935
 
841
936
  `escapeHtmlText` and `serializeTypeScriptString` are the two escaping leaves used when a
842
937
  caller-supplied name reaches generated HTML or generated TypeScript source; the latter preserves
@@ -859,6 +954,14 @@ that block enters agent instruction context. `isBehind` is the shared freshness
859
954
  report projections count with.
860
955
 
861
956
  `diffPlan` is the audit engine, and `inferGroup` classifies a target file the plan does not own.
957
+ `matchesOrchestrationPath` is the shared membership test both classifiers use to decide whether a
958
+ path instructs or wires an agent rather than configuring the toolchain.
959
+ A host artifact without canonical `hex` is presence-owned: present is `aligned`, absent is
960
+ `missing`. The server face attaches `hex` to every readable vendored source before executable
961
+ audits, except the dependency-guide pointers hydration deliberately marks presence-owned. The
962
+ hydrated `CATALOG_AGENT_PATH` artifact remains presence-owned even with vendored bytes because
963
+ `catalog` owns its bounded marker region. The same engine governs `Materializer.repair`'s preview
964
+ recheck and direct library consumers without a call-site plan rewrite.
862
965
  `snapshotOf`, `contentToHex`, `contentToBytes`, `contentByteLength`, `contentCodePoint`, and
863
966
  `bytesToHex` are the host-independent byte leaves that make exact comparison possible without a
864
967
  host encoder or buffer; an unpaired surrogate encodes as `U+FFFD` rather than throwing.
@@ -888,7 +991,12 @@ id is already registered: an identical plan is idempotent, while a distinct payl
888
991
  `formatJson` and its leaves — `renderValue`,
889
992
  `renderArray`, `renderObject`, `computeColumnWidth`, and `fitsPrintWidth` — emit JSON that matches the fleet
890
993
  formatter byte for byte, collapsing a short array onto one line and breaking a long one, so
891
- computed configuration JSON is format-stable by construction.
994
+ computed configuration JSON is format-stable by construction. `renderStringArray` applies the same
995
+ inline-or-broken width rule to single-quoted TypeScript string-array literals — with a trailing
996
+ comma on every broken line, matching `oxfmt`'s `trailingComma: "all"` for non-JSON files — so
997
+ generated TypeScript configuration is format-stable too. It serializes every string element through
998
+ `serializeTypeScriptString`, so quotes, backslashes, controls, and line separators remain inert in
999
+ both layouts.
892
1000
 
893
1001
  ### Helpers — server
894
1002
 
@@ -901,6 +1009,7 @@ From [`helpers.ts`](../../src/server/helpers.ts).
901
1009
  | `digestFile` | function |
902
1010
  | `digestHex` | function |
903
1011
  | `digestText` | function |
1012
+ | `digestHostManifest` | function |
904
1013
  | `guideStub` | function |
905
1014
  | `packageShortName` | function |
906
1015
  | `readGuideReferences` | function |
@@ -953,7 +1062,9 @@ directory. All three reject malformed paths before filesystem access. Containmen
953
1062
  realpath-aware rather than merely lexical, so a symlinked subdirectory planted inside an otherwise
954
1063
  legitimate root cannot smuggle a write or a read outside it.
955
1064
 
956
- `digestFile`, `digestHex`, and `digestText` are the three SHA-256 leaves. The file digest is
1065
+ `digestFile`, `digestHex`, and `digestText` are the byte and text SHA-256 leaves;
1066
+ `digestHostManifest` hashes the canonical entry/root membership independently of the stored digest
1067
+ field. The file digest is
957
1068
  bounded-memory and revalidates device, inode, size, and modification time before and after reading,
958
1069
  so a file swapped mid-read is a failure rather than a silent wrong digest. `readFileHex` and
959
1070
  `readFileText` read one contained file under the same revalidation, and the text reader decodes
@@ -985,8 +1096,9 @@ existing workspace so a mature package is diffed against its own would-be scaffo
985
1096
  dependency-less stand-in. Environments come from `src/<environment>/` and `app/<environment>/`, the
986
1097
  three directory-shaped structural project facts from their directory probes, `global` from the
987
1098
  physical exact-case `tests/setupGlobal.ts` file, and `showcase` from the physical exact-case regular
988
- file `configs/app/vite.showcase.config.ts`; the service companion law remains the one the blueprint
989
- section states every fact is a reading of the filesystem, never of the name. Dependencies and peers come
1099
+ file `configs/app/vite.showcase.config.ts`; service names come from the direct vendor directories
1100
+ under `tests/service/`, subject to the companion law in the blueprint section. Every fact is a
1101
+ reading of the filesystem, never of the package name. Dependencies and peers come
990
1102
  from the manifest's scoped entries, with an optional peer recovered from
991
1103
  `peerDependenciesMeta`; and `extras` is every development dependency minus the complete set
992
1104
  `devDependenciesFor` emits for those environments and structural axes, and minus anything already
@@ -1000,7 +1112,8 @@ repository forking the file.
1000
1112
  `hydratePlan` are the vendored-host path. `storagePath` maps a repo-relative path to its un-dotted
1001
1113
  storage name, `stageHost` copies the vendored set into an output directory behind a full preflight
1002
1114
  and an atomic swap, and `readHostManifest` reads and validates the resulting `manifest.json`,
1003
- returning `undefined` when a host has none — the raw-repository-root fallback that maps sources 1:1.
1115
+ including its independently stored membership digest, returning `undefined` when a host has none —
1116
+ the raw-repository-root fallback that maps sources 1:1.
1004
1117
  `locateHostSource` resolves one source to its storage file, `remapArtifactPath` maps a manifest
1005
1118
  destination back onto an artifact's target prefix, and `hydratePlan` rehydrates a plan's host
1006
1119
  artifacts with their exact bytes, expanding a directory-shaped host artifact into one artifact per
@@ -1049,6 +1162,7 @@ From [`compilers.ts`](../../src/core/compilers.ts).
1049
1162
  | `renderViteTest` | function |
1050
1163
  | `viteHeader` | function |
1051
1164
  | `policyViteProject` | function |
1165
+ | `configViteProject` | function |
1052
1166
  | `guidesViteProject` | function |
1053
1167
  | `binViteProject` | function |
1054
1168
  | `integrationViteProject` | function |
@@ -1136,10 +1250,11 @@ formatter's 100-column fixed point: a complete registration-array line, includin
1136
1250
  its trailing comma, stays collapsed when it fits and expands one entry per line otherwise.
1137
1251
  `viteProjectRegistrations` is the one registration derivation every root shape consumes: it derives
1138
1252
  the selected source and application projects from the canonical environment order, then appends
1139
- `policy`, `guides`, and the optional `srcBin`, `integration`, and `service` projects.
1253
+ `policy`, `config`, `guides`, the optional `srcBin` and `integration` projects, and one
1254
+ `service<Vendor>` project for every selected service.
1140
1255
  `viteProjectDefinitions` renders the standalone proof and structural-fact definitions in that same
1141
1256
  order with one blank line between declarations. Both consume `ViteFacts`, so each optional project
1142
- is controlled only by its matching `bin`, `integration`, or `service` blueprint fact; the same
1257
+ is controlled only by its matching `bin`, `integration`, or `services` blueprint fact; the same
1143
1258
  slice carries `global` to integration and the source-browser compiler, and `showcase` to the
1144
1259
  application-browser compiler, without adding another test project.
1145
1260
 
@@ -1149,15 +1264,17 @@ the root `srcCore` factory and its wrapper stay host-independent even when the w
1149
1264
  browser target. `binTsconfig` emits the executable declaration scope; `rootViteConfig`,
1150
1265
  `singleSrcViteConfig`, and `applicationViteConfig` emit the root configuration for a library-only,
1151
1266
  single non-core `src` environment, and application-bearing workspace respectively; and
1152
- `policyViteProject`, `guidesViteProject`, `integrationViteProject`, and `serviceViteProject` emit
1153
- the standalone Node proof projects, with `binViteProject` the single executable-project emitter. A
1267
+ `policyViteProject`, `configViteProject`, `guidesViteProject`, `integrationViteProject`, and
1268
+ `serviceViteProject` emit the standalone Node proof projects, with `binViteProject` the single
1269
+ executable-project emitter. A
1154
1270
  proof project is structurally derived from the directory holding its tests and never wraps a source
1155
1271
  or application environment project. The guides project therefore uses only `tests/setup.ts`, never
1156
- `setupServer.ts`, `setupBrowser.ts`, or `setupService.ts`; and its `tests/src/**/*.test.ts` and
1272
+ `setupServer.ts`, `setupBrowser.ts`, or a vendor readiness module; and its `tests/src/**/*.test.ts` and
1157
1273
  `tests/app/**/*.test.ts` exclude rows are uniform across all root shapes by design, including
1158
1274
  core-only workspaces where one row cannot currently match. Integration and service use 120-second
1159
- test and hook timeouts with file parallelism disabled, and service alone layers
1160
- `tests/setupService.ts` onto the shared setup. The integration project wires
1275
+ test and hook timeouts with file parallelism disabled. Each service project layers
1276
+ `tests/setupServer.ts` and `tests/service/<vendor>/setup.ts` onto the shared setup, carries the
1277
+ server environment boundary, and may exercise either the `src` or `app` axis. The integration project wires
1161
1278
  `tests/setupGlobal.ts` for the shared template-registry harness exactly when `bin`, `integration`,
1162
1279
  and `global` are all true. Independently, a `global` source-browser project places
1163
1280
  `globalSetup: ['./tests/setupGlobal.ts']` immediately before its ordinary `setupFiles` row (and
@@ -1174,7 +1291,9 @@ generated parity suite resolves fence imports through. `guideMemberTable`, `guid
1174
1291
  `guideMethods`, and `guideTests` render the generated guide's member tables, usage examples, method
1175
1292
  contract, and test inventory. `fillArtifact` fills one template entry into a `template`-origin
1176
1293
  artifact with missing placeholders treated as an error, and `hostGroup` resolves which group a
1177
- byte-copied host path belongs to. `applyOverrides` replaces a matching artifact's content in place
1294
+ byte-copied host path belongs to splitting by what a path governs rather than where it sits, so
1295
+ that both MCP registrations (`.mcp.json` and `.cursor/mcp.json`) group with the harness bridges as
1296
+ `orchestration` rather than with the root dotfiles they sit beside. `applyOverrides` replaces a matching artifact's content in place
1178
1297
  and deliberately leaves an unmatched, host-owned, or `package.json` override unapplied, because the
1179
1298
  gate reports it as a blocking question. `ciWorkflow` renders the generated workflow.
1180
1299
 
@@ -1270,7 +1389,10 @@ The public methods of each behavioral interface, one table per type.
1270
1389
  `audit(blueprint, current, groups?)` compiles and then diffs the resulting plan against the
1271
1390
  caller-supplied current content; a gated blueprint returns `complete: false` with the gate's
1272
1391
  blocking questions and zero findings, and a complete one carries the gate's advisories on that same
1273
- `questions` field. `destroy()` is idempotent teardown. The interface also exposes the readonly
1392
+ `questions` field. Because this core-only method performs no host I/O, its compiled host artifacts
1393
+ have no `hex` and are audited by presence. Callers that need host-byte verdicts hydrate the compiled
1394
+ plan through the server face and call `diffPlan`, which is the path every executable audit uses.
1395
+ `destroy()` is idempotent teardown. The interface also exposes the readonly
1274
1396
  `emitter`.
1275
1397
 
1276
1398
  #### `PlanManagerInterface`
@@ -1300,13 +1422,14 @@ interface also exposes the readonly `emitter` and `size` properties.
1300
1422
  | `destroy` | `void` |
1301
1423
 
1302
1424
  `materialize(plan, target)` is green-field: it refuses any target `isVacant` rejects, then copies
1303
- each host artifact and writes each template and computed artifact. `repair(plan, audit, target)` is
1304
- into-existing: it skips the vacancy check, re-verifies that the target still matches the audit
1305
- preview, and writes only the missing and stale artifacts that audit names, leaving aligned ones
1306
- untouched and reporting them as `skipped`. `prune(target, expected)` deletes exactly the unexpected
1307
- files the vendored host no longer declares under the prune directories, and only after the observed
1308
- bytes still match the `expected` snapshot it was previewed with. `destroy()` is idempotent teardown.
1309
- The interface also exposes the readonly `emitter`.
1425
+ each host artifact and writes each template and computed artifact. `repair(plan, audit, target,
1426
+ replace?)` is into-existing: it skips the vacancy check, re-verifies that the target still matches
1427
+ the audit preview, and writes missing artifacts. Stale artifacts are report-only and returned as
1428
+ `skipped` by default; passing `true` for `replace` explicitly replaces their bytes and discards their
1429
+ local changes. Aligned artifacts are always `skipped`. `prune(target, expected)` deletes exactly the
1430
+ unexpected files the vendored host no longer declares under the prune directories, and only after
1431
+ the observed bytes still match the `expected` snapshot it was previewed with. `destroy()` is
1432
+ idempotent teardown. The interface also exposes the readonly `emitter`.
1310
1433
 
1311
1434
  #### `SyncInterface`
1312
1435
 
@@ -1377,10 +1500,11 @@ produce the same digest — and a `PlanManager` id is that digest.
1377
1500
  how it is audited, and whether it may ever be overwritten.
1378
1501
 
1379
1502
  - **`host`** — byte-copied from the vendored data root. These are the shared files a whole fleet
1380
- keeps identical: the root instruction documents and licence, the agent, rule, and skill
1381
- directories, the session scripts, the repository coding-law policy module, the byte-identical root
1382
- dotfiles, and the two line guide mirrors a workspace carries for contracts other than its own.
1383
- `HOST_PATHS` is the exact vendored list; what a given plan carries is `selectHostPaths` of it.
1503
+ keeps identical: the root instruction documents and licence, the canonical orchestration contract
1504
+ and the three harness bridges that point at it, the agent, rule, and skill directories, the
1505
+ session scripts, the repository coding-law policy module, the byte-identical root dotfiles, and
1506
+ the two line guide mirrors a workspace carries for contracts other than its own. `HOST_PATHS` is
1507
+ the exact vendored list; what a given plan carries is `selectHostPaths` of it.
1384
1508
  - **`template`** — filled from a frozen template definition by a pure fill engine. These are
1385
1509
  starter files: source stubs, test stubs, the starter guide, the README.
1386
1510
  - **`computed`** — derived by this package's own combination logic. These are the structural files:
@@ -1396,19 +1520,32 @@ Audit semantics follow directly from that.
1396
1520
  missing, or clean tallies.
1397
1521
  - A **computed** artifact is content-aware canon: `missing`, `aligned`, or `stale`, and it gates the
1398
1522
  audit like any other drift.
1399
- - A **host** artifact is audited by presence alone `missing` or `aligned`, never `stale` — unless
1400
- it has been hydrated with its real host bytes, in which case it is content-compared exactly like a
1401
- computed artifact and can be `stale`. Hydration also expands a directory-shaped host artifact into
1402
- one artifact per file, so agent configuration and skills are audited file by file.
1403
- - A target file the plan does not own is `foreign`, and `inferGroup` classifies it by its leading
1404
- path segment.
1523
+ - A **host** artifact with canonical `hex` is content-compared exactly like a computed artifact and
1524
+ can be `stale`. Without `hex`, it is presence-owned: present is `aligned`, absent is `missing`.
1525
+ The catalog agent is explicitly presence-owned because `catalog` is its sole content writer.
1526
+ Hydration expands a directory-shaped host artifact into one artifact per declared file and verifies
1527
+ that the manifest digest matches the manifest's current membership before expansion. That detects
1528
+ stale-digest truncation; a self-consistently rewritten manifest defines a smaller valid inventory,
1529
+ so the digest alone cannot authenticate omitted membership.
1530
+ - In a caller-supplied snapshot, a path the plan does not own is `foreign`, and `inferGroup`
1531
+ classifies it by its leading path segment. The executable supplies unexpected paths only from
1532
+ `.claude/agents`, `.codex/agents`, and `scripts`, because those prune-owned directories are the
1533
+ only regions scaffold has authority to delete from; unplanned files elsewhere are not reported
1534
+ as foreign.
1405
1535
 
1406
1536
  The same ownership boundary is what makes mutation safe. **`fleet` and default `repair` both scope
1407
- the compiled plan to host origin before hydrating, diffing, or applying.** `--generated` widens that
1408
- scoped plan to generated canon except `package.json`; template artifacts remain birth-only in
1409
- either mode. A mature workspace's hand-written source, tests, guides, and manifest are therefore
1410
- never overwritten with a stub. The generated `.github/workflows/ci.yml` is a **computed** artifact,
1411
- so user-owned CI stands by default but is intentionally restored when `--generated` is passed.
1537
+ the compiled plan to host origin before hydrating, diffing, or applying.** Missing files in that
1538
+ scope are restored, but stale files are report-only unless `--replace` explicitly authorizes byte
1539
+ replacement. `--generated` widens the selected ownership scope to generated canon. It keeps the
1540
+ `package.json` publication boundary protected except for the generated service-script keys needed
1541
+ when the derived service set changes; it composes with `--replace` and does not itself authorize
1542
+ replacement. Template
1543
+ artifacts remain birth-only in either scope, except that an absent service provisioner and absent
1544
+ service conformance test are promoted to missing-file repair artifacts. A present customized copy
1545
+ is never compared or replaced. A mature workspace's hand-written source, tests, and guides are
1546
+ therefore never overwritten with a stub. The generated
1547
+ `.github/workflows/ci.yml` is a **computed** artifact, so user-owned CI stands by default and is
1548
+ restored only when both `--generated` and `--replace` are passed.
1412
1549
  Audit always compares it because computed artifacts are content-aware canon. A legitimate
1413
1550
  difference that the blueprint cannot express is a canon gap: add the missing blueprint axis rather
1414
1551
  than forking the computed file in one repository.
@@ -1430,8 +1567,11 @@ owner: a dependency this package vendors a byte-identical mirror for gets a real
1430
1567
  dependency, so a package depending on `@orkestrel/guide` plans one `guides/src/guide.md` rather than
1431
1568
  two. Any other dependency gets a host-origin _pointer_ artifact plus a non-blocking question, never
1432
1569
  a fabricated mirror; on materialization that pointer degrades to a short stub, and `scaffold pull`
1433
- fetches the real thing. That degrade is scoped exactly to guide pointers: any other missing manifest
1434
- entry means a corrupt or truncated vendored manifest, and fails closed. Selection is the law and
1570
+ fetches the real thing. Hydration marks that permanent pointer state presence-owned, so both the
1571
+ birth stub and a later pulled guide audit clean while present; `pull` refreshes content but is not a
1572
+ remedy for an audit state. That degrade is scoped exactly to guide pointers. A manifest whose
1573
+ membership changes without a matching digest is rejected, while any other undeclared or unreadable
1574
+ source is rejected with a coded `TARGET` failure. Selection is the law and
1435
1575
  `findFileConflict` is its backstop: two artifacts at one path refuse the plan rather than racing to
1436
1576
  be the last writer.
1437
1577
 
@@ -1442,23 +1582,63 @@ checks one. `readTarget` supplies the snapshot as exact bytes; `diffPlan` return
1442
1582
  `auditToReview` renders them for a human. Nothing in that path writes.
1443
1583
 
1444
1584
  The executable's physical unexpected-file scan treats exactly `scripts/service.sh` as an expected
1445
- consumer-owned seam when the derived blueprint has `service: true`. That exclusion is warranted
1446
- because a service blueprint cannot derive without the physical file: the companion-file law raises
1447
- a `TARGET` failure first, so the scan removes a false positive and can never mask an absent
1448
- provisioner. A non-service workspace still reports the same path as foreign.
1585
+ workspace-owned seam when the derived blueprint has at least one service. That exclusion is
1586
+ warranted because the promoted plan reports an absent file as missing while a present file is
1587
+ consumer-owned. A workspace with no services still reports the same path as foreign.
1449
1588
 
1450
1589
  `repair` turns those findings back into the narrowest possible write. It re-reads the target,
1451
1590
  re-diffs it, and refuses to proceed if the findings changed since the preview it was given — a
1452
1591
  target that moved under the caller is a `TARGET` failure, not a race to win. It then derives a write
1453
1592
  precondition per artifact from the audit itself: a `missing` finding requires the destination to
1454
- still be absent, a `stale` finding requires it to still carry exactly the bytes that were observed.
1455
- Those preconditions are checked again inside the write transaction before any promotion.
1456
- An interactive audit repair hand-off forwards `--generated` into the repair invocation when the
1457
- flag was present on `audit`.
1593
+ still be absent. A `stale` finding remains untouched and is reported as skipped unless the caller
1594
+ passes `replace`; an authorized stale replacement requires the destination to still carry exactly
1595
+ the bytes that were observed. Those preconditions are checked again inside the write transaction
1596
+ before any promotion. A skipped stale path keeps the executable at exit `1`, because the selected
1597
+ workspace remains drifted; a clean run and a run that fully applies its findings exit `0`. An
1598
+ interactive audit repair hand-off forwards both `--generated` and `--replace` when those flags were
1599
+ present on `audit`.
1600
+
1601
+ That boundary governs the executable's words too. Every drift line states what a command will do
1602
+ rather than how a file came to differ: the executable cannot know whether a generated file was
1603
+ hand-edited, and a consumer whose blueprint cannot yet express what it needs legitimately edits one.
1604
+ Every cost is stated where it can still be declined, and nowhere else: a run with nothing to write
1605
+ states no boundary it is not about to act on, because a warning attached to a no-op only trains
1606
+ operators to ignore warnings. `repair` states its scope when the audit found something to repair;
1607
+ `fleet` states its scope, its repository count, and the same replacement cost once `--apply` has
1608
+ authorized a write; neither states it over a dry run. Each run closes on the tally of what it did,
1609
+ including a run that writes nothing — and a drifted file left alone is counted apart from an
1610
+ aligned one, because `unchanged` is already the audit table's word for a file that matches canon.
1611
+
1612
+ **Four paths discard content a consumer may own, and each names its cost before it acts.**
1613
+
1614
+ | Path | What it discards | Ownership boundary |
1615
+ | --------------------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
1616
+ | `--replace` on repair or fleet | The local bytes of a drifted file the report named | Host-owned artifacts, widened to generated canon by `--generated`; never a present starter, never `package.json` |
1617
+ | `--prune --apply` | Whole unexpected files, quarantined first and reported by exact path | Only `.claude/agents`, `.codex/agents`, and `scripts`, and only paths the vendored host does not declare |
1618
+ | `catalog --apply` | Everything between the two catalog markers, local additions included | Exactly the one bounded marker region of `CATALOG_AGENT_PATH`; the rest of that file is never touched |
1619
+ | `pull --apply` / `mirror --apply` | A locally edited vendored guide mirror, which reads as `behind` | Only `guides/src/<name>.md` mirrors of other packages; never the guide this workspace owns |
1620
+
1621
+ `--replace` is the only one of the four that is an opt-in modifier rather than a verb, so it is the
1622
+ one whose cost is repeated in every line that offers it: the scope line, the repair verdict, the
1623
+ audit's drift guidance, and the hand-off question itself. `--apply` authorizes all four and nothing
1624
+ else does — `--yes` only skips a confirmation it can no longer stand in for, and an unexpected-file
1625
+ hint that recommended `repair --prune` without it would name a command that deletes nothing. `new`
1626
+ is absent from the table on purpose: it refuses any target `isVacant` rejects, so it has no local
1627
+ content to discard.
1628
+
1629
+ The catalog agent has a narrower ownership exception in `diffPlan` itself.
1630
+ `CATALOG_AGENT_PATH` remains presence-owned after host hydration: repair can restore the
1631
+ absent file, but audit, repair, fleet, and direct library consumers never compare or replace its
1632
+ existing bytes, even under `--replace`. `catalog` is the sole content writer and continues to
1633
+ replace only the uniquely bounded marker region. Thus
1634
+ `repair` → `catalog` → `repair` converges without restoring a stale embedded catalog snapshot over
1635
+ the current fleet table.
1458
1636
 
1459
1637
  `prune` is the deletion arm, and it is deliberately narrow. Its candidate set comes from
1460
- `pruneTargets`, which is also what the executable's audit and preview read, so what is reported and
1461
- what is deleted cannot diverge. Only the three prune directories are in scope; the allowlist must be
1638
+ `pruneTargets`, which is also what the executable's merged report and preview read. Repair's
1639
+ optimistic-concurrency recheck receives the raw plan audit, while the separate foreign findings
1640
+ remain attached to reporting and exit status; after that recheck succeeds, the same preview snapshot
1641
+ drives deletion. Only the three prune directories are in scope; the allowlist must be
1462
1642
  positively established from the vendored host, or the call fails closed rather than treating an
1463
1643
  unresolved host as "vendors nothing" and proposing to delete everything. Each candidate is verified
1464
1644
  as a plain physical file whose bytes still match the preview, moved into a private quarantine rather
@@ -1527,11 +1707,14 @@ fleet refresh is never partial. Files outside the discovered guide set remain un
1527
1707
 
1528
1708
  A generated workspace is not a folder of suggestions; it is a working, gated project.
1529
1709
 
1530
- **Manifest and scripts.** A published workspace is scoped and carries an `exports` map, publish
1531
- configuration, and ships `dist/src` plus its README. An application-only workspace is unscoped and
1532
- `private: true`, with no export map and no publish configuration, and ships `dist/app`. A workspace
1533
- that builds its own executable additionally ships `dist/bin` and `dist/host`. Scripts are emitted in
1534
- a fixed, interleaved order so aggregates sit immediately before their per-environment members:
1710
+ **Manifest and scripts.** A published workspace is scoped, carries the Orkestrel GitHub homepage,
1711
+ issues, and repository identity, carries an `exports` map and publish configuration, and ships
1712
+ `dist/src` plus its README. An application-only workspace is unscoped and `private: true`, with no
1713
+ export map, publish configuration, or invented GitHub identity, and ships `dist/app`. Both carry
1714
+ `license: "MIT"` because every generated workspace receives the same host-owned MIT `LICENSE`.
1715
+ A workspace that builds its own executable additionally ships `dist/bin` and `dist/host`. Scripts
1716
+ are emitted in a fixed, interleaved order so aggregates sit immediately before their per-environment
1717
+ members:
1535
1718
 
1536
1719
  - `clean`, `copy`, `scaffold`, `lint`
1537
1720
  - `check`, then `check:src` with one `check:src:<environment>` per published environment, then
@@ -1539,32 +1722,34 @@ a fixed, interleaved order so aggregates sit immediately before their per-enviro
1539
1722
  Vue typechecker, every other scope uses plain `tsc`
1540
1723
  - `format`, `format:check`, `lint:check`
1541
1724
  - `test`, then `test:src` and its per-environment scopes, the optional `test:integration`,
1542
- `test:equivalence`, and `test:service` proofs, `test:app` and its per-environment scopes, then
1543
- `test:policy` and `test:guides`
1725
+ `test:equivalence`, and `test:service` aggregate followed by its sorted per-vendor proofs,
1726
+ `test:app` and its per-environment scopes, then
1727
+ `test:policy`, `test:config`, and `test:guides`
1544
1728
  - `build`, then `build:src` and its per-environment targets, `build:app` and its runtime targets, and
1545
1729
  `build:host` for a bin workspace
1546
1730
  - `dev` when a browser application is selected; `serve` and `serve:build` when a server application
1547
1731
  is selected
1548
1732
  - `showcase`, `build:showcase`, and `show` only when the physical showcase wrapper is present;
1549
- `show` builds and copies `dist/showcase/index.html` to `demo/showcase.html`
1733
+ `show` formats, then builds, then copies `dist/showcase/index.html` to `demo/showcase.html`
1550
1734
  - `prepublishOnly` chaining `format:check → lint:check → check → build → test`, followed by
1551
- `test:integration` when the integration axis is selected
1735
+ `test:integration` when selected and finally `test:service` when any service is selected
1552
1736
 
1553
1737
  **Proof gating.** The opt-in proofs are predictable from the axes alone. `test:integration` rides
1554
- the `integration` axis and `test:service` the `service` axis, while `test:equivalence` is emitted
1738
+ the `integration` axis and `test:service` a nonempty `services` axis, while `test:equivalence` is emitted
1555
1739
  only where `bin` and `integration` are both set:
1556
1740
 
1557
- | Proof | `npm test` | `prepublishOnly` | CI |
1558
- | ------------------ | ---------- | ---------------- | -------------------------- |
1559
- | `test:integration` | no | yes, last | after the standard gates |
1560
- | `test:equivalence` | no | no | no |
1561
- | `test:service` | no | never | after `scripts/service.sh` |
1562
-
1563
- No proof joins the default chain: `npm test` runs the source, application, policy, and guide
1564
- projects, and nothing there needs a build artifact or a foreign process. Publication is the one
1565
- asymmetry `prepublishOnly` appends `test:integration`, because a package about to be published
1566
- should prove itself against its own built output, while `test:service` is never in that chain.
1567
- Neither default testing nor publication starts or requires a foreign process.
1741
+ | Proof | `npm test` | `prepublishOnly` | CI |
1742
+ | ------------------ | ---------- | ------------------- | -------------------------- |
1743
+ | `test:integration` | no | yes, before service | after the standard gates |
1744
+ | `test:equivalence` | no | no | no |
1745
+ | `test:service` | no | yes, last | after `scripts/service.sh` |
1746
+
1747
+ No opt-in proof joins the default chain: `npm test` runs the source, application, policy,
1748
+ configuration, and guide projects, and nothing there needs a build artifact or a foreign process.
1749
+ Publication is the one asymmetry: `prepublishOnly` appends integration and then service proofs.
1750
+ A package that claims to drive a vendor has not proved that claim unless publishing runs against
1751
+ it, despite the provisioning cost. Neither default testing nor publication starts a foreign
1752
+ process; publication requires the caller to provision one first.
1568
1753
  The showcase is likewise outside `build`, `test`, and `prepublishOnly`; it is an explicit projection
1569
1754
  of `app/browser`, not an environment, test-project row, or source/demo artifact.
1570
1755
  Its copied `demo/showcase.html` is generated and minified, so the mirrored `.prettierignore` keeps it
@@ -1582,22 +1767,29 @@ The equivalence proof is a dual-path re-run rather than a separate suite. Run
1582
1767
  integration project in dual-path mode and proves each programmatic driver verdict against the
1583
1768
  spawned npm-script reference. Ordinary integration runs keep the faster driver-only path.
1584
1769
 
1585
- **Consumer-owned service seams.** The service axis is the one place canon stops at the boundary:
1586
- there is no template for a proof project and neither companion path is on `HOST_PATHS`, so a
1587
- service workspace owns both of its seams outright. They come as a pair.
1588
-
1589
- - `tests/setupService.ts` is the readiness seam. It probes the foreign process and warms it before
1590
- any test runs, and throws at module load — naming the `service` project when that process is
1591
- unreachable, so an unprovisioned run fails loudly instead of passing an empty suite. Only the
1592
- `service` project loads it.
1593
- - `scripts/service.sh` is the provisioning seam, named once by `SERVICE_SCRIPT_PATH`. It brings that
1594
- process up idempotently a second run against an already-provisioned service is a no-op rather
1595
- than a second instance and exits nonzero when it cannot, which is what makes CI's
1596
- `bash scripts/service.sh` step a gate rather than a hint.
1770
+ **Consumer-owned service seams.** Each vendor owns its readiness module at
1771
+ `tests/service/<vendor>/setup.ts`. It probes and warms that vendor at module load, throwing a clear
1772
+ error when unavailable so only that vendor project fails readiness. The scaffold never generates
1773
+ these modules because an inert readiness check would be a false proof.
1774
+
1775
+ `scripts/service.sh`, named once by `SERVICE_SCRIPT_PATH`, is shared provisioning for every vendor.
1776
+ The scaffold emits a template skeleton that exits nonzero until the workspace replaces it with
1777
+ idempotent provisioning; an already-provisioned vendor must be a no-op, and any vendor that cannot
1778
+ be prepared must make the script fail. The skeleton is written at birth when services are already
1779
+ declared, or by repair when a post-birth vendor declaration makes it newly absent. Once present it
1780
+ is consumer-owned and never replaced. CI invokes it once before the aggregate project run.
1781
+
1782
+ The configuration conformance test lives in the ordinary `config` project, so `npm test` checks the
1783
+ directory names, readiness files, project declarations, scripts, default-test omission, and
1784
+ publication suffix without contacting a vendor. Like the provisioner it is repaired only when
1785
+ absent, then remains workspace-owned and audit-exempt. Service adoption under `--generated`
1786
+ regenerates the Vite and CI canon and merges only `test:service`, the per-vendor service scripts,
1787
+ and the `prepublishOnly` service suffix into `package.json`; publication metadata and unrelated
1788
+ scripts retain their existing values.
1597
1789
 
1598
1790
  The audit expects the script rather than reporting it foreign, on the derive-time warrant the audit
1599
1791
  section gives. Repair pruning applies the same exclusion, so it never proposes or removes that
1600
- required consumer-owned provisioner.
1792
+ required workspace-owned provisioner.
1601
1793
 
1602
1794
  **Environment isolation.** Scoped TypeScript projects remove the wrong host's globals from each
1603
1795
  environment: core scopes carry the WHATWG web-interop surface and no host at all — no DOM, no Node,
@@ -1633,10 +1825,16 @@ comments, text, raw blocks, attributes, adjacent tokens, casing, and user-author
1633
1825
  byte-stable. The trusted preparation hook owns the final pre-parse phase; inline proxy code is
1634
1826
  restored before module analysis, and the first normal post-parse hook restores the original HTML
1635
1827
  spelling. The browser entry begins with a generated, byte-stable security prologue: the doctype,
1636
- document and head opening, and a `Content-Security-Policy` meta element are one required prefix.
1637
- Preparation rejects a missing, moved, or changed prologue before Vite parses the document, and the
1638
- final trusted post-hook verifies that Vite retained the policy. CRLF and LF files are both accepted;
1639
- the prologue's markup and ordering are otherwise exact. Vite's `%ENV%` HTML substitution is rejected
1828
+ head opening, and `Content-Security-Policy` meta markup, ordering, and indentation are exact. The
1829
+ opening `html` start tag is parsed by `@orkestrel/html`'s fail-closed `parseStartTag` boundary,
1830
+ so ASCII case and well-formed attributes such as `lang`, `data-bs-theme`, and `data-bs-core`
1831
+ may vary without weakening the position of the following head and policy. A malformed, incomplete,
1832
+ duplicate-attribute, wrong-name, or syntactically slashed root still fails closed. Preparation owns
1833
+ that positional check while the document is still generated bytes; the final trusted post-hook
1834
+ checks only that the exact
1835
+ policy survived because Vite may legitimately inject into the head. CRLF and LF files are both
1836
+ accepted. Vite's
1837
+ `%ENV%` HTML substitution is rejected
1640
1838
  before parsing because Vite performs that expansion after every plugin pre-hook, where it could
1641
1839
  otherwise create a late control attribute. The guard walks the exact left-to-right `%(\S+?)%`
1642
1840
  tokens Vite recognizes instead of performing a substring search, and each preparation plugin owns
@@ -1685,15 +1883,31 @@ still rejects public directories, browser asset inlining, and output path overri
1685
1883
  post-factory composition as defense in depth; that narrow check is not a general extension seam.
1686
1884
 
1687
1885
  When the showcase fact is present, the generated root also exports closed
1688
- `appShowcase(...config: never[])`; both factories reject every argument at runtime and
1689
- share one fixed internal browser configuration. The ordinary factory retains its strict
1886
+ `appShowcase(...config: never[])`; both factories reject every argument at runtime. The
1887
+ ordinary factory retains its strict
1690
1888
  `script-src 'self'` policy, external asset auditing, and `dist/app/browser` output. The showcase
1691
- factory alone writes `dist/showcase`, applies `viteSingleFile` with
1889
+ factory is a standalone configuration with `base: './'`, unlimited asset inlining, and
1890
+ `dist/showcase` output. It applies `viteSingleFile` with
1692
1891
  `removeViteModuleLoader: true` and `useRecommendedBuildConfig: true`, uses Oxc and Lightning CSS
1693
- minification for an `esnext` build without source maps or module preload, and inserts a non-empty ISO
1694
- `build-id` meta. Its generated CSP admits only the inline script and style required by the
1695
- self-contained `file://` artifact while retaining `script-src-attr 'none'`, `object-src 'none'`, and
1696
- `base-uri 'none'`.
1892
+ minification for an `esnext` build without source maps or module preload, and inserts a SHA-256
1893
+ `build-id` derived from the secured, fully inlined document. An unchanged document therefore keeps
1894
+ the same id, while any changed byte changes it. The showcase development CSP keeps scripts
1895
+ same-origin and permits Vue's injected inline styles. Its built CSP swaps that script permission to
1896
+ inline and admits only inline styles plus data images and fonts, while both policies retain
1897
+ `default-src 'none'`, `script-src-attr 'none'`, `object-src 'none'`, and `base-uri 'none'`.
1898
+
1899
+ The showcase fact also emits its own entry pair, `app/browser/showcase.html` and
1900
+ `app/browser/showcase.ts`, beside the application's `index.html` and `main.ts`. Both HTML entries
1901
+ open with a generated security prologue: the application carries the ordinary strict policy and the
1902
+ showcase carries its development policy. The boundary plugins select and validate the matching
1903
+ prologue; the showcase build alone swaps in the self-contained policy before hashing and renames its
1904
+ single HTML output to `index.html`, which is what `show` copies to `demo/showcase.html`. The showcase entry
1905
+ mounts `mountShowcaseApplication`, and `app/browser/seeders.ts` exports exactly one seeder,
1906
+ `seedApplication`, returning a frozen identity of the same shape the shipped root view receives.
1907
+ The two mount factories differ in the seed expression alone. Both explicitly pass
1908
+ `{ name: seed.name }` to the same `createBrowserApplication` root: the showcase seed comes from
1909
+ `seedApplication()`, while the shipped application seed comes from `readApplicationHealth` with
1910
+ the configured identity as its fallback.
1697
1911
 
1698
1912
  The browser development server applies the same trust boundary before Vite's internal middleware.
1699
1913
  Its explicit filesystem allowlist contains only browser/core source roots, browser tests, their
@@ -1756,12 +1970,26 @@ it is not a general-purpose source analyzer. Generated workspaces receive the sa
1756
1970
  module as a host-origin file and run it as a dedicated Node-only `policy` test project over
1757
1971
  `tests/policy.test.ts`.
1758
1972
 
1973
+ **The configuration suite.** Policy reads source, the `config` project exercises the root
1974
+ configuration, and integration builds for real. Every generated workspace therefore receives a
1975
+ universal Node-only
1976
+ `config` project over `tests/config/**/*.test.ts`. Its base cases execute the root module's physical
1977
+ workspace containment and environment-direction helpers; conditional cases exercise output
1978
+ containment when the workspace builds, managed/system browser discovery when a browser environment
1979
+ exists, and the HTML/CSP boundary only for an application browser. Those cases import the generated
1980
+ root `vite.config.ts` itself, so a failure is repaired in the generator rather than patched into a
1981
+ consumer. The generated-consumer integration matrix remains the fidelity boundary for real builds;
1982
+ the configuration suite supplies deterministic edge coverage without duplicating build orchestration.
1983
+ When scaffold changes a generated configuration invariant, an existing consumer's `vite.config.ts`
1984
+ is intentionally reported stale until that consumer accepts the regenerated configuration and its
1985
+ matching config test.
1986
+
1759
1987
  **Real browser capability.** Browser test projects are gated on one centralized discovery chain:
1760
1988
  Playwright's pinned Chromium executable first, then a managed Chromium alias or cached revision,
1761
1989
  then stable system Chrome, then stable system Edge. Managed candidates must be executable regular
1762
1990
  files. System channels are selected only when their executable exists at Playwright's standard
1763
1991
  Linux, macOS, or Windows installation location; custom installations are not guessed. The generated
1764
- policy test consumes the same discovery helpers and accepts either an executable managed path or the
1992
+ configuration test consumes the same discovery helpers and accepts either an executable managed path or the
1765
1993
  stable `chrome` / `msedge` channel, so it does not maintain a second heuristic.
1766
1994
 
1767
1995
  A browser suite runs when any one of those real browser capabilities is available and is skipped
@@ -1797,8 +2025,13 @@ the proof-gating table gives them.
1797
2025
  dependency, model, and external-tool readiness scripts at session start. The **`Stop` hook runs only
1798
2026
  `git diff --check`** — a whitespace and conflict-marker check over the working tree, nothing more.
1799
2027
  Bash invocation and sensitive reads are controlled by the **settings permission list, not by a guard
1800
- script**: every Bash command requires explicit approval, including commands Claude Code otherwise
1801
- classifies as read-only. Read-only reviewer, checker, and ecosystem roles carry no Bash tool; the
2028
+ script**. The allow list is closed and holds exactly two entries — `Bash(codex --version)` and
2029
+ `Bash(codex login *)` because the orchestration contract requires a bench-liveness probe and a
2030
+ device-login recovery at session start, and prompting for those would stall every session before
2031
+ planning. Every other Bash command requires explicit approval, including commands Claude Code
2032
+ otherwise classifies as read-only. That list is inherited by every workspace in the line, so a
2033
+ machine-local grant belongs in `settings.local.json`, which `SENSITIVE_HOST_PATH_PATTERN` keeps out
2034
+ of every vendored host. Read-only reviewer, checker, and ecosystem roles carry no Bash tool; the
1802
2035
  orchestrator supplies their diff and status evidence. Bridge, writer, and verifier roles request
1803
2036
  approval when their bounded shell work is needed. Read patterns covering environment files,
1804
2037
  package-manager credentials, credential stores, private keys, key stores, SSH, cloud credentials,
@@ -1821,7 +2054,7 @@ no module API of its own. Seven verbs:
1821
2054
  | `pull` | refresh vendored guides and versions, report drift |
1822
2055
  | `mirror` | refresh every published Orkestrel package guide |
1823
2056
  | `audit` | whole-plan conformance report |
1824
- | `repair` | restore host-owned files and optional generated canon |
2057
+ | `repair` | restore missing canon; optionally replace drifted bytes |
1825
2058
  | `fleet` | audit or repair every workspace under the cwd's children |
1826
2059
  | `catalog` | regenerate the fleet package-catalog table |
1827
2060
 
@@ -1835,8 +2068,10 @@ Orkestrel short name. Other npm packages are not a creation-time flag — add th
1835
2068
  manifest's development dependencies afterwards, and they round-trip through `deriveBlueprint`'s
1836
2069
  extras so the workspace stays audit-clean.
1837
2070
 
1838
- **Other flags.** `--target <path>` selects the directory a verb operates on. `--from <path>` is
1839
- repeatable and points at a local template or catalog source instead of the bundled one.
2071
+ **Other flags.** `--target <path>` selects the directory a single-workspace verb operates on;
2072
+ `fleet --target` is a usage error because fleet's root is always the current directory.
2073
+ `--from <path>` points at a local template source instead of the bundled one and may be passed once
2074
+ to those verbs. It is repeatable only for `catalog`, where each occurrence adds one catalog source.
1840
2075
  On `pull`, `--deps x,y` limits refresh to those declared Orkestrel dependencies; without it, every
1841
2076
  declared dependency mirror is considered.
1842
2077
  `mirror` accepts no dependency selection: its exact npm organization discovery is the operation's
@@ -1844,25 +2079,34 @@ scope, and it fetches guides without registry version or packument requests.
1844
2079
  `--groups a,b` scopes an audit to artifact groups. `--live` adds an upstream freshness check to an
1845
2080
  audit. `--strict` makes a pull or mirror throw on a network fault. `--offline` restricts a catalog to local
1846
2081
  sources. `--prune` opts a repair or fleet run into deleting unexpected files under the three prune
1847
- directories. `--generated` opts a repair or fleet run into restoring generated canon except
1848
- `package.json`; on `audit`, it is inherited if the interactive repair hand-off is accepted.
2082
+ directories. `--generated` opts a repair or fleet run into including generated canon while
2083
+ protecting `package.json` outside its generated service-script keys; on `audit`, it is inherited if
2084
+ the interactive repair hand-off is accepted.
2085
+ `--replace` authorizes repair to discard local changes in the drifted files named by its report; it
2086
+ composes with `--generated`, and is likewise inherited by an accepted audit hand-off.
1849
2087
  `--json` emits one machine-readable value. `--apply` writes, `--yes` skips the confirmation, and
1850
2088
  `-h` or `--help` prints usage.
1851
2089
 
1852
- **Safety model.** Every verb is a dry run by default. On a terminal a write asks for confirmation
1853
- first, defaulting to no; in a script, `--apply` writes and `--yes` skips the question. Every write is
2090
+ **Safety model.** Every verb is a dry run by default. `--apply` is the sole write authorization;
2091
+ `--yes` only skips a confirmation and never authorizes a write or deletion by itself. On a terminal
2092
+ an authorized write asks for confirmation first, defaulting to no; scripts do not prompt. Every write is
1854
2093
  confined to the working directory, so the instruction is to change into it first rather than to pass
1855
2094
  a root. `repair` asks a second, separately defaulted question before deleting anything, and a
1856
- non-interactive session without `--apply` or `--yes` skips pruning rather than guessing. `fleet`
1857
- operates on the immediate children of the working directory and never on the directory itself, and
1858
- it has no root flag at all `repair` is the single-workspace tool.
1859
-
1860
- `fleet` and default `repair` are scoped to host-origin artifacts. `repair` states its selected scope
1861
- in the output; `--generated` widens both verbs to generated files while still excluding starter
1862
- files and `package.json`.
2095
+ session without `--apply` skips pruning regardless of `--yes`. `fleet` operates on the immediate
2096
+ children of the working directory and never on the directory itself. It has no root flag at all:
2097
+ passing `--target` is rejected with exit `2` instead of being silently ignored. `repair` is the
2098
+ single-workspace tool.
2099
+
2100
+ `fleet` and default `repair` are scoped to host-origin artifacts plus absent service-owned starter
2101
+ seams. Both state that selected scope in the output before they act — `repair` once its audit found
2102
+ something to repair, `fleet` once `--apply` authorized a write, naming the number of repositories
2103
+ that write covers. `--generated` widens both verbs to generated
2104
+ files and the manifest's generated service-script keys while still excluding present starter files
2105
+ and package publication metadata. Within either scope, missing files are safe to restore, stale
2106
+ files are report-only by default, and `--replace` is the explicit destructive opt-in.
1863
2107
 
1864
2108
  **Catalog markers.** `catalog` rewrites the block between `<!-- catalog:start -->` and
1865
- `<!-- catalog:end -->` in `.claude/agents/orkestrel.md`. **Ambiguous markers fail before any
2109
+ `<!-- catalog:end -->` in `CATALOG_AGENT_PATH`. **Ambiguous markers fail before any
1866
2110
  mutation**: the file must contain exactly one ordered pair. A missing marker, a reversed pair, or a
1867
2111
  repeated marker of either kind is a coded `TARGET` failure raised before the file is touched, and
1868
2112
  the run reports the drift and any row-count shrink rather than rewriting a file it cannot bound.
@@ -1874,10 +2118,16 @@ The check is a feature detection: **earlier supported Node 22 releases simply us
1874
2118
  roots**. It only ever adds trusted issuers — nothing disables verification — and a failure is a
1875
2119
  silent no-op rather than a crash. Custom PEMs are added through the standard environment variable.
1876
2120
 
1877
- **Exit codes.** `0` is clean or successful, `1` is drift or failure, `2` is a usage error. An audit
1878
- exits non-zero on any drift, foreign files included, which makes it usable directly as a CI gate. A
1879
- pull exits non-zero on any drift or failure whether or not `--strict` was passed; `--strict`
1880
- additionally throws on a network fault. Every unknown verb is a usage error and gets a nearest-match
2121
+ **Exit codes.** `0` is clean or successful, `1` is drift or failure, `2` is a usage error. Repair
2122
+ and fleet use the same dirty-repository predicate: selected-scope drift or any full-plan finding
2123
+ outside that scope keeps exit `1`. A repair that skips stale files therefore exits `1`; a repair
2124
+ exits `0` only when its selected audit and its reported outside scope are both clean. An audit exits
2125
+ non-zero on any drift, foreign files included, which makes it usable directly as a CI gate.
2126
+ `repair --json` carries that same terminal audit after any authorized write, while its `result`
2127
+ records the files the write copied, wrote, skipped, and removed. A pull exits non-zero on any drift
2128
+ or failure whether or not `--strict` was passed, including when
2129
+ other entries were applied successfully; `--strict` additionally throws on a network fault. Every
2130
+ unknown verb is a usage error and gets a nearest-match
1881
2131
  suggestion when one is sufficiently close.
1882
2132
 
1883
2133
  ## Package contents
@@ -1888,7 +2138,8 @@ files, plus `./package.json`. The `scaffold` binary maps to the built executable
1888
2138
 
1889
2139
  The published file set is exactly `dist/src`, `dist/bin`, `dist/host`, and `README.md`. `dist/host`
1890
2140
  is the vendored data root: the byte-preserved host files plus the `manifest.json` recording their
1891
- storage names, destinations, and executable bits. Storage names are un-dotted, because a leading dot
2141
+ storage names, destinations, executable bits, directory roots, and membership digest. Storage names
2142
+ are un-dotted, because a leading dot
1892
2143
  does not survive packaging intact; the manifest is what maps a storage name back to its real
1893
2144
  destination. That is also why the default host is resolved from the installed module's own
1894
2145
  location — the package carries its host data with itself, and a caller-supplied raw repository root
@@ -1900,7 +2151,10 @@ renderer behind the table and blockquote work; the template engine behind every
1900
2151
  artifact; and, consumed only at the executable boundary, the terminal prompt toolkit and the console
1901
2152
  reporter. The core face uses the first four and stays pure; the server face adds only `node:*`
1902
2153
  builtins. Development dependencies are the shared tooling baseline plus the guide-parity toolkit
1903
- that drives [`parity.test.ts`](../../tests/guides/src/parity.test.ts). The engines floor is Node
2154
+ that drives [`parity.test.ts`](../../tests/guides/src/parity.test.ts) and `@orkestrel/html`,
2155
+ which this package's real emitted-configuration tests execute. Generated manifests keep that HTML
2156
+ dependency scoped to `app/browser`; source-only, `app/core`, and `app/server` workspaces do not
2157
+ receive it. The engines floor is Node
1904
2158
  `>=22.12.0`, and the build emits ES and CJS for both library faces plus an ES executable.
1905
2159
 
1906
2160
  ## Patterns
@@ -2245,6 +2499,7 @@ import {
2245
2499
  appViteConfig,
2246
2500
  applicationViteConfig,
2247
2501
  binViteProject,
2502
+ configViteProject,
2248
2503
  coreTsconfig,
2249
2504
  coreViteConfig,
2250
2505
  guidesViteProject,
@@ -2276,13 +2531,18 @@ coreViteConfig()
2276
2531
  srcViteConfig('browser')
2277
2532
  appViteConfig('server')
2278
2533
  policyViteProject()
2534
+ configViteProject()
2279
2535
  guidesViteProject()
2280
2536
  binViteProject()
2281
2537
  integrationViteProject({ bin: true, integration: true, global: true })
2282
- serviceViteProject()
2283
- viteProjectDefinitions({ integration: true }).includes('export const integration =') // true
2284
- viteProjectRegistrations(['core'], [], { integration: true }).map(({ project }) => project)
2285
- // ['srcCore', 'policy', 'guides', 'integration']
2538
+ serviceViteProject('claude')
2539
+ viteProjectDefinitions({ integration: true, services: ['claude'] }).includes(
2540
+ 'export const serviceClaude =',
2541
+ ) // true
2542
+ viteProjectRegistrations(['core'], [], { integration: true, services: ['claude'] }).map(
2543
+ ({ project }) => project,
2544
+ )
2545
+ // ['srcCore', 'policy', 'config', 'guides', 'integration', 'serviceClaude']
2286
2546
 
2287
2547
  rootViteConfig(['core', 'server'], { bin: true })
2288
2548
  singleSrcViteConfig('server').includes('srcServer') // true
@@ -2322,6 +2582,7 @@ syncReportOf('./packages/router', [], []) // { clean: true, failed: 0, … }
2322
2582
  import { blueprint, blueprintToPlan, diffPlan } from '@orkestrel/scaffold'
2323
2583
  import {
2324
2584
  createMaterializer,
2585
+ digestHostManifest,
2325
2586
  hostRoot,
2326
2587
  hydratePlan,
2327
2588
  isVacant,
@@ -2335,6 +2596,7 @@ import {
2335
2596
  } from '@orkestrel/scaffold/server'
2336
2597
 
2337
2598
  const host = hostRoot()
2599
+ digestHostManifest([], []) // exact empty manifest membership digest
2338
2600
  readHostManifest(host) // the vendored manifest, or undefined for a raw root
2339
2601
  storagePath('.claude/agents/reviewer.md') // 'claude/agents/reviewer.md'
2340
2602
  locateHostSource(undefined, 'package.json', host)
@@ -2354,7 +2616,8 @@ const current = readTarget(
2354
2616
  './packages/router',
2355
2617
  plan.artifacts.map((artifact) => artifact.path),
2356
2618
  )
2357
- materializer.repair(plan, diffPlan(plan, current), './packages/router')
2619
+ materializer.repair(plan, diffPlan(plan, current), './packages/router') // missing only; stale is skipped
2620
+ materializer.repair(plan, diffPlan(plan, current), './packages/router', true) // replace stale bytes
2358
2621
  materializer.prune('./packages/router', {})
2359
2622
  materializer.destroy()
2360
2623
 
@@ -2549,7 +2812,11 @@ hasOnlyDataProperties({ a: 1 }) // true
2549
2812
  isDenseDataArray(['a'], 10, isPortablePath) // true
2550
2813
  isWritePrecondition({ path: 'package.json', shape: 'absent' }) // true
2551
2814
  isManifestEntry({ storage: 'AGENTS.md', destination: 'AGENTS.md', executable: false }) // true
2552
- isHostManifest({ entries: [], roots: [] }) // true
2815
+ isHostManifest({
2816
+ entries: [],
2817
+ roots: [],
2818
+ digest: 'f98e1531d9fd8fab7e301d1cc944249913d93f48c918a11a753048b877211679',
2819
+ }) // true
2553
2820
  isSyncEventHooks({ done: () => undefined }) // true
2554
2821
  isMaterializerEventHooks({ done: () => undefined }) // true
2555
2822
  isEmitterErrorHandler(() => undefined) // true
@@ -2576,6 +2843,8 @@ isMissingPathError(caught) // true only for an ENOENT error
2576
2843
  ids, the batch-overload semantics, and all-or-nothing list removal.
2577
2844
  - [`tests/src/core/policy.test.ts`](../../tests/src/core/policy.test.ts) — the repository coding-law
2578
2845
  policy module against this workspace and against deliberately hostile fixtures.
2846
+ - [`tests/config/vite.test.ts`](../../tests/config/vite.test.ts) — the executable root Vite
2847
+ invariants for workspace, environment, and output containment.
2579
2848
  - [`tests/src/server/helpers.test.ts`](../../tests/src/server/helpers.test.ts) — containment,
2580
2849
  digests, host staging, hydration, derivation, prune scanning, and the local catalog.
2581
2850
  - [`tests/src/server/validators.test.ts`](../../tests/src/server/validators.test.ts) — the portable