@orkestrel/scaffold 0.0.22 → 0.0.24

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 (39) hide show
  1. package/README.md +84 -99
  2. package/dist/bin/main.js +1094 -0
  3. package/dist/bin/main.js.map +1 -0
  4. package/dist/host/CLAUDE.md +3 -1
  5. package/dist/host/agents/orchestration.md +61 -4
  6. package/dist/host/agents/skills/orkestrel-align-packages/SKILL.md +1 -1
  7. package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +7 -5
  8. package/dist/host/agents/skills/orkestrel-harden-package/SKILL.md +1 -1
  9. package/dist/host/agents/skills/orkestrel-harden-package/references/contract.md +1 -1
  10. package/dist/host/claude/agents/orkestrel.md +9 -9
  11. package/dist/host/claude/rules/architecture.md +45 -3
  12. package/dist/host/claude/rules/quality.md +4 -0
  13. package/dist/host/claude/rules/tests.md +57 -1
  14. package/dist/host/claude/rules/workspace.md +50 -17
  15. package/dist/host/codex/agents/orkestrel.toml +1 -1
  16. package/dist/host/configs/helpers.ts +762 -0
  17. package/dist/host/dotfiles/oxlintrc.json +2 -1
  18. package/dist/host/guides/scaffold.md +862 -0
  19. package/dist/host/manifest.json +40 -33
  20. package/dist/host/tests/config.test.ts +544 -0
  21. package/dist/host/tests/policy.test.ts +46 -0
  22. package/dist/host/tests/setupPolicy.ts +557 -602
  23. package/dist/src/core/index.cjs +3569 -10510
  24. package/dist/src/core/index.cjs.map +1 -1
  25. package/dist/src/core/index.d.cts +2361 -2789
  26. package/dist/src/core/index.d.ts +2361 -2789
  27. package/dist/src/core/index.js +3513 -10374
  28. package/dist/src/core/index.js.map +1 -1
  29. package/dist/src/server/index.cjs +2855 -3765
  30. package/dist/src/server/index.cjs.map +1 -1
  31. package/dist/src/server/index.d.cts +1920 -1335
  32. package/dist/src/server/index.d.ts +1920 -1335
  33. package/dist/src/server/index.js +2812 -3680
  34. package/dist/src/server/index.js.map +1 -1
  35. package/package.json +16 -23
  36. package/dist/bin/scaffold.js +0 -1896
  37. package/dist/bin/scaffold.js.map +0 -1
  38. package/dist/host/guides/src/scaffold.md +0 -2886
  39. /package/dist/host/guides/{src/guide.md → guide.md} +0 -0
@@ -1,2886 +0,0 @@
1
- # Scaffold
2
-
3
- > A deterministic workspace-blueprint compiler: a closed, JSON-serializable `Blueprint` compiles
4
- > into a `Plan` of ordered `Artifact`s, and every downstream product — the files on disk, a review
5
- > document, an audit of an existing package, a freshness report — is projected from that one plan
6
- > rather than authored separately.
7
- >
8
- > The core face is pure and synchronous: no `node:*`, no clocks, no randomness, no I/O. A plan's
9
- > `trace` and `hash` derive from its own content. The server face owns the only two impure
10
- > entities — `Materializer`, which writes a plan to disk behind an explicit call, and `Sync`,
11
- > which reads upstream guides and registry versions over HTTPS. The `scaffold` executable is a
12
- > thin command-line shell around both.
13
- >
14
- > Every discriminant names its own axis. `origin` says how an artifact's content is produced,
15
- > `group` says which artifact group it belongs to, `environment` says which environment owns it,
16
- > `category` says what a declared member is, `drift` says how a target compares to its plan,
17
- > `freshness` says how a mirror compares to upstream, `stage` says which pipeline phase ran, and
18
- > `code` says which coded failure was raised.
19
- >
20
- > Source: [`src/core`](../../src/core) and [`src/server`](../../src/server), with
21
- > [`src/bin`](../../src/bin) as an executable build target. Core exports through
22
- > `@orkestrel/scaffold`; the materializer and sync export through `@orkestrel/scaffold/server`.
23
-
24
- Standing up — or auditing — a workspace in this style is a mechanical projection of a fixed set of
25
- conventions onto a name: the exports map for the selected src environments, the per-environment build
26
- configuration, the barrels, the test projects, the guide stubs, the parity harness. This package is
27
- that projection, expressed as data. Rendered defaults ship as versioned package data (frozen
28
- `TemplateDefinition` values filled by a pure fill engine), so a convention change is a version bump
29
- here rather than a hand edit in every workspace.
30
-
31
- The module is mechanism, never product policy. The judgment calls — the name, the description, the
32
- keywords, which src and app environments, which dependencies, any artifact override —
33
- belong to the caller. What this module supplies is the closed vocabularies, the variant matrix as
34
- data, exact-record validation, a fail-closed gate, a deterministic pin, and lossless projections.
35
-
36
- Separating the _what_ (the `Blueprint`) from the _how_ (the `Plan` and its writes) is the whole
37
- design. Because the plan and the audit are pure data, the same engine that creates a workspace can
38
- audit an existing one — `diffPlan` against its current bytes — and repair only what drifted. And
39
- because vendored dependency mirrors and pinned ranges themselves fall behind as upstream moves,
40
- `Sync` reports (and, under an explicit apply, refreshes) what has aged.
41
-
42
- ## Faces and dependency direction
43
-
44
- The package has three code faces. Generated workspaces use the separate `Environment` vocabulary
45
- (`core`, `browser`, `server`) to identify an environment selected on the `src` or `app` axis; the
46
- three faces below are this package's own.
47
-
48
- - **core** — [`src/core`](../../src/core), published as `@orkestrel/scaffold`. Pure, synchronous,
49
- host-independent. Compiling, validating, diffing, projecting, and every rendered default.
50
- - **server** — [`src/server`](../../src/server), published as `@orkestrel/scaffold/server`. Node
51
- only. Filesystem writes (`Materializer`), upstream fetches (`Sync`), the write-transaction
52
- machinery, and the host-staging primitive.
53
- - **bin** — [`src/bin`](../../src/bin), built to the `scaffold` executable. Not a barrel and not
54
- published as a module: it exports nothing to consumers, so it carries no guide parity of its own
55
- and is documented here in prose.
56
-
57
- Core imports neither of the others. Server imports core. The bin imports both. The same direction
58
- is what a generated workspace is held to, and the compiled workspace makes it enforceable rather
59
- than aspirational:
60
-
61
- - `src/core` and `app/core` are host-independent — no DOM, no `node:*`, no stylesheet imports.
62
- - `src/browser` and `app/browser` may import their own core plus browser libraries; they may never
63
- reach a Node builtin or a `/server` subpath.
64
- - `src/server` and `app/server` may import their own core plus server libraries; they may never
65
- reach Vue, a `/browser` subpath, or a stylesheet.
66
- - Published `src/*` may never import private `app/*`.
67
- - `app/browser` reaches server behavior only through shared `app/core` contracts and transports,
68
- never through a server implementation import.
69
-
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
81
- `reportApplicationServerError` handler writes only a stable configuration, lifecycle, or unknown
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.
105
-
106
- Every environment barrel is an export-star barrel: `index.ts` contains only `export * from './x.js'`
107
- rows and nothing else. Named, default, namespace, and type-only barrel rows are absent by design,
108
- so a star-export collision is a naming failure to fix at the owner rather than something to paper
109
- over with a selective row. Both of this package's own barrels follow that rule, and every generated
110
- barrel is emitted the same way.
111
-
112
- ## Surface
113
-
114
- Compile a blueprint into a `Scaffolding`, then project the `Plan` it carries. The whole core path
115
- is pure and synchronous; writing lives on the server face.
116
-
117
- ```ts
118
- import { blueprint, createCompiler, dependency, planToReview } from '@orkestrel/scaffold'
119
-
120
- const compiler = createCompiler()
121
-
122
- const scaffolding = compiler.compile(
123
- blueprint('router', {
124
- description: 'A tiny hash router.',
125
- keywords: ['router', 'hash'],
126
- src: ['core', 'browser', 'server'],
127
- dependencies: [dependency('@orkestrel/contract', '^0.0.7')],
128
- }),
129
- )
130
-
131
- scaffolding.complete // true — the gate passed
132
- if (scaffolding.plan) {
133
- scaffolding.plan.artifacts.length // every file the workspace needs, ordered
134
- planToReview(scaffolding.plan) // the copy-ready dry-run review document
135
- }
136
-
137
- compiler.emitter.on('block', (questions) => questions.length)
138
- compiler.destroy()
139
- ```
140
-
141
- An application-only blueprint uses an empty published set and an independent app set:
142
-
143
- ```ts
144
- import { blueprint, blueprintToPlan } from '@orkestrel/scaffold'
145
-
146
- const workspace = blueprint('console', {
147
- src: [],
148
- app: ['core', 'browser', 'server'],
149
- })
150
-
151
- const plan = blueprintToPlan(workspace)
152
- plan.artifacts.some((artifact) => artifact.path === 'app/browser/index.html') // true
153
- plan.artifacts.some((artifact) => artifact.path === 'app/server/main.ts') // true
154
- ```
155
-
156
- ### Types — core
157
-
158
- From [`types.ts`](../../src/core/types.ts).
159
-
160
- | Name | Kind |
161
- | ------------------------- | --------- |
162
- | `Environment` | type |
163
- | `BuildFormat` | type |
164
- | `SrcDefinition` | interface |
165
- | `AppDefinition` | interface |
166
- | `ViteMachinery` | interface |
167
- | `ViteFacts` | interface |
168
- | `ViteProjectRegistration` | interface |
169
- | `Origin` | type |
170
- | `Group` | type |
171
- | `Category` | type |
172
- | `CatalogEntry` | interface |
173
- | `Drift` | type |
174
- | `Freshness` | type |
175
- | `CompileStage` | type |
176
- | `ScaffoldErrorCode` | type |
177
- | `Dependency` | interface |
178
- | `Override` | interface |
179
- | `Blueprint` | interface |
180
- | `Member` | interface |
181
- | `ArtifactBase` | interface |
182
- | `HostArtifact` | interface |
183
- | `ContentArtifact` | interface |
184
- | `Artifact` | type |
185
- | `Snapshot` | type |
186
- | `Plan` | interface |
187
- | `Finding` | interface |
188
- | `Audit` | interface |
189
- | `Question` | interface |
190
- | `Validation` | interface |
191
- | `GuideSync` | interface |
192
- | `VersionSync` | interface |
193
- | `SyncReport` | interface |
194
- | `PlanSummary` | interface |
195
- | `CompileRecord` | interface |
196
- | `CompileFailure` | interface |
197
- | `Scaffolding` | interface |
198
- | `PlanRecord` | interface |
199
- | `CompilerEventMap` | type |
200
- | `CompilerOptions` | interface |
201
- | `CompilerInterface` | interface |
202
- | `PlanManagerEventMap` | type |
203
- | `PlanManagerOptions` | interface |
204
- | `PlanManagerInterface` | interface |
205
-
206
- The closed vocabularies are small and total. `Environment` is `'core' | 'browser' | 'server'`.
207
- `BuildFormat` is `'es' | 'cjs'`. `Origin` is `'host' | 'template' | 'computed'`. `Group` is
208
- `'manifest' | 'configs' | 'source' | 'tests' | 'guides' | 'docs' | 'orchestration'`. `Category` is
209
- `'type' | 'alias' | 'constant' | 'factory' | 'entity' | 'parser' | 'guard' | 'handler' | 'error'`.
210
- `Drift` is `'aligned' | 'stale' | 'missing' | 'foreign'`. `Freshness` is
211
- `'current' | 'behind' | 'missing' | 'failed'`, where `missing` is an upstream `404` and `failed` is
212
- a transport fault. `CompileStage` is `'draft' | 'gate' | 'pin'`, in that order. `ScaffoldErrorCode`
213
- is `'INVALID' | 'BLOCKED' | 'DESTROYED' | 'TARGET' | 'WRITE' | 'FETCH'`.
214
-
215
- `SrcDefinition` and `AppDefinition` are the per-environment matrix rows: the configuration files an
216
- environment contributes, its test-project label, and — on the `src` axis — its `exports` subpath
217
- and build formats, or — on the `app` axis — its optional runtime entry.
218
-
219
- `ViteMachinery` names the four host-specific pipelines a workspace's generated `vite.config.ts` may
220
- carry: `browser` selects the shared root CSS-analysis and Playwright machinery, `vue` selects the
221
- single-file-component, HTML, and development-server machinery an application browser environment
222
- needs, `output` selects build-output containment, and `showcase` selects the optional single-file
223
- application-browser projection. The root machinery selection never attaches a
224
- `css` property to a nonbrowser project: only the `srcBrowser` and `appBrowser` factories own
225
- `ENVIRONMENT_CSS`. It never selects a boundary guarantee — those ship in every shape, as the
226
- compilers section sets out.
227
-
228
- `ViteFacts` is the optional structural-fact slice shared by every root Vite compiler:
229
- `bin` and `integration` each select their matching standalone project when `true`, while `services`
230
- selects one standalone project for every listed vendor;
231
- `global` records the exact-case consumer-owned global-setup module and wires it into each eligible
232
- project; `showcase` records the exact-case consumer-owned showcase wrapper and selects only its
233
- generated browser machinery.
234
-
235
- `ViteProjectRegistration` carries one generated project factory identifier and its optional browser
236
- label. Root configuration renderers preserve that browser ownership as data through registration
237
- instead of inferring it from a project identifier.
238
-
239
- `Blueprint` is the closed input spec:
240
-
241
- ```ts
242
- interface Blueprint {
243
- readonly name: string
244
- readonly description?: string
245
- readonly keywords: readonly string[]
246
- readonly src: readonly Environment[]
247
- readonly app: readonly Environment[]
248
- readonly dependencies: readonly Dependency[]
249
- readonly peers: readonly Dependency[]
250
- readonly extras: readonly Dependency[]
251
- readonly version: string
252
- readonly engines: string
253
- readonly overrides: readonly Override[]
254
- readonly bin: boolean
255
- readonly integration: boolean
256
- readonly services: readonly string[]
257
- readonly global: boolean
258
- readonly showcase: boolean
259
- }
260
- ```
261
-
262
- `src` selects published library environments under `src`; `app` selects private runtime
263
- environments under `app`. The two axes are independent, so library-only, application-only, and
264
- mixed workspaces are all first class. `dependencies` and `peers` are runtime `@orkestrel/*`
265
- packages — a peer flagged `optional` also gets a `peerDependenciesMeta` entry. `extras` are
266
- package-specific development dependencies merged over the generated baseline, and may carry any
267
- valid npm package name.
268
-
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.
273
- `deriveBlueprint` probes those paths, so a fresh compile and an audit of a mature repository agree
274
- on what the workspace is.
275
-
276
- - **`bin`** — `src/bin/` exists. It alone turns on the self-hosting extras: the manifest's `bin`
277
- entry, the `scaffold` script pointed at the built executable, the bin check, test, and build
278
- scripts, `build:host`, the `configs/src/tsconfig.bin.json` and `configs/src/vite.bin.config.ts`
279
- artifacts, and the `src:bin` test project.
280
- - **`integration`** — `tests/integration/` exists. It records a slow, opt-in proof project over the
281
- workspace's own built output, outside the default run: the generated root configuration registers
282
- a standalone `integration` project including `tests/integration/**/*.test.ts`, and the manifest
283
- emits `test:integration`.
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.
289
- - **`global`** — the physical, exact-case `tests/setupGlobal.ts` file exists. It is the single
290
- governing setup-presence fact. A declared `src/browser` project runs that consumer-owned module
291
- as `globalSetup`; integration runs it only when `bin` and `integration` are also true.
292
- Application-browser, styles, service, and unrelated proof projects never receive it.
293
- - **`showcase`** — the physical, exact-case regular file
294
- `configs/app/vite.showcase.config.ts` exists. It is valid only with `app/browser` and turns on the
295
- computed wrapper, the closed `appShowcase()` root factory, three opt-in scripts, and the
296
- consumer-only `vite-plugin-singlefile` development dependency. A directory, link, wrong-case
297
- name, absent wrapper, demo HTML, script, or installed dependency never implies this fact.
298
-
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.
313
-
314
- `Override` replaces a rendered artifact's content at a path, never partially merges it. `Member` is
315
- one declared public export of the scaffolded workspace, derived rather than authored.
316
-
317
- `Artifact` is origin-discriminated. `ArtifactBase` carries `path`, `group`, and an optional
318
- `environment`. A `HostArtifact` has `origin: 'host'`, an optional `source` (defaulting to `path`), and
319
- an optional `hex` of exact lowercase bytes; it never carries `content`. A `ContentArtifact` has
320
- `origin: 'template' | 'computed'` and always carries `content`; it never carries `hex` or `source`.
321
- `Snapshot` is `Readonly<Record<string, string>>` — exact lowercase hexadecimal target bytes keyed
322
- by artifact-relative path.
323
-
324
- `Plan` carries the originating `blueprint`, the `groups` it covers, the ordered `artifacts`, and the
325
- `trace` and `hash` the pin fills. The trace names both independent axes as `src:<selection>` and
326
- `app:<selection>`, using `none` when one axis is empty, so app-only and mixed plans stay
327
- self-describing. `PlanSummary` is the dry-run tally by origin and carries both selections. `Finding` is one
328
- drift verdict with an optional bounded `observed` byte hex for a stale destination, and `Audit` is
329
- the whole diff plus its `clean` and `complete` flags, `questions`, and `drifted` / `missing` /
330
- `foreign` counts.
331
- `Question` is one validation issue; `blocking: true` fails the gate closed while
332
- `false` rides a complete result as an advisory. `Validation` is the semantic pass result and never
333
- throws.
334
-
335
- `Scaffolding` is the replayable outcome of one compile: the `blueprint`, the `plan` when complete,
336
- the accumulated `questions`, one `CompileRecord` per stage, any `CompileFailure` markers, the
337
- `complete` flag, and the content `digest`. `PlanRecord` is a versioned, content-hashed plan inside a
338
- `PlanManager`.
339
-
340
- `GuideSync`, `VersionSync`, and `SyncReport` are the freshness shapes. `GuideSync` carries the
341
- fetched `content`, its `freshness`, an optional `note` explaining a non-clean outcome, and an
342
- optional `baseline` — the SHA-256 of the observed local mirror, or the literal `absent`, present
343
- only on target-aware synchronization. `VersionSync` compares a declared `range` to the registry
344
- `latest`.
345
- `SyncReport` is `clean` only when nothing drifted and nothing failed. `CatalogEntry` is one fleet
346
- package row; its `description` is the flattened text of that package's own guide's first
347
- blockquote, and the empty string when that guide is missing, unreadable, or carries no blockquote.
348
-
349
- `CompilerEventMap`, `CompilerOptions`, and `CompilerInterface` are the compiler triad;
350
- `PlanManagerEventMap`, `PlanManagerOptions`, and `PlanManagerInterface` are the registry triad.
351
- Both options records take `on` initial listeners and an `error` listener-failure handler, and
352
- `PlanManagerOptions` additionally seeds `plans`.
353
-
354
- ### Types — server
355
-
356
- From [`types.ts`](../../src/server/types.ts).
357
-
358
- | Name | Kind |
359
- | ----------------------- | --------- |
360
- | `MaterializeResult` | interface |
361
- | `MaterializerEventMap` | type |
362
- | `MaterializerOptions` | interface |
363
- | `ManifestEntry` | interface |
364
- | `HostManifest` | interface |
365
- | `WriteExpectation` | interface |
366
- | `WritePrecondition` | interface |
367
- | `WriteAnchor` | interface |
368
- | `WriteDirectoryResult` | interface |
369
- | `SyncAllowance` | type |
370
- | `CatalogAllowance` | type |
371
- | `SyncBase` | type |
372
- | `SyncBranch` | type |
373
- | `VersionLookup` | type |
374
- | `GuideWrite` | interface |
375
- | `MaterializerInterface` | interface |
376
- | `SyncEventMap` | type |
377
- | `SyncOptions` | interface |
378
- | `SyncInterface` | interface |
379
-
380
- `MaterializeResult` reports the `target` plus the `written`, `copied`, `skipped`, and `removed`
381
- paths of one call. `MaterializerOptions` accepts a `host` root override plus emitter `on` hooks and
382
- an `error` handler; the default host is this package's own vendored data root, resolved from the
383
- installed module's own location rather than the caller's working directory. A caller-supplied host
384
- pointing at a raw repository root — one with no `manifest.json` beside it — maps artifact paths 1:1
385
- instead of through the manifest.
386
-
387
- `ManifestEntry` is one vendored-host file record — its un-dotted `storage` name, its `destination`
388
- relative to a target, and an `executable` bit. `HostManifest` pairs the sorted file `entries` with
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.
394
-
395
- The write-transaction shapes are the fail-closed mutation vocabulary. `WriteExpectation` is one
396
- destination snapshot captured before mutation (`absent`, `file`, or `directory`, with device,
397
- inode, modification time, size, and digest where they apply). `WritePrecondition` is the narrower
398
- caller-observed state a transaction must still match. `WriteAnchor` is a physical directory
399
- identity, and `WriteDirectoryResult` pairs the final anchor with the subset a call created.
400
- `GuideWrite` pairs one validated guide update with its contained destination. `SyncAllowance` and
401
- `CatalogAllowance` are one-cell `Float64Array` allowances: the former shares a byte budget across
402
- concurrent network readers, while the latter shares one entry budget across every fleet root and
403
- child visited by a catalog operation. `SyncBase` and `SyncBranch` are normalized strings returned
404
- only by their corresponding boundary parsers. `VersionLookup` is the bare-name registry result:
405
- a successful lookup carries `latest` with `freshness: 'behind'` because no declared range was
406
- supplied as a reference, while `missing` and `failed` carry a `note` and no invented version.
407
-
408
- `SyncOptions` groups the injectable endpoints under the entity they configure — `guides` with
409
- `base`, `branch`, and `timeout`; `registry` with `base` and `timeout` — alongside `concurrency`,
410
- `retries`, `strict`, `limit`, `items`, `budget`, and the emitter `on` and `error` keys.
411
-
412
- ### Constants — core
413
-
414
- From [`constants.ts`](../../src/core/constants.ts).
415
-
416
- | Name | Kind |
417
- | --------------------------------- | ----- |
418
- | `ENVIRONMENTS` | const |
419
- | `ORIGINS` | const |
420
- | `GROUPS` | const |
421
- | `CATEGORIES` | const |
422
- | `FRESHNESS` | const |
423
- | `COMPILE_STAGES` | const |
424
- | `SRC_MATRIX` | const |
425
- | `BIN_CONFIGS` | const |
426
- | `APP_MATRIX` | const |
427
- | `HOST_PATHS` | const |
428
- | `ORCHESTRATION_PATH_PREFIXES` | const |
429
- | `ORCHESTRATION_PATH_NAMES` | const |
430
- | `SERVICE_SCRIPT_PATH` | const |
431
- | `GLOBAL_SETUP_PATH` | const |
432
- | `SHOWCASE_CONFIG_PATH` | const |
433
- | `CATALOG_AGENT_PATH` | const |
434
- | `NAME_PATTERN` | const |
435
- | `MAX_NAME_LENGTH` | const |
436
- | `MAX_DEPENDENCY_NAME_LENGTH` | const |
437
- | `MAX_PATH_LENGTH` | const |
438
- | `CONTROL_CHARACTER_PATTERN` | const |
439
- | `INVALID_PATH_CHARACTER_PATTERN` | const |
440
- | `MAX_RANGE_LENGTH` | const |
441
- | `MAX_COLLECTION_ITEMS` | const |
442
- | `MAX_DATA_GRAPH_NODES` | const |
443
- | `MAX_DATA_GRAPH_KEYS` | const |
444
- | `VERSION_PATTERN` | const |
445
- | `ORKESTREL_RANGE_PATTERN` | const |
446
- | `EXTRA_RANGE_PATTERN` | const |
447
- | `ENGINES_PATTERN` | const |
448
- | `MINIMUM_NODE_VERSION` | const |
449
- | `EXPORT_KEYWORD` | const |
450
- | `CONST_KEYWORD` | const |
451
- | `IMPORT_KEYWORD` | const |
452
- | `FUNCTION_KEYWORD` | const |
453
- | `HEX_PATTERN` | const |
454
- | `MAX_ARTIFACT_BYTES` | const |
455
- | `MAX_TOTAL_ARTIFACT_BYTES` | const |
456
- | `MAX_SERIALIZED_INPUT_BYTES` | const |
457
- | `MAX_MANIFEST_BYTES` | const |
458
- | `MAX_ARTIFACT_HEX_LENGTH` | const |
459
- | `SYNC_BASELINE_PATTERN` | const |
460
- | `DEPENDENCY_NAME_PATTERN` | const |
461
- | `EXTRA_NAME_PATTERN` | const |
462
- | `DEFAULT_VERSION` | const |
463
- | `DEFAULT_ENGINES` | const |
464
- | `SCAFFOLD_RANGE` | const |
465
- | `BASE_DEV_DEPENDENCIES` | const |
466
- | `SOURCE_BROWSER_DEV_DEPENDENCIES` | const |
467
- | `APP_DEV_DEPENDENCIES` | const |
468
- | `APP_BROWSER_DEV_DEPENDENCIES` | const |
469
- | `APP_SERVER_DEV_DEPENDENCIES` | const |
470
- | `CHECKOUT_ACTION_SHA` | const |
471
- | `SETUP_NODE_ACTION_SHA` | const |
472
- | `COMPILER_ID` | const |
473
- | `TYPESCRIPT_EXTENSIONS` | const |
474
- | `JSON_PRINT_WIDTH` | const |
475
- | `JSON_TAB_WIDTH` | const |
476
-
477
- `ENVIRONMENTS`, `ORIGINS`, `GROUPS`, `CATEGORIES`, `FRESHNESS`, and `COMPILE_STAGES` are the frozen
478
- value lists behind their literal unions. `SRC_MATRIX` is the `src` environment matrix as
479
- data — each environment's `configs/src` files, test-project label, `exports` subpath, and build
480
- formats. `APP_MATRIX` is its application sibling, adding the runtime entry where an environment
481
- produces one (`app/browser/index.html`, `app/server/main.ts`). `BIN_CONFIGS` is the executable
482
- axis's computed `tsconfig` and Vite wrapper pair. `HOST_PATHS` is the ordered list of byte-copied
483
- host artifacts, and it is the staging manifest rather than the per-plan carried set:
484
- `stageHost` vendors every path on it, while each plan carries the subset `selectHostPaths` selects
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
491
- module that independently selected projects can load. `SHOWCASE_CONFIG_PATH` names the sole
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
- ```
517
-
518
- The bounds are public because they are part of the contract, not implementation trivia.
519
- `MAX_ARTIFACT_BYTES` caps one artifact at 5 MiB and `MAX_TOTAL_ARTIFACT_BYTES` caps one blueprint,
520
- plan, audit, or report at 100 MiB in aggregate. `MAX_SERIALIZED_INPUT_BYTES` is four times that
521
- aggregate ceiling so serialized hexadecimal records have a bounded envelope before JSON parsing,
522
- and `MAX_MANIFEST_BYTES` caps every package or host manifest at 1 MiB.
523
- `MAX_ARTIFACT_HEX_LENGTH` is the hexadecimal form of the per-artifact bound.
524
- `MAX_COLLECTION_ITEMS` bounds one public collection at 1,000 entries.
525
- `MAX_DATA_GRAPH_NODES` and `MAX_DATA_GRAPH_KEYS` cap recursive ownership inspection even when an
526
- adversarial proxy produces a fresh identity at every step.
527
- `MAX_NAME_LENGTH` is 203 so the published scoped name fits npm's 214-character limit, which
528
- `MAX_DEPENDENCY_NAME_LENGTH` records directly. `MAX_PATH_LENGTH` and `MAX_RANGE_LENGTH` bound
529
- serialized path and range tokens.
530
-
531
- The patterns are the shape laws. `NAME_PATTERN` is the lowercase, letter-first workspace name.
532
- `DEPENDENCY_NAME_PATTERN` closes `dependencies` and `peers` to `@orkestrel/<name>` — a name-shaped
533
- law at the gate, because those are the only names that ever feed a derived `guides/src/<name>.md`
534
- path. `EXTRA_NAME_PATTERN` is deliberately broader (any valid npm package name, scoped or not),
535
- because `extras` names are manifest content and never feed a path. `VERSION_PATTERN` is exact
536
- three-component semver; `ORKESTREL_RANGE_PATTERN` is the caret-pinned pre-1.0 range;
537
- `EXTRA_RANGE_PATTERN` is the registry-only semver subset; `ENGINES_PATTERN` is the minimum-Node
538
- form. `HEX_PATTERN` requires whole lowercase byte pairs, and `SYNC_BASELINE_PATTERN` accepts either
539
- `absent` or an exact SHA-256 digest. `CONTROL_CHARACTER_PATTERN` and
540
- `INVALID_PATH_CHARACTER_PATTERN` reject control characters and non-portable path characters.
541
-
542
- `MINIMUM_NODE_VERSION` is `22.12.0`, `DEFAULT_ENGINES` derives from it, and `DEFAULT_VERSION` is
543
- `0.0.1`. `BASE_DEV_DEPENDENCIES` is the host-neutral tooling baseline every generated workspace
544
- gets; `SOURCE_BROWSER_DEV_DEPENDENCIES` adds the real browser providers a published browser environment
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
550
- 8.2 `CSSOptions`, `preprocessCSS`, and `isCSSRequest` surface, while the selected
551
- `css.transformer` / `lightningcss` path is experimental and must not float into an unreviewed minor.
552
- `SCAFFOLD_RANGE` is the range generated workspaces pin this package at.
553
- `CHECKOUT_ACTION_SHA` and `SETUP_NODE_ACTION_SHA` pin the two official CI actions to immutable
554
- commits. `TYPESCRIPT_EXTENSIONS` is the module extension set every generated scoped check covers.
555
- `JSON_PRINT_WIDTH` and `JSON_TAB_WIDTH` mirror the formatter configuration, so computed JSON is
556
- format-stable by construction. `EXPORT_KEYWORD`, `CONST_KEYWORD`, `IMPORT_KEYWORD`, and
557
- `FUNCTION_KEYWORD` keep declaration tokens out of rendered template literals, so a line-based
558
- parity scan reading this package's own source never mistakes emitted file text for a real export.
559
- `COMPILER_ID` is the default orchestrator id.
560
-
561
- ### Constants — server
562
-
563
- From [`constants.ts`](../../src/server/constants.ts).
564
-
565
- | Name | Kind |
566
- | -------------------------------- | ----- |
567
- | `PRUNE_DIRECTORIES` | const |
568
- | `HOST_MANIFEST_PATH` | const |
569
- | `SENSITIVE_HOST_PATH_PATTERN` | const |
570
- | `RESERVED_TARGET_PATH_PATTERN` | const |
571
- | `MAX_CATALOG_DESCRIPTION_LENGTH` | const |
572
- | `MAX_GUIDE_BYTES` | const |
573
- | `MAX_HOST_ENTRIES` | const |
574
- | `MAX_HOST_DEPTH` | const |
575
- | `MAX_FILESYSTEM_DEPTH` | const |
576
- | `MAX_PATH_SEGMENT_BYTES` | const |
577
- | `RESERVED_PATH_SEGMENT_PATTERN` | const |
578
- | `MAX_SYNC_CONCURRENCY` | const |
579
- | `DEFAULT_SYNC_CONCURRENCY` | const |
580
- | `MAX_SYNC_RETRIES` | const |
581
- | `MAX_SYNC_TIMEOUT` | const |
582
- | `DEFAULT_SYNC_TIMEOUT` | const |
583
- | `MAX_SYNC_LIMIT` | const |
584
- | `DEFAULT_SYNC_LIMIT` | const |
585
- | `DEFAULT_SYNC_ITEMS` | const |
586
- | `MAX_SYNC_ITEMS` | const |
587
- | `DEFAULT_SYNC_BUDGET` | const |
588
- | `MAX_SYNC_BUDGET` | const |
589
- | `MAX_SYNC_BASE_LENGTH` | const |
590
- | `MAX_SYNC_BRANCH_LENGTH` | const |
591
- | `WRITE_DIGEST_PATTERN` | const |
592
- | `SYNC_BRANCH_PATTERN` | const |
593
-
594
- `PRUNE_DIRECTORIES` is the closed set of prune-owned directories — `.claude/agents`,
595
- `.codex/agents`, and `scripts`. Nothing outside those roots is ever a deletion candidate, which is
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.
605
- `HOST_MANIFEST_PATH` is the reserved `manifest.json` written at the root of every staged host.
606
- `SENSITIVE_HOST_PATH_PATTERN` rejects credential-like, key-store, certificate-key, and
607
- local-configuration paths at the staging boundary. `RESERVED_TARGET_PATH_PATTERN` protects `.git`
608
- and every descendant from materialization, including when a hand-built plan targets a directory
609
- that is otherwise vacant. `RESERVED_PATH_SEGMENT_PATTERN` rejects Windows device names even when
610
- they carry an extension. `MAX_HOST_ENTRIES` and `MAX_HOST_DEPTH` bound vendored-host walks;
611
- `MAX_FILESYSTEM_DEPTH` and `MAX_PATH_SEGMENT_BYTES` bound caller-supplied filesystem paths before
612
- traversal. `MAX_GUIDE_BYTES` limits a catalog guide to the per-artifact ceiling before Markdown
613
- parsing.
614
-
615
- The `Sync` bounds come in matched default and maximum pairs: `concurrency` defaults to 6 and is
616
- capped at 64, `timeout` defaults to 10 seconds and is capped at 5 minutes, `retries` is capped at
617
- 5, the per-response byte `limit` defaults to and is capped at the 5 MiB artifact limit, `items`
618
- defaults to 256 and is capped at 1,000, and the cumulative `budget` defaults to 16 MiB and is
619
- capped at 100 MiB. Endpoint bases and branch names are additionally bounded by
620
- `MAX_SYNC_BASE_LENGTH` and `MAX_SYNC_BRANCH_LENGTH`. `WRITE_DIGEST_PATTERN` is the exact SHA-256
621
- form a write precondition accepts; `SYNC_BRANCH_PATTERN` is the initial safe-character law for the
622
- upstream guide URL boundary, followed by Git-ref structural checks in `parseSyncBranch`.
623
- `MAX_CATALOG_DESCRIPTION_LENGTH` bounds a normalized catalog description at 500 characters.
624
-
625
- ### Templates
626
-
627
- From [`templates.ts`](../../src/core/templates.ts).
628
-
629
- | Name | Kind |
630
- | ----------- | ----- |
631
- | `TEMPLATES` | const |
632
-
633
- `TEMPLATES` is the shipped, versioned `TemplateDefinition` data behind every `template`-origin
634
- artifact. Only genuinely templated prose and source live here — starter README and guide text,
635
- source stubs, application stubs, test stubs. Every structural file (`package.json`, the tsconfigs,
636
- the build configuration) is `computed` instead, so a literal `{{…}}` inside a configuration can
637
- never be mistaken for a placeholder. Changing a convention is a version bump of this package rather
638
- than a hand edit of a generated workspace's copy.
639
-
640
- ### Errors
641
-
642
- From [`errors.ts`](../../src/core/errors.ts).
643
-
644
- | Name | Kind |
645
- | ----------------- | -------- |
646
- | `ScaffoldError` | class |
647
- | `isScaffoldError` | function |
648
-
649
- `ScaffoldError` carries a machine-readable `code` and an optional `context`, and `isScaffoldError`
650
- is its total narrowing guard for a `catch`. Throwing is reserved for caller misuse:
651
- `createBlueprint` on off-contract data throws `INVALID`; any method called after `destroy()` throws
652
- `DESTROYED`; on the server face a non-vacant materialize target throws `TARGET` and a failed write
653
- throws `WRITE`; a strict-mode upstream failure throws `FETCH`. A failing gate is deliberately _not_
654
- an error — it fails closed into an incomplete `Scaffolding` whose `failures` carry a `BLOCKED`
655
- marker.
656
-
657
- ### Validators — core
658
-
659
- From [`validators.ts`](../../src/core/validators.ts).
660
-
661
- | Name | Kind |
662
- | ------------------------- | -------- |
663
- | `isDependency` | const |
664
- | `isOverride` | const |
665
- | `hasValidOverrideBytes` | function |
666
- | `isWorkspaceName` | function |
667
- | `hasOnlyDataProperties` | function |
668
- | `isDenseDataArray` | function |
669
- | `isEmitterErrorHandler` | function |
670
- | `isCompilerEventHooks` | function |
671
- | `isPlanManagerEventHooks` | function |
672
- | `hasBlueprintEnvironment` | function |
673
- | `hasValidBlueprintBytes` | function |
674
- | `isBlueprint` | const |
675
- | `isMember` | const |
676
- | `hasValidArtifactHex` | function |
677
- | `hasValidArtifactBytes` | function |
678
- | `hasValidPlanHex` | function |
679
- | `hasValidPlanBytes` | function |
680
- | `hasValidAuditBytes` | function |
681
- | `hasValidSnapshotBytes` | function |
682
- | `isArtifact` | const |
683
- | `isPlan` | const |
684
- | `validatePlan` | function |
685
- | `hasValidSyncReportBytes` | function |
686
- | `isSyncReport` | const |
687
-
688
- The seven `is*` constants are total guards compiled from their shapes and refined by the `has*`
689
- predicates beside them. A guard never throws — adversarial input, hostile prototypes, deep nesting,
690
- and cycles all return `false`. The refinements are exported separately because they carry real
691
- laws: `hasBlueprintEnvironment` requires at least one selected environment across the two axes;
692
- `hasValidArtifactHex` applies the lowercase byte-pair law; and the `*Bytes` predicates apply the
693
- per-item and aggregate byte limits to overrides, blueprints, artifacts, plans, audits, snapshots,
694
- and sync reports. `isWorkspaceName` is the bounded bare-name guard used wherever a manifest name is
695
- read back. `hasOnlyDataProperties` and `isDenseDataArray` are core guards because every environment
696
- uses the same accessor-free graph and dense-array boundary. `isEmitterErrorHandler`,
697
- `isCompilerEventHooks`, and `isPlanManagerEventHooks` validate callable observation seams before
698
- entity allocation.
699
-
700
- `validatePlan` is the pre-mutation gate: it runs the semantic pass over the plan's own blueprint and
701
- then checks every override against the exact artifact set the plan would write. An override whose
702
- `path` matches no planned artifact, targets a `host`-origin artifact, or targets the
703
- blueprint-owned `package.json` publication boundary is a blocking question rather than a silent
704
- no-op. An override that clears all three lands a `warnings` entry naming the path it replaces — the
705
- declaration is accepted, and it is never accepted silently.
706
-
707
- ### Validators — server
708
-
709
- From [`validators.ts`](../../src/server/validators.ts).
710
-
711
- | Name | Kind |
712
- | -------------------------- | -------- |
713
- | `isPortablePath` | function |
714
- | `isFilesystemPath` | function |
715
- | `isTerminalText` | function |
716
- | `isDependencyData` | function |
717
- | `isSensitiveHostPath` | function |
718
- | `isReservedTargetPath` | function |
719
- | `isCatalogAllowance` | function |
720
- | `isCatalogDescription` | function |
721
- | `isMissingPathError` | function |
722
- | `isWritePrecondition` | function |
723
- | `isManifestEntry` | function |
724
- | `isHostManifest` | function |
725
- | `isSyncEventHooks` | function |
726
- | `isMaterializerEventHooks` | function |
727
-
728
- `isPortablePath` is the law every write and read is held to: a non-empty relative POSIX path, under
729
- the length bound, free of control characters and non-portable characters, with no empty, `.`, `..`,
730
- trailing-dot, trailing-space, or reserved-device segment. `isFilesystemPath` is the looser bound for
731
- a host path a caller supplies, and `isTerminalText` is the bound for anything rendered into a
732
- terminal or a JSON diagnostic. `isDependencyData` combines the data-only reflection with the core
733
- dependency guard. `isReservedTargetPath` identifies preserved `.git` metadata, while
734
- `isCatalogAllowance` bounds the single fleet counter at `MAX_HOST_ENTRIES` before directory
735
- traversal and reads the typed array's intrinsic backing buffer, rejecting shared storage even when a
736
- caller shadows the public `buffer` property.
737
-
738
- `hasOnlyDataProperties` and `isDenseDataArray` exist because a boundary that copies a caller's graph
739
- must never invoke a caller-defined accessor: the first walks a record or array graph and rejects any
740
- non-data property within the public node/key budgets, while the second rejects a sparse,
741
- symbol-bearing, or method-bearing array. Together they make a structured clone of an untrusted
742
- input safe without admitting unbounded traversal. `isWritePrecondition`, `isManifestEntry`, and
743
- `isHostManifest` are the exact-shape guards for the mutation and vendored-host records, and
744
- `isSyncEventHooks`, `isMaterializerEventHooks`, and `isEmitterErrorHandler` reject an options object
745
- carrying an unknown or non-callable hook. `isMissingPathError` narrows a caught filesystem error to
746
- exactly `ENOENT`, so an absent path is never conflated with a permission failure.
747
-
748
- ### Parsers — core
749
-
750
- From [`parsers.ts`](../../src/core/parsers.ts).
751
-
752
- | Name | Kind |
753
- | ------------------------- | -------- |
754
- | `parseBoundedJSON` | function |
755
- | `parseCompilerOptions` | function |
756
- | `parseBlueprint` | function |
757
- | `parsePlan` | function |
758
- | `parsePlanIds` | function |
759
- | `parsePlanManagerOptions` | function |
760
- | `parseSyncReport` | function |
761
-
762
- `parseBoundedJSON` measures serialized UTF-8 bytes before allocating a parsed graph, applies a
763
- caller-supplied `@orkestrel/contract` guard, and returns `undefined` for an invalid budget,
764
- oversized or malformed JSON, or an off-contract result. The three domain parsers are the coercing
765
- counterparts of their guards. Given a value they return it when the guard accepts it; given a string
766
- they pass through the shared serialized-input ceiling before JSON parsing. A guard-valid value
767
- round-trips unchanged, and malformed or off-contract input returns `undefined` rather than throwing.
768
- `parsePlanIds` snapshots a bounded dense unique string array entirely through own data descriptors;
769
- it never invokes a caller's iterator, accessor, symbol member, or sparse index.
770
- `parseCompilerOptions` accepts only own `on` and `error` data properties and copies the compiler's
771
- declared listener hooks before its emitter is allocated.
772
- `parsePlanManagerOptions` performs the same fail-closed work for constructor options: it accepts
773
- only own `plans`, `on`, and `error` data properties, bounds and snapshots seed plans without calling
774
- their iterator, and copies only the three declared listener hooks.
775
-
776
- ### Cloners — core
777
-
778
- From [`cloners.ts`](../../src/core/cloners.ts).
779
-
780
- | Name | Kind |
781
- | -------------- | -------- |
782
- | `snapshotPlan` | function |
783
-
784
- `snapshotPlan` validates a data-only plan, detaches it through its canonical JSON representation,
785
- and recursively freezes the entire owned graph. A `PlanManager` therefore never aliases a caller's
786
- blueprint, artifacts, arrays, or returned record.
787
-
788
- ### Parsers — server
789
-
790
- From [`parsers.ts`](../../src/server/parsers.ts).
791
-
792
- | Name | Kind |
793
- | -------------------------- | -------- |
794
- | `parseSyncDependencies` | function |
795
- | `parseSyncNames` | function |
796
- | `parseFilesystemPaths` | function |
797
- | `parsePortablePaths` | function |
798
- | `parseWritePreconditions` | function |
799
- | `parseSyncBase` | function |
800
- | `parseSyncCurrent` | function |
801
- | `parseSyncBranch` | function |
802
- | `parseMaterializerOptions` | function |
803
- | `parseSyncOptions` | function |
804
-
805
- These are the boundary coercers that run before any resource is allocated or any request is issued.
806
- `parseMaterializerOptions` and `parseSyncOptions` reject an unknown key, an accessor-backed
807
- property, or a malformed nested endpoint group, then compile the remainder through the shared
808
- contract. `parseSyncBase` rejects an overlong token before URL allocation, then normalizes an
809
- endpoint to an absolute `https:` origin — plain `http:` is accepted only for loopback — and rejects
810
- embedded credentials, a query, or a fragment. `parseSyncBranch` implements the Git ref-name safety
811
- subset used in raw-guide URLs: it rejects overlong values, empty or dot-leading components, `..`,
812
- `@{`, the single `@`, trailing dots, and `.lock` suffixes without regard to case.
813
- `parseSyncCurrent` snapshots only the declared guide references, enforcing both the per-file and
814
- cumulative byte allowance. The three array parsers return frozen copies read through property
815
- descriptors, so a caller-supplied array can never smuggle in a getter. `parseSyncNames` snapshots a
816
- bounded dense array of unique npm package names and validates only the names; declaration ranges
817
- remain the responsibility of `parseSyncDependencies` and the blueprint gate.
818
-
819
- ### Shapers — core
820
-
821
- From [`shapers.ts`](../../src/core/shapers.ts).
822
-
823
- | Name | Kind |
824
- | ----------------- | -------- |
825
- | `dependencyShape` | function |
826
- | `overrideShape` | function |
827
- | `blueprintShape` | function |
828
- | `memberShape` | function |
829
- | `artifactShape` | function |
830
- | `planShape` | function |
831
- | `syncReportShape` | function |
832
-
833
- Each returns a fresh declarative contract shape that compiles into a guard, a parser, a schema, and
834
- a seeded generator. The shapes stay structural on purpose: `blueprintShape` declares `name` as a
835
- plain bounded string rather than a pattern so the generator stays satisfiable, and the
836
- `NAME_PATTERN` law lives in the semantic pass instead. Likewise `artifactShape` splits on `origin` —
837
- host artifacts may carry `source` and `hex`, content artifacts require `content` — while the
838
- lowercase byte-pair law stays a semantic refinement.
839
-
840
- ### Shapers — server
841
-
842
- From [`shapers.ts`](../../src/server/shapers.ts).
843
-
844
- | Name | Kind |
845
- | -------------------------- | -------- |
846
- | `syncGuideOptionsShape` | function |
847
- | `syncRegistryOptionsShape` | function |
848
- | `syncOptionsShape` | function |
849
- | `materializerOptionsShape` | function |
850
-
851
- The closed data-only option shapes. Every numeric option is an integer shape bounded by its own
852
- maximum constant, so an out-of-range `concurrency`, `retries`, `limit`, `items`, `budget`, or
853
- `timeout` fails at the boundary rather than deep inside a request loop.
854
-
855
- ### Contracts — server
856
-
857
- From [`contracts.ts`](../../src/server/contracts.ts).
858
-
859
- | Name | Kind |
860
- | ----------------------------- | ----- |
861
- | `syncOptionsContract` | const |
862
- | `materializerOptionsContract` | const |
863
-
864
- The compiled, closed data-only option contracts the two server parsers run their inputs through.
865
-
866
- ### Helpers — core
867
-
868
- From [`helpers.ts`](../../src/core/helpers.ts).
869
-
870
- | Name | Kind |
871
- | --------------------------- | -------- |
872
- | `dependency` | function |
873
- | `ownDataValue` | function |
874
- | `override` | function |
875
- | `member` | function |
876
- | `blueprint` | function |
877
- | `pascalCase` | function |
878
- | `escapeHtmlText` | function |
879
- | `serializeTypeScriptString` | function |
880
- | `hasApplicationBoundary` | function |
881
- | `hasApplicationShowcase` | function |
882
- | `blueprintToMembers` | function |
883
- | `catalogNames` | function |
884
- | `alignTable` | function |
885
- | `splitTableRow` | function |
886
- | `padCell` | function |
887
- | `delimiterCell` | function |
888
- | `planToSummary` | function |
889
- | `planToReview` | function |
890
- | `auditToReview` | function |
891
- | `isBehind` | function |
892
- | `syncToReview` | function |
893
- | `catalogToBlock` | function |
894
- | `inferGroup` | function |
895
- | `matchesOrchestrationPath` | function |
896
- | `diffPlan` | function |
897
- | `bytesToHex` | function |
898
- | `contentCodePoint` | function |
899
- | `contentToBytes` | function |
900
- | `contentByteLength` | function |
901
- | `contentToHex` | function |
902
- | `snapshotOf` | function |
903
- | `selectHostPaths` | function |
904
- | `findPathConflict` | function |
905
- | `findFileConflict` | function |
906
- | `validateDependencyArray` | function |
907
- | `validateBlueprint` | function |
908
- | `manifestToDependencies` | function |
909
- | `manifestToName` | function |
910
- | `rangeToFreshness` | function |
911
- | `computeHash` | function |
912
- | `stableStringify` | function |
913
- | `planPayload` | function |
914
- | `computeColumnWidth` | function |
915
- | `fitsPrintWidth` | function |
916
- | `renderArray` | function |
917
- | `renderObject` | function |
918
- | `renderValue` | function |
919
- | `renderStringArray` | function |
920
- | `formatJson` | function |
921
- | `pinPlan` | function |
922
-
923
- `dependency`, `override`, `member`, and `blueprint` are the builders. `ownDataValue` reads only an
924
- own data descriptor, so parsed JSON cannot acquire manifest fields through a polluted prototype
925
- and accessors are never invoked. Each builder omits an absent optional
926
- field entirely rather than writing `undefined`, so a built value round-trips its own exact-record
927
- guard. `blueprint` fills the defaults: `version` and `engines` from their constants, `src` to
928
- `['core']`, every other collection to empty, and every structural fact to `false`. `pascalCase`
929
- derives the entity name from a lowercase-hyphen package name, and `blueprintToMembers` derives the
930
- declared public `Member[]` — a full entity, options type, interface, and factory per published
931
- environment, plus the exact declaration inventory each selected application environment
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.
935
-
936
- `escapeHtmlText` and `serializeTypeScriptString` are the two escaping leaves used when a
937
- caller-supplied name reaches generated HTML or generated TypeScript source; the latter preserves
938
- every UTF-16 code unit, escaping lone surrogates and line separators.
939
-
940
- `alignTable` builds a formatter-width-aligned GFM table by rendering a real table node and then
941
- re-padding both the cells and the delimiter row to per-column codepoint width. `splitTableRow`,
942
- `padCell`, and `delimiterCell` are its exported leaves — the row splitter honours an escaped pipe
943
- as literal text rather than a column boundary, and `padCell` measures codepoints so a surrogate
944
- pair counts once. `catalogNames` is the mirror-image reader: it extracts `@orkestrel/<name>` package
945
- names from a catalog table by a pure line scan, and returns `[]` rather than throwing when the text
946
- has no rows.
947
-
948
- `planToSummary`, `planToReview`, `auditToReview`, `syncToReview`, and `catalogToBlock` are the
949
- lossless projections. The review documents are copy-ready markdown; `auditToReview` groups findings
950
- by drift, elides the aligned ones, and rejects an unsafe finding path outright. `catalogToBlock`
951
- deduplicates by name, sorts by code unit, prefixes a standing trust notice, and emits only the
952
- `Package` and `Version` columns — network-controlled descriptions are deliberately omitted, because
953
- that block enters agent instruction context. `isBehind` is the shared freshness predicate both
954
- report projections count with.
955
-
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.
965
- `snapshotOf`, `contentToHex`, `contentToBytes`, `contentByteLength`, `contentCodePoint`, and
966
- `bytesToHex` are the host-independent byte leaves that make exact comparison possible without a
967
- host encoder or buffer; an unpaired surrogate encodes as `U+FFFD` rather than throwing.
968
- `selectHostPaths` is the one-owner filter plan assembly applies before it carries anything: it
969
- returns the host paths in input order minus `guides/src/<name>.md`, so a workspace never plans a
970
- vendored mirror of the guide it writes itself. `findPathConflict` finds the first exact or
971
- case-insensitive collision in a path list, and `findFileConflict` additionally rejects a file that
972
- would sit inside another planned path — the loud backstop behind that selection.
973
-
974
- `validateBlueprint` and `validateDependencyArray` are the semantic pass. The array validator is
975
- pure — it returns its questions and the set of names it saw, so the caller can apply the
976
- cross-array overlap rules on top. `manifestToDependencies` reads a manifest's `dependencies`,
977
- `devDependencies`, and `peerDependencies` in that order, keeps only own data sections and scoped
978
- names, deduplicates, and never throws. `manifestToName` is its self-reading sibling over the same
979
- text: the manifest's own string `name`, or `undefined` when the text is oversized, malformed,
980
- rootless, or nameless — the projection that lets a target recognize itself in its own declared
981
- dependencies. `rangeToFreshness` applies the exact-pin comparison; the `missing` and `failed`
982
- verdicts come from the fetch layer, never from this pure comparison.
983
-
984
- `computeHash` is a deterministic FNV-1a digest and `stableStringify` a key-order-independent
985
- canonical serialization, so two logically equal blueprints hash identically. `planPayload`
986
- serializes exactly the blueprint, groups, and artifacts that establish plan identity, and `pinPlan`
987
- hashes that payload while filling an explicit `src:<selection> · app:<selection>` trace (`none`
988
- marks an empty axis). `PlanManager` compares the canonical payload whenever an
989
- id is already registered: an identical plan is idempotent, while a distinct payload with the same
990
- 32-bit digest fails closed with `ScaffoldError('INVALID', 'Plan hash collision')`.
991
- `formatJson` and its leaves — `renderValue`,
992
- `renderArray`, `renderObject`, `computeColumnWidth`, and `fitsPrintWidth` — emit JSON that matches the fleet
993
- formatter byte for byte, collapsing a short array onto one line and breaking a long one, so
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.
1000
-
1001
- ### Helpers — server
1002
-
1003
- From [`helpers.ts`](../../src/server/helpers.ts).
1004
-
1005
- | Name | Kind |
1006
- | -------------------------- | -------- |
1007
- | `isRealDirectory` | function |
1008
- | `isRealFile` | function |
1009
- | `digestFile` | function |
1010
- | `digestHex` | function |
1011
- | `digestText` | function |
1012
- | `digestHostManifest` | function |
1013
- | `guideStub` | function |
1014
- | `packageShortName` | function |
1015
- | `readGuideReferences` | function |
1016
- | `syncReportOf` | function |
1017
- | `hostRoot` | function |
1018
- | `resolveRealPath` | function |
1019
- | `resolveContainedPath` | function |
1020
- | `resolvePhysicalPath` | function |
1021
- | `validateWriteAnchor` | function |
1022
- | `createWriteDirectory` | function |
1023
- | `validateWriteDirectories` | function |
1024
- | `validateWriteTarget` | function |
1025
- | `discardWriteTransaction` | function |
1026
- | `commitWriteTransaction` | function |
1027
- | `resolveGuideWrites` | function |
1028
- | `restoreFiles` | function |
1029
- | `replaceDirectory` | function |
1030
- | `selectOrkestrelEntries` | function |
1031
- | `deriveBlueprint` | function |
1032
- | `isVacant` | function |
1033
- | `readTarget` | function |
1034
- | `readManifest` | function |
1035
- | `readHostManifest` | function |
1036
- | `readFileHex` | function |
1037
- | `readFileText` | function |
1038
- | `listFiles` | function |
1039
- | `listDirectories` | function |
1040
- | `storagePath` | function |
1041
- | `stageHost` | function |
1042
- | `locateHostSource` | function |
1043
- | `remapArtifactPath` | function |
1044
- | `hydratePlan` | function |
1045
- | `vendoredPruneSet` | function |
1046
- | `pruneTargets` | function |
1047
- | `consumeCatalogAllowance` | function |
1048
- | `discoverPackages` | function |
1049
- | `guideToDescription` | function |
1050
- | `catalogPackages` | function |
1051
-
1052
- `hostRoot` resolves this module's own installed package root — the nearest ancestor of its own file
1053
- holding a `package.json` — and returns its vendored `dist/host` directory. Walking up from the
1054
- module rather than from the working directory is what makes the default host correct once installed:
1055
- the package ships its vendored data with itself.
1056
-
1057
- `resolveRealPath`, `resolveContainedPath`, and `resolvePhysicalPath` are the containment ladder.
1058
- The first resolves the deepest existing ancestor through symlinks with bounded iterative traversal;
1059
- the second rejects any candidate that escapes its root after that resolution; the third additionally
1060
- requires every existing ancestor between the root and the destination to be a real, unlinked
1061
- directory. All three reject malformed paths before filesystem access. Containment is therefore
1062
- realpath-aware rather than merely lexical, so a symlinked subdirectory planted inside an otherwise
1063
- legitimate root cannot smuggle a write or a read outside it.
1064
-
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
1068
- bounded-memory and revalidates device, inode, size, and modification time before and after reading,
1069
- so a file swapped mid-read is a failure rather than a silent wrong digest. `readFileHex` and
1070
- `readFileText` read one contained file under the same revalidation, and the text reader decodes
1071
- strictly, rejecting invalid UTF-8. Manifest reads stop at `MAX_MANIFEST_BYTES`; catalog guide reads
1072
- stop at `MAX_GUIDE_BYTES`. `listFiles` and `listDirectories` walk a real, unlinked root under the
1073
- entry and depth bounds, returning sorted POSIX-relative paths and `[]` for an absent root.
1074
- `isRealDirectory` and `isRealFile` are the physical path predicates they all lean on.
1075
-
1076
- The write-transaction helpers are the fail-closed mutation path. `createWriteDirectory` establishes
1077
- a directory one segment at a time behind captured identities; `validateWriteAnchor`,
1078
- `validateWriteDirectories`, and `validateWriteTarget` revalidate those identities before each step;
1079
- `commitWriteTransaction` promotes a complete staged set and rolls every earlier destination back
1080
- when a later promotion fails; `discardWriteTransaction` removes the private residue of an
1081
- uncommitted or already-committed transaction; `restoreFiles` returns quarantined files to their
1082
- original paths in reverse order; and `replaceDirectory` atomically swaps a completed staging
1083
- directory for its target, preserving a recoverable backup. `resolveGuideWrites` is the sync-side
1084
- preflight: it resolves every behind-guide destination, enforces the canonical
1085
- `guides/src/<short>.md` path for its dependency name, rejects collisions, and rejects a destination
1086
- that is not a plain physical file — all before any mutation.
1087
-
1088
- `isVacant` is the green-field target law: a path is vacant when it is absent, empty, or contains
1089
- nothing but a real `.git` directory. `readTarget` reads a target's current bytes at a set of paths
1090
- into an exact-byte snapshot, mapping a directly requested directory to the empty string and
1091
- omitting an absent path entirely. `readManifest` reads `package.json` text, and
1092
- `selectOrkestrelEntries` filters a manifest field to its scoped name-and-range entries.
1093
-
1094
- `deriveBlueprint` is the faithful inverse an audit needs: it reconstructs a blueprint from an
1095
- existing workspace so a mature package is diffed against its own would-be scaffold rather than a
1096
- dependency-less stand-in. Environments come from `src/<environment>/` and `app/<environment>/`, the
1097
- three directory-shaped structural project facts from their directory probes, `global` from the
1098
- physical exact-case `tests/setupGlobal.ts` file, and `showcase` from the physical exact-case regular
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
1102
- from the manifest's scoped entries, with an optional peer recovered from
1103
- `peerDependenciesMeta`; and `extras` is every development dependency minus the complete set
1104
- `devDependenciesFor` emits for those environments and structural axes, and minus anything already
1105
- declared as a dependency or peer. An axis-emitted dependency is therefore never double-counted,
1106
- while a hand-added development dependency round-trips and stays audit-clean. Derivation yields no
1107
- `overrides`: they are caller-time inputs, not repository state. A computed artifact that must differ
1108
- reveals a gap in the canon; the blueprint grows an axis for that distinction rather than the
1109
- repository forking the file.
1110
-
1111
- `storagePath`, `stageHost`, `readHostManifest`, `locateHostSource`, `remapArtifactPath`, and
1112
- `hydratePlan` are the vendored-host path. `storagePath` maps a repo-relative path to its un-dotted
1113
- storage name, `stageHost` copies the vendored set into an output directory behind a full preflight
1114
- and an atomic swap, and `readHostManifest` reads and validates the resulting `manifest.json`,
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.
1117
- `locateHostSource` resolves one source to its storage file, `remapArtifactPath` maps a manifest
1118
- destination back onto an artifact's target prefix, and `hydratePlan` rehydrates a plan's host
1119
- artifacts with their exact bytes, expanding a directory-shaped host artifact into one artifact per
1120
- file.
1121
-
1122
- `vendoredPruneSet` establishes the allowlist for one prune directory and fails closed rather than
1123
- returning an unestablished empty set — a missing host root, or a host with neither a manifest nor
1124
- that directory, is a coded failure, while a host that genuinely vendors nothing there remains a
1125
- valid empty allowlist. `pruneTargets` is the single source of truth for prune drift: it lists the
1126
- paths under a target's prune directories that the allowlist does not declare, and it never deletes
1127
- anything.
1128
-
1129
- `consumeCatalogAllowance` decrements the single shared entry allowance and throws `TARGET` before an
1130
- over-budget traversal continues. `discoverPackages` requires a real, unlinked root and lists its
1131
- immediate child directories whose bounded manifest names a scoped package, skipping anything else
1132
- silently. A control-bearing child directory fails closed before its manifest is read and the
1133
- untrusted name is never reflected in the diagnostic. `catalogPackages` applies one allowance across
1134
- every root and directory rather than
1135
- resetting a per-root budget, then draws each description from the first paragraph of the first
1136
- blockquote of that package's own bounded guide via `guideToDescription`; a missing guide, an
1137
- unreadable or oversized one, or one with no blockquote yields an empty description rather than an
1138
- error.
1139
-
1140
- `packageShortName` strips the canonical scope, `guideStub` renders the pointer written when a
1141
- dependency guide is not vendored yet, `readGuideReferences` reads a target's existing local mirrors
1142
- for package names so synchronization verdicts are target-relative, and `syncReportOf` assembles one report from already
1143
- ordered guide and version outcomes.
1144
-
1145
- ### Compilers — core
1146
-
1147
- From [`compilers.ts`](../../src/core/compilers.ts).
1148
-
1149
- | Name | Kind |
1150
- | -------------------------- | -------- |
1151
- | `hostGroup` | function |
1152
- | `fillArtifact` | function |
1153
- | `srcVariant` | function |
1154
- | `entryFields` | function |
1155
- | `dualCondition` | function |
1156
- | `exportsMap` | function |
1157
- | `compareCodeUnit` | function |
1158
- | `devDependenciesFor` | function |
1159
- | `packageManifest` | function |
1160
- | `rootTsconfig` | function |
1161
- | `viteMachinery` | function |
1162
- | `renderViteTest` | function |
1163
- | `viteHeader` | function |
1164
- | `policyViteProject` | function |
1165
- | `configViteProject` | function |
1166
- | `guidesViteProject` | function |
1167
- | `binViteProject` | function |
1168
- | `integrationViteProject` | function |
1169
- | `serviceViteProject` | function |
1170
- | `viteProjectRegistrations` | function |
1171
- | `viteProjectDefinitions` | function |
1172
- | `singleSrcViteConfig` | function |
1173
- | `rootViteConfig` | function |
1174
- | `applicationViteConfig` | function |
1175
- | `coreTsconfig` | function |
1176
- | `coreViteConfig` | function |
1177
- | `srcTsconfig` | function |
1178
- | `srcViteConfig` | function |
1179
- | `binTsconfig` | function |
1180
- | `binViteConfig` | function |
1181
- | `appTsconfig` | function |
1182
- | `appViteConfig` | function |
1183
- | `ciWorkflow` | function |
1184
- | `configArtifacts` | function |
1185
- | `sourceArtifacts` | function |
1186
- | `applicationArtifacts` | function |
1187
- | `paritySpecifiers` | function |
1188
- | `testArtifacts` | function |
1189
- | `guideMemberTable` | function |
1190
- | `guideUsage` | function |
1191
- | `guideMethods` | function |
1192
- | `guideTests` | function |
1193
- | `guideArtifacts` | function |
1194
- | `applyOverrides` | function |
1195
- | `blueprintToPlan` | function |
1196
-
1197
- `blueprintToPlan` is the whole pure compilation: draft each selected group's artifacts, append the
1198
- host set, apply overrides, and pin. Everything above it is an exported leaf of that drafting, each
1199
- independently callable and independently tested.
1200
-
1201
- `srcVariant` classifies an `src` environment selection into its manifest variant — one environment, or
1202
- several. `entryFields`, `dualCondition`, and `exportsMap` build the manifest entry fields and the
1203
- `exports` map from that variant; a browser-only package exports a single module condition, while
1204
- core and server src get dual import and require conditions with matching declaration files.
1205
- `devDependenciesFor` emits the blueprint's complete development dependency set: the shared
1206
- baseline, package extras, dev-installed peers, selected browser toolchains, and the bin axis's
1207
- browser test provider. Extras and peers are sorted by `compareCodeUnit` so ordering is stable across
1208
- locales. `packageManifest` assembles the whole file — name, publication mode, files, scripts,
1209
- dependencies, peers and their optional metadata, and engines.
1210
-
1211
- `rootTsconfig` emits the root compiler options and one path alias per declared environment;
1212
- `coreTsconfig`, `srcTsconfig`, and `appTsconfig` emit the scoped configurations that remove the
1213
- wrong host's globals from each environment. A core scope is the interesting one: `lib` is
1214
- `["ESNext", "WebWorker"]` and `types` stays `[]`, which declares the WHATWG surface that is
1215
- identical across Node, browsers, and workers — `fetch` and its request/response/header types,
1216
- streams, `URL`, `AbortController`, the text encoders, `crypto`, timers, `console`, `DOMException`,
1217
- `structuredClone` — while leaving `document`, `window`, and every `node:*` type unresolvable. That
1218
- is one declaration set for a host-independent module, not a host. `viteHeader` renders the shared
1219
- header — the alias block
1220
- derived from the tsconfig paths, plus the environment-boundary plugin — and `viteMachinery` is the
1221
- one place the root header's axes are derived, read by `rootViteConfig`, `singleSrcViteConfig`, and
1222
- `applicationViteConfig`; `configArtifacts` delegates to those roots rather than deriving another
1223
- answer.
1224
-
1225
- **The boundary guarantees do not vary by blueprint.** Every generated `vite.config.ts` — a
1226
- `core`-only library, an application of `app/core` alone, or the full six-environment workspace —
1227
- emits `environmentBoundary`, its `resolveId` / `load` / `buildEnd` walks, the module-graph AST audit
1228
- (`environmentAssetSources`, `parseSync`, `Visitor`), and stylesheet rejection (`isStylesheetPath`
1229
- plus its `environmentPathError` / `environmentSourceError` clauses). Those enforce owner-independent
1230
- laws: core stays host-independent whatever else the workspace declares, a server module never
1231
- imports a stylesheet, and a `@vite-ignore` dynamic import — which `resolveId` never sees and the
1232
- module graph never records — has no other enforcement point in workspace-owned source. Dependency
1233
- and toolchain modules are outside that ownership boundary. Only host-specific pipelines vary,
1234
- along the three `ViteMachinery` axes:
1235
-
1236
- | Machinery | Emitted when |
1237
- | ------------------------------------------------------------------------ | ------------------------------------ |
1238
- | Shared CSS analysis (`ENVIRONMENT_CSS`, `preprocessCSS`, `isCSSRequest`) | a `src` or `app` browser environment |
1239
- | Playwright provider and managed/system browser discovery | a `src` or `app` browser environment |
1240
- | Vue plugin, HTML boundary, browser development server | an `app` browser environment |
1241
- | Output containment (`outputBoundary`, `enforceOutputPath`) | anything the workspace builds |
1242
-
1243
- An application of `app/core` alone is the sole shape that builds nothing, so it is the sole shape
1244
- without output containment — and it still carries every boundary guarantee above.
1245
-
1246
- `renderViteTest` is the single root-project renderer. It consumes ordered `ViteProjectRegistration`
1247
- data and emits either the plain project list or the browser gate, keeping source and application
1248
- root configurations byte-consistent without reconstructing browser ownership. Both forms use the
1249
- formatter's 100-column fixed point: a complete registration-array line, including indentation and
1250
- its trailing comma, stays collapsed when it fits and expands one entry per line otherwise.
1251
- `viteProjectRegistrations` is the one registration derivation every root shape consumes: it derives
1252
- the selected source and application projects from the canonical environment order, then appends
1253
- `policy`, `config`, `guides`, the optional `srcBin` and `integration` projects, and one
1254
- `service<Vendor>` project for every selected service.
1255
- `viteProjectDefinitions` renders the standalone proof and structural-fact definitions in that same
1256
- order with one blank line between declarations. Both consume `ViteFacts`, so each optional project
1257
- is controlled only by its matching `bin`, `integration`, or `services` blueprint fact; the same
1258
- slice carries `global` to integration and the source-browser compiler, and `showcase` to the
1259
- application-browser compiler, without adding another test project.
1260
-
1261
- `coreViteConfig`, `srcViteConfig`, `binViteConfig`, and `appViteConfig` emit the thin per-target
1262
- wrappers. `coreViteConfig()` is parameterless and never imports or attaches browser CSS machinery;
1263
- the root `srcCore` factory and its wrapper stay host-independent even when the workspace also owns a
1264
- browser target. `binTsconfig` emits the executable declaration scope; `rootViteConfig`,
1265
- `singleSrcViteConfig`, and `applicationViteConfig` emit the root configuration for a library-only,
1266
- single non-core `src` environment, and application-bearing workspace respectively; and
1267
- `policyViteProject`, `configViteProject`, `guidesViteProject`, `integrationViteProject`, and
1268
- `serviceViteProject` emit the standalone Node proof projects, with `binViteProject` the single
1269
- executable-project emitter. A
1270
- proof project is structurally derived from the directory holding its tests and never wraps a source
1271
- or application environment project. The guides project therefore uses only `tests/setup.ts`, never
1272
- `setupServer.ts`, `setupBrowser.ts`, or a vendor readiness module; and its `tests/src/**/*.test.ts` and
1273
- `tests/app/**/*.test.ts` exclude rows are uniform across all root shapes by design, including
1274
- core-only workspaces where one row cannot currently match. Integration and service use 120-second
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
1278
- `tests/setupGlobal.ts` for the shared template-registry harness exactly when `bin`, `integration`,
1279
- and `global` are all true. Independently, a `global` source-browser project places
1280
- `globalSetup: ['./tests/setupGlobal.ts']` immediately before its ordinary `setupFiles` row (and
1281
- after the core-test exclusion where that row exists). Application browser projects never receive
1282
- that field.
1283
-
1284
- `configArtifacts`, `sourceArtifacts`, `applicationArtifacts`, `testArtifacts`, and `guideArtifacts`
1285
- are the per-group drafters. When `bin` is selected, `configArtifacts` includes
1286
- `configs/src/tsconfig.bin.json` and `configs/src/vite.bin.config.ts` beside the declared environment
1287
- configuration pairs. When `showcase` is selected, it includes the computed thin
1288
- `configs/app/vite.showcase.config.ts` wrapper beside the ordinary application browser pair.
1289
- `paritySpecifiers` computes the self-specifier and module map the
1290
- generated parity suite resolves fence imports through. `guideMemberTable`, `guideUsage`,
1291
- `guideMethods`, and `guideTests` render the generated guide's member tables, usage examples, method
1292
- contract, and test inventory. `fillArtifact` fills one template entry into a `template`-origin
1293
- artifact with missing placeholders treated as an error, and `hostGroup` resolves which group a
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
1297
- and deliberately leaves an unmatched, host-owned, or `package.json` override unapplied, because the
1298
- gate reports it as a blocking question. `ciWorkflow` renders the generated workflow.
1299
-
1300
- ### Factories
1301
-
1302
- From [`factories.ts`](../../src/core/factories.ts) and
1303
- [`factories.ts`](../../src/server/factories.ts).
1304
-
1305
- | Name | Kind |
1306
- | -------------------- | -------- |
1307
- | `createCompiler` | function |
1308
- | `createPlanManager` | function |
1309
- | `createBlueprint` | function |
1310
- | `createMaterializer` | function |
1311
- | `createSync` | function |
1312
-
1313
- `createBlueprint` is the validating constructor: it fills the builder defaults and then checks both
1314
- the exact-record shape and the semantic pass, throwing `INVALID` when either fails. The other four
1315
- construct their entities from their options records.
1316
-
1317
- ### `Compiler`
1318
-
1319
- The compilation orchestrator, from [`Compiler.ts`](../../src/core/Compiler.ts). It runs the fixed
1320
- three-stage `draft → gate → pin` pipeline over a blueprint and owns a typed emitter whose event map
1321
- is `compile`, `audit`, `block`, `error`, and `destroy`. Both public methods are genuinely
1322
- synchronous and pure. `compile` emits `compile` only for a complete compilation and `block` for a
1323
- gated one; `audit` emits `block` when gated and then always emits `audit`, never `compile`. After
1324
- `destroy()` every method other than the getter and `destroy` itself throws `DESTROYED`, and teardown
1325
- is idempotent with the emitter destroyed last.
1326
-
1327
- ### `PlanManager`
1328
-
1329
- The versioned, content-hashed plan registry, from
1330
- [`PlanManager.ts`](../../src/core/PlanManager.ts). Its event map is `add`, `remove`, and `destroy`.
1331
- Construction parses its exact options before allocating the emitter. `add` re-pins an immutable,
1332
- detached plan snapshot and mints the record id from that content hash, so re-adding an unchanged plan
1333
- resolves to the same frozen record, a changed plan mints a fresh id, and a distinct canonical payload
1334
- with a colliding digest throws `INVALID` before mutation or emission. `remove` follows the
1335
- batch-overload convention with the array overload declared first. Its list form is all-or-nothing:
1336
- if any listed id is unregistered the collection is untouched and `false` is returned; on success all
1337
- selected records are removed before the first stable-order event, so synchronous listeners observe
1338
- the committed state and cannot create reentrant duplicate removals. After `destroy()` every method
1339
- other than the getters and `destroy` throws `DESTROYED`.
1340
-
1341
- ### `Materializer`
1342
-
1343
- The materialization entity, from [`Materializer.ts`](../../src/server/Materializer.ts) — the only
1344
- filesystem writer in the package. Its event map is `copy`, `write`, `remove`, `done`, `error`, and
1345
- `destroy`. Every call preflights completely before mutating: a structural plan match, the semantic
1346
- and contextual validation result, portable-path checks on every artifact path and source, collision
1347
- detection, destination-shape checks, and realpath-anchored containment against both the target and
1348
- the host root. Only then does staging begin, inside a private same-volume write transaction that is
1349
- promoted atomically and rolled back on any failure. After `destroy()` every method throws
1350
- `DESTROYED`.
1351
-
1352
- ### `Sync`
1353
-
1354
- The upstream-synchronization entity, from [`Sync.ts`](../../src/server/Sync.ts) — the only network
1355
- reader in the package. Its event map is `guide`, `version`, `package`, `write`, `done`, `error`, and
1356
- `destroy`. Every request runs under a per-request abort timeout and a bounded worker pool rather
1357
- than an unbounded parallel await, follows no redirects, sends no credentials, and reads its response
1358
- body incrementally against both the per-response limit and a shared cumulative allowance. The
1359
- default posture collects failures into the result as `missing` or `failed` verdicts; `strict` mode
1360
- turns those into a thrown `FETCH` naming the failing URL. `destroy()` aborts every in-flight request
1361
- and is idempotent, and every method afterwards throws `DESTROYED`.
1362
-
1363
- ### `WriteTransaction`
1364
-
1365
- The nominal, same-volume write-transaction state, from
1366
- [`WriteTransaction.ts`](../../src/server/WriteTransaction.ts). It is constructed only through its
1367
- static `create`, which derives every filesystem path from a target plus portable relative paths — a
1368
- caller can neither supply a deletion root nor mutate the captured arrays. Creation snapshots every
1369
- destination into a frozen `WriteExpectation`, verifies any supplied preconditions against what is
1370
- actually on disk, captures the parent anchor identity, and creates private staging and backup
1371
- directories with restrictive permissions. Its readonly getters — `target`, `root`, `stage`,
1372
- `backup`, `expectations`, `parents`, `directories`, `anchor`, and `existing` — are the only way to
1373
- observe it; every operation over it lives in the exported transaction helpers.
1374
-
1375
- ## Methods
1376
-
1377
- The public methods of each behavioral interface, one table per type.
1378
-
1379
- #### `CompilerInterface`
1380
-
1381
- | Method | Returns |
1382
- | --------- | ------------- |
1383
- | `compile` | `Scaffolding` |
1384
- | `audit` | `Audit` |
1385
- | `destroy` | `void` |
1386
-
1387
- `compile(blueprint, groups?)` runs the pipeline and returns a complete or visibly incomplete
1388
- `Scaffolding`; the optional group selection scopes the plan to those artifact groups.
1389
- `audit(blueprint, current, groups?)` compiles and then diffs the resulting plan against the
1390
- caller-supplied current content; a gated blueprint returns `complete: false` with the gate's
1391
- blocking questions and zero findings, and a complete one carries the gate's advisories on that same
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
1396
- `emitter`.
1397
-
1398
- #### `PlanManagerInterface`
1399
-
1400
- | Method | Returns |
1401
- | --------- | ------------------------- |
1402
- | `has` | `boolean` |
1403
- | `plan` | `PlanRecord \| undefined` |
1404
- | `plans` | `readonly PlanRecord[]` |
1405
- | `add` | `PlanRecord` |
1406
- | `remove` | `boolean \| void` |
1407
- | `destroy` | `void` |
1408
-
1409
- `has(id)` tests registration. `plan(id)` is the singular accessor and returns `undefined` for an
1410
- unregistered id; `plans()` is the plural accessor and returns a snapshot array. `add(plan)`
1411
- registers or re-registers one plan. `remove()` removes every plan and returns `void`; `remove(id)`
1412
- removes one and returns whether it existed; `remove(ids)` is all-or-nothing over a list. The
1413
- interface also exposes the readonly `emitter` and `size` properties.
1414
-
1415
- #### `MaterializerInterface`
1416
-
1417
- | Method | Returns |
1418
- | ------------- | ------------------- |
1419
- | `materialize` | `MaterializeResult` |
1420
- | `repair` | `MaterializeResult` |
1421
- | `prune` | `MaterializeResult` |
1422
- | `destroy` | `void` |
1423
-
1424
- `materialize(plan, target)` is green-field: it refuses any target `isVacant` rejects, then copies
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`.
1433
-
1434
- #### `SyncInterface`
1435
-
1436
- | Method | Returns |
1437
- | ---------- | ----------------------------------- |
1438
- | `lookup` | `Promise<readonly VersionLookup[]>` |
1439
- | `guides` | `Promise<readonly GuideSync[]>` |
1440
- | `versions` | `Promise<readonly VersionSync[]>` |
1441
- | `catalog` | `Promise<readonly CatalogEntry[]>` |
1442
- | `pull` | `Promise<SyncReport>` |
1443
- | `mirror` | `Promise<SyncReport>` |
1444
- | `write` | `Promise<readonly string[]>` |
1445
- | `destroy` | `void` |
1446
-
1447
- `lookup(names)` resolves registry versions from bare package names, with no declaration range
1448
- required or synthesized. `guides(deps, current?)` fetches each dependency's upstream guide. The optional `current` map is
1449
- keyed by dependency name: with it, a fetched guide byte-equal to its entry verdicts `current` and
1450
- anything else verdicts `behind`; without it, every successful fetch verdicts `behind`, because no
1451
- reference means it needs syncing. `versions(deps)` compares each declared range to the registry
1452
- latest. `catalog()` enumerates the fleet from the registry's exact organization package list — an
1453
- unreachable or malformed list is always a coded failure, since without it there is no catalog — then
1454
- degrades gracefully per package. `pull(target, dependencies?)` builds the reference map from the
1455
- target's own mirrors, so its verdicts are target-relative, and rejects a selection the target does
1456
- not declare. `mirror(target)` reuses the exact organization enumeration without catalog's
1457
- per-package packument reads, sorts the names, excludes the target's own manifest name, and builds a
1458
- guide-only report with no versions. `write(report, target)` commits only the `behind` guides. `destroy()` aborts every
1459
- in-flight request. The interface also exposes the readonly `emitter`.
1460
-
1461
- ## The compile pipeline
1462
-
1463
- `compile` runs three stages in fixed order and records each as a `CompileRecord` carrying its input,
1464
- its output, whether it failed, and any error text.
1465
-
1466
- 1. **draft** — `blueprintToPlan` selects the covered groups, drafts each group's artifacts, carries
1467
- the selected host set — every vendored host path except the workspace's own guide — applies
1468
- overrides, and pins the draft. A throw here records a `draft` failure coded
1469
- `INVALID`, emits `error`, marks the remaining two stages skipped, and returns incomplete.
1470
- 2. **gate** — `validatePlan` runs the semantic pass over the blueprint and checks every override
1471
- against the drafted artifact set. Blocking questions fail the stage; an accepted override and a
1472
- dependency outside the vendored guide set each contribute a non-blocking advisory question
1473
- instead.
1474
- 3. **pin** — a host-origin pointer artifact is appended for each non-vendored dependency, and
1475
- `pinPlan` fills `trace` and `hash` from the plan's own content.
1476
-
1477
- The gate fails closed. A blueprint that fails validation, or that carries an override matching no
1478
- planned artifact or targeting a host-origin path, yields a visible incomplete `Scaffolding` — `plan`
1479
- absent, `questions` populated, a `BLOCKED` failure marker recorded — rather than throwing and rather
1480
- than emitting a half-formed workspace. A half-formed workspace is worse than a question.
1481
-
1482
- `validateBlueprint` is the semantic law in one place. It checks the name against `NAME_PATTERN` and
1483
- the length bound, the version and engines patterns, and that the declared engines floor is not below
1484
- the supported Node minimum. It requires at least one selected environment across the two axes, keeps
1485
- both axes on-vocabulary with no repeats, and blocks the one combination that has no defined
1486
- configuration class: `browser` plus `server` without `core` in the same axis. It validates each
1487
- dependency array
1488
- for a well-formed name and range with no duplicates — scoped names for `dependencies` and `peers`,
1489
- any valid npm name for `extras` — and blocks a name declared in two of the three arrays. It bounds
1490
- the description and every override by the per-item and aggregate byte limits, and blocks a repeated
1491
- or empty override path.
1492
-
1493
- Because `pinPlan` derives `hash` from a canonical, key-order-independent serialization of the
1494
- blueprint, groups, and artifacts, two logically equal blueprints built in different field orders
1495
- produce the same digest — and a `PlanManager` id is that digest.
1496
-
1497
- ## Origin and ownership
1498
-
1499
- `origin` is the ownership axis, and it decides everything downstream: how an artifact is produced,
1500
- how it is audited, and whether it may ever be overwritten.
1501
-
1502
- - **`host`** — byte-copied from the vendored data root. These are the shared files a whole fleet
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.
1508
- - **`template`** — filled from a frozen template definition by a pure fill engine. These are
1509
- starter files: source stubs, test stubs, the starter guide, the README.
1510
- - **`computed`** — derived by this package's own combination logic. These are the structural files:
1511
- the manifest, the tsconfigs, the build configuration, the generated CI workflow.
1512
-
1513
- Audit semantics follow directly from that.
1514
-
1515
- - A **template** artifact is birth-only and audit-exempt. It is always reported `aligned`, whatever
1516
- the target holds. Starter files are written once and are legitimately outgrown — real code
1517
- replaces the stub, a hand-authored guide replaces the scaffold prose, an entity gets renamed.
1518
- Comparing a mature workspace against its birth stub is a category error, and it would make any
1519
- unscoped repair a data-loss hazard. Template findings therefore never contribute to the drifted,
1520
- missing, or clean tallies.
1521
- - A **computed** artifact is content-aware canon: `missing`, `aligned`, or `stale`, and it gates the
1522
- audit like any other drift.
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.
1535
-
1536
- The same ownership boundary is what makes mutation safe. **`fleet` and default `repair` both scope
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.
1549
- Audit always compares it because computed artifacts are content-aware canon. A legitimate
1550
- difference that the blueprint cannot express is a canon gap: add the missing blueprint axis rather
1551
- than forking the computed file in one repository.
1552
-
1553
- Overrides respect the same boundary from the other direction. `applyOverrides` never replaces a
1554
- host-origin artifact and never replaces `package.json`; the gate turns either attempt — and an
1555
- override matching no planned artifact at all — into a blocking question rather than a silent no-op.
1556
- What survives those three refusals is applied and announced: the gate carries a non-blocking
1557
- advisory naming each replaced path, and that advisory rides the `Scaffolding` and the `Audit` all
1558
- the way through the library result.
1559
-
1560
- Guide mirrors are the one place ownership is conditional, and the law is one owner per guide path.
1561
- **A workspace mirrors every line guide except its own.** When the name matches — the guide package
1562
- on `guides/src/guide.md`, this package on `guides/src/scaffold.md` — the workspace itself is the
1563
- owner, keeping that path as its **template**-origin starter guide, and `selectHostPaths` drops the
1564
- vendored mirror so the path is contributed exactly once. For every other contract the mirror is the
1565
- owner: a dependency this package vendors a byte-identical mirror for gets a real host-origin copy of
1566
- `guides/src/<short>.md`, contributed once whether it arrives through the host set or through the
1567
- dependency, so a package depending on `@orkestrel/guide` plans one `guides/src/guide.md` rather than
1568
- two. Any other dependency gets a host-origin _pointer_ artifact plus a non-blocking question, never
1569
- a fabricated mirror; on materialization that pointer degrades to a short stub, and `scaffold pull`
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
1575
- `findFileConflict` is its backstop: two artifacts at one path refuse the plan rather than racing to
1576
- be the last writer.
1577
-
1578
- ## Audit, repair, and prune
1579
-
1580
- An audit is a pure function of a plan and a snapshot, so the same engine that creates a workspace
1581
- checks one. `readTarget` supplies the snapshot as exact bytes; `diffPlan` returns findings as data;
1582
- `auditToReview` renders them for a human. Nothing in that path writes.
1583
-
1584
- The executable's physical unexpected-file scan treats exactly `scripts/service.sh` as an expected
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.
1588
-
1589
- `repair` turns those findings back into the narrowest possible write. It re-reads the target,
1590
- re-diffs it, and refuses to proceed if the findings changed since the preview it was given — a
1591
- target that moved under the caller is a `TARGET` failure, not a race to win. It then derives a write
1592
- precondition per artifact from the audit itself: a `missing` finding requires the destination to
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.
1636
-
1637
- `prune` is the deletion arm, and it is deliberately narrow. Its candidate set comes from
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
1642
- positively established from the vendored host, or the call fails closed rather than treating an
1643
- unresolved host as "vendors nothing" and proposing to delete everything. Each candidate is verified
1644
- as a plain physical file whose bytes still match the preview, moved into a private quarantine rather
1645
- than unlinked, re-verified after the move, and only then reported as removed — with a full restore
1646
- attempt if any candidate fails mid-way.
1647
-
1648
- ## Upstream sync, pull, mirror, and catalog
1649
-
1650
- `Sync` is the only network reader, and its posture is conservative by construction.
1651
-
1652
- Every request is unauthenticated: no token, no authorization header, anywhere. Every fleet
1653
- repository is public, so plain reachability is the only signal, and a guide `404` degrades
1654
- gracefully instead of needing credentials. Redirects are never followed — a 3xx, or the opaque
1655
- response a manual redirect policy produces, is treated as a distinct named transport fault, so a
1656
- compromised or misconfigured endpoint cannot silently redirect cross-host. Guide URLs are therefore
1657
- built in their canonical form directly rather than relying on a redirect to reach it.
1658
-
1659
- Concurrency is bounded by a worker pool over a shared cursor, never an unbounded parallel await. The
1660
- pool preserves input order, stops issuing new work after the first error, awaits every worker so a
1661
- sibling rejection is always observed, and then rethrows the first error. Response bodies are read
1662
- incrementally against both the per-response limit and a shared cumulative allowance; a declared
1663
- oversized content length short-circuits before any read. An oversized body is a transport fault like
1664
- any other — retry-eligible, then `failed`, or a thrown `FETCH` under `strict`.
1665
-
1666
- Every non-clean outcome carries a `note` explaining the cause: a transport error message with the
1667
- underlying socket code appended when the runtime attaches one, an HTTP status, the fixed
1668
- redirect-blocked string, or the oversized-body message. `current` and `behind` carry no note,
1669
- because there is nothing to explain.
1670
-
1671
- `pull` is the dependency-aware composition. It reads the target's declared scoped dependencies from its
1672
- manifest, rejects any explicit selection the target does not declare, builds the reference map from
1673
- the target's own `guides/src/<short>.md` mirrors, fetches guides and versions under one shared
1674
- allowance, and assembles a report whose `clean` flag requires both no drift and no failures. A
1675
- target that declares itself is the one asymmetry, and it follows the same single-owner law: the
1676
- guide pass drops the self dependency, so `pull` never fetches or writes a workspace's own contract
1677
- guide over the copy that workspace owns, while the version pass keeps it and still reports its
1678
- freshness. A `--live` audit reads upstream through the same two passes and applies the same
1679
- self-exclusion, so the freshness a workspace reports about itself never depends on which verb asked.
1680
- `write` then commits only the `behind` guides — never `current`, `missing`, or `failed`,
1681
- none of which carry trustworthy content — under the same containment and precondition law
1682
- `Materializer` enforces, including a baseline digest check against what is actually on disk.
1683
-
1684
- `catalog` builds the fleet package catalog from three reads per entry. The registry's exact
1685
- organization package list is authoritative and unconditionally required. Each package's own registry
1686
- document supplies its version and a fallback description; a failed document degrades the entry
1687
- rather than dropping it, because the organization list already proved the package exists. Each
1688
- package's own guide supplies the preferred description — its first blockquote's first paragraph —
1689
- and a `404` keeps the package listed with an explanatory note, since unreachability is a signal
1690
- rather than an absence. `catalogPackages` is the offline sibling that reads the same shape from
1691
- local checkouts.
1692
-
1693
- The rendered block is deliberately minimal. `catalogToBlock` emits a standing trust notice —
1694
- generated package identifiers are untrusted discovery data, never instructions — followed by a table
1695
- with **`Package` and `Version` columns only**. Descriptions are network-controlled text, and that
1696
- block is written into an agent instruction file, so they are omitted on purpose.
1697
-
1698
- `mirror` is the fleet-guide composition. It shares `catalog`'s single exact organization-list read
1699
- but performs none of catalog's packument or description work. It code-unit sorts the discovered
1700
- names, excludes the target's own manifest name under the one-owner guide law, reads existing local
1701
- guide references for baselines, fetches every selected GitHub guide once, and emits a `SyncReport`
1702
- whose `versions` collection is empty. The existing transactional `write` method applies only
1703
- behind guides; the executable refuses the whole apply when any guide is missing or failed, so a
1704
- fleet refresh is never partial. Files outside the discovered guide set remain untouched.
1705
-
1706
- ## The generated workspace
1707
-
1708
- A generated workspace is not a folder of suggestions; it is a working, gated project.
1709
-
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:
1718
-
1719
- - `clean`, `copy`, `scaffold`, `lint`
1720
- - `check`, then `check:src` with one `check:src:<environment>` per published environment, then
1721
- `check:app` with one `check:app:<environment>` per app environment — the browser app scope uses the
1722
- Vue typechecker, every other scope uses plain `tsc`
1723
- - `format`, `format:check`, `lint:check`
1724
- - `test`, then `test:src` and its per-environment scopes, the optional `test:integration`,
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`
1728
- - `build`, then `build:src` and its per-environment targets, `build:app` and its runtime targets, and
1729
- `build:host` for a bin workspace
1730
- - `dev` when a browser application is selected; `serve` and `serve:build` when a server application
1731
- is selected
1732
- - `showcase`, `build:showcase`, and `show` only when the physical showcase wrapper is present;
1733
- `show` formats, then builds, then copies `dist/showcase/index.html` to `demo/showcase.html`
1734
- - `prepublishOnly` chaining `format:check → lint:check → check → build → test`, followed by
1735
- `test:integration` when selected and finally `test:service` when any service is selected
1736
-
1737
- **Proof gating.** The opt-in proofs are predictable from the axes alone. `test:integration` rides
1738
- the `integration` axis and `test:service` a nonempty `services` axis, while `test:equivalence` is emitted
1739
- only where `bin` and `integration` are both set:
1740
-
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.
1753
- The showcase is likewise outside `build`, `test`, and `prepublishOnly`; it is an explicit projection
1754
- of `app/browser`, not an environment, test-project row, or source/demo artifact.
1755
- Its copied `demo/showcase.html` is generated and minified, so the mirrored `.prettierignore` keeps it
1756
- outside the whole-tree formatter while source and configuration files remain fully gated.
1757
-
1758
- When a prerequisite is absent the proof fails rather than skipping. `test:integration` reads the
1759
- workspace's own built output, so it belongs after `build` — which is exactly where `prepublishOnly`
1760
- and CI put it. `test:service` refuses to start against an unprovisioned service: its setup throws at
1761
- module load, which is why CI runs `bash scripts/service.sh` immediately before it. And a script the
1762
- axes do not emit is simply not there: `test:equivalence` in a workspace that is not both `bin` and
1763
- `integration` is an unknown script rather than a quietly passing one.
1764
-
1765
- The equivalence proof is a dual-path re-run rather than a separate suite. Run
1766
- `npm run test:equivalence` after changing the persistent boundary build driver; it invokes the
1767
- integration project in dual-path mode and proves each programmatic driver verdict against the
1768
- spawned npm-script reference. Ordinary integration runs keep the faster driver-only path.
1769
-
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.
1789
-
1790
- The audit expects the script rather than reporting it foreign, on the derive-time warrant the audit
1791
- section gives. Repair pruning applies the same exclusion, so it never proposes or removes that
1792
- required workspace-owned provisioner.
1793
-
1794
- **Environment isolation.** Scoped TypeScript projects remove the wrong host's globals from each
1795
- environment: core scopes carry the WHATWG web-interop surface and no host at all — no DOM, no Node,
1796
- no `vite/client`; browser scopes carry DOM and no Node; server scopes carry Node and no DOM. The
1797
- worker-only globals the `WebWorker` declarations would otherwise admit — `name`, `onrtctransform`,
1798
- `close`, `postMessage`, `dispatchEvent`, `location`, `onerror`, `onlanguagechange`, `onoffline`,
1799
- `ononline`, `onrejectionhandled`, `onunhandledrejection`, `self`, `importScripts`, `fonts`, `caches`,
1800
- `crossOriginIsolated`, `indexedDB`, `isSecureContext`, `origin`, `scheduler`, `createImageBitmap`,
1801
- `reportError`, `cancelAnimationFrame`, `requestAnimationFrame`, `onmessage`, `onmessageerror`,
1802
- `addEventListener`, and `removeEventListener` — are fenced out of `src/core` and `app/core` sources
1803
- by the policy suite, so the declarations widen what a host-independent module may call without
1804
- widening where it may run. On every TypeScript bump, derive this list from the module-scope
1805
- global-object `declare var` and `declare function` declarations in the installed
1806
- `lib.webworker.d.ts`, then subtract values supplied by `lib.esnext*` or current Node globals. Lint
1807
- restricts declared package, alias, and conventional relative imports in the same directions.
1808
- Neither replaces the other, and neither replaces the build.
1809
-
1810
- **The generated build boundary.** The emitted configuration carries an environment-boundary plugin
1811
- that resolves the real module graph rather than re-implementing a parser. **TypeScript and
1812
- JavaScript references are read through Vite's own Oxc/Rolldown AST**; **Vue single-file components
1813
- are read through the official SFC compiler**, block by block, including `src`-referenced blocks;
1814
- **CSS dependencies are parsed by Vite's bundled Lightning CSS analyzer**; and **HTML attributes,
1815
- entities, candidate lists, and metadata use Vite's own HTML parser callbacks**. The plugin runs at
1816
- resolve and transform time, checks the finished module graph at build end, rescans every emitted
1817
- JavaScript chunk's remaining dynamic imports after optimization and tree-shaking, and audits every
1818
- emitted asset's physical source path. Source-level asset URLs are checked before Vite transforms
1819
- them, so generated runtime `new URL(...)` expressions are not mistaken for caller input. HTML
1820
- `vite-ignore` tokens are reversibly encoded as an HTML character reference before Vite parses the
1821
- document, so the attribute cannot opt an element out of Vite's normal HTML graph while the same
1822
- text inside a resource URL still decodes to its original filename before resolution. Existing
1823
- equivalent character references are shifted before encoding and unshifted afterward, which keeps
1824
- comments, text, raw blocks, attributes, adjacent tokens, casing, and user-authored entity spelling
1825
- byte-stable. The trusted preparation hook owns the final pre-parse phase; inline proxy code is
1826
- restored before module analysis, and the first normal post-parse hook restores the original HTML
1827
- spelling. The browser entry begins with a generated, byte-stable security prologue: the doctype,
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
1838
- before parsing because Vite performs that expansion after every plugin pre-hook, where it could
1839
- otherwise create a late control attribute. The guard walks the exact left-to-right `%(\S+?)%`
1840
- tokens Vite recognizes instead of performing a substring search, and each preparation plugin owns
1841
- the resolved environment/definition keys for its configuration, so one build cannot contaminate
1842
- another and overlapping percent text remains ordinary text. Read environment values from the
1843
- application's module graph through `import.meta.env` instead.
1844
- Asset URLs that force `?inline` are rejected before Vite can read them outside that auditable output
1845
- graph. Dynamic imports must use a static quoted string or expression-free template string; even
1846
- `/* @vite-ignore */` static values repeat the same environment and containment checks inside the
1847
- transform boundary, including inline HTML proxy modules. The transform, load, resolution, emitted
1848
- asset, and finished-module-graph passes apply that law only to workspace-owned `src/*` and `app/*`
1849
- modules. Resolved ids under any `node_modules` segment, Vite/Vitest virtual ids, and tooling client
1850
- injections remain owned by their toolchain and are exempt.
1851
-
1852
- Browser application scripts are modules. Vite's parsed HTML asset callback rejects a classic
1853
- external `<script src>` before resolution and directs the author to `type="module"`. A module
1854
- script URL must be a non-empty local Vite-graph URL: schemes, protocol-relative URLs, data URLs,
1855
- fragments, surrounding URL whitespace, and ASCII C0 controls or DEL are rejected rather than left
1856
- as unaudited browser loads. This is deliberately broader than the URL parser's edge stripping.
1857
- Numeric
1858
- HTML character references and semicolon-terminated named references are rejected before resolution,
1859
- so neither control references nor entity-built scheme characters can bypass the boundary. Vite can
1860
- begin resolving an entity-decoded module URL before its parsed per-asset callback runs; that earlier
1861
- path remains Vite-owned and passes through the environment resolver, which rejects the same full
1862
- ASCII control range and every non-Node URL scheme before loading or output. No second HTML parser or global
1863
- reference rewrite is involved, so comments, text, non-script attributes, and entity-spelled asset
1864
- filenames retain Vite's native parsing and resolution behavior.
1865
- The resolver leaves NUL-prefixed and `virtual:` Rolldown/Vite module IDs, tooling client injections,
1866
- and every resolved `node_modules` module to the tool that owns that namespace; author module and
1867
- asset URLs are extracted and validated before they reach those resolver exceptions.
1868
- SVG script `href` and `xlink:href` attributes are parsed too and rejected as classic script loads.
1869
- Inline module scripts enter Vite's HTML proxy graph and receive the same Oxc boundary analysis as
1870
- module files. Classic inline scripts cannot enter that graph, so the required security prologue places
1871
- `Content-Security-Policy` before every author-controlled document token with `script-src 'self'`
1872
- and `script-src-attr 'none'`: inline classic code and inline event handlers cannot execute, while
1873
- Vite's same-origin external module entry remains usable. `appBrowser()` accepts no configuration
1874
- arguments. The returned Vite configuration is one closed trusted unit: its Vue and boundary
1875
- plugins, CSS analyzer, dependency optimizer, environment, builder, output pipeline, and HTML asset
1876
- callbacks cannot be extended or replaced through the factory. This deliberately excludes arbitrary
1877
- Vite, Rolldown, esbuild, PostCSS, worker, environment, builder, externalization, output-injection,
1878
- and URL-rewrite hooks that could mutate a dependency, worker graph, bundle, or final asset after
1879
- the boundary has inspected it. The computed root Vite configuration is trusted generated code:
1880
- wrapping, mutating, or replacing the object returned by `appBrowser()` is outside the factory
1881
- contract and is reported as computed-artifact drift by `scaffold audit`. The output-boundary plugin
1882
- still rejects public directories, browser asset inlining, and output path overrides in a
1883
- post-factory composition as defense in depth; that narrow check is not a general extension seam.
1884
-
1885
- When the showcase fact is present, the generated root also exports closed
1886
- `appShowcase(...config: never[])`; both factories reject every argument at runtime. The
1887
- ordinary factory retains its strict
1888
- `script-src 'self'` policy, external asset auditing, and `dist/app/browser` output. The showcase
1889
- factory is a standalone configuration with `base: './'`, unlimited asset inlining, and
1890
- `dist/showcase` output. It applies `viteSingleFile` with
1891
- `removeViteModuleLoader: true` and `useRecommendedBuildConfig: true`, uses Oxc and Lightning CSS
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.
1911
-
1912
- The browser development server applies the same trust boundary before Vite's internal middleware.
1913
- Its explicit filesystem allowlist contains only browser/core source roots, browser tests, their
1914
- exact setup files, and installed dependencies. The pre-internal middleware decodes direct,
1915
- alias-shaped, and `/@fs/` requests, resolves existing targets through their physical paths, and
1916
- returns a path-free 403 response unless the target remains in one of those roots. It also rejects
1917
- an allowed root whose physical identity escapes the workspace, so neither a nested symlink nor a
1918
- linked root can expose `app/server`, `src/server`, repository metadata, or unrelated files.
1919
-
1920
- What it allows is deliberately real-world:
1921
-
1922
- - safe stylesheet `@import`s and `url()` assets;
1923
- - HTML-referenced assets, including candidate lists, inline style blocks, and inline module scripts;
1924
- - static `new URL('./asset', import.meta.url)` asset references;
1925
- - static-string dynamic imports whose decoded source passes the same environment and containment law.
1926
-
1927
- What it rejects is equally deliberate:
1928
-
1929
- - a published `src/*` module reaching into private `app/*`;
1930
- - a core module reaching a stylesheet, a browser module, a server module, a Node builtin, or a
1931
- browser or server package subpath;
1932
- - a browser module reaching a Node builtin or a server subpath;
1933
- - a server module reaching a stylesheet, Vue, or a browser subpath;
1934
- - a workspace-relative import that resolves outside the workspace;
1935
- - an HTML reference carrying `vite-ignore` that violates the same environment or containment law
1936
- as an ordinary reference, a Vite `%ENV%` HTML substitution, a classic external script, or a
1937
- computed dynamic import in the module graph that would bypass graph resolution;
1938
- - a computed or expression-bearing `new URL` asset source that could escape at runtime;
1939
- - malformed URI encoding, encoded traversal segments, or a local `file:` URL outside the owning
1940
- environment/package root; file schemes are matched case-insensitively and converted to physical
1941
- paths before containment.
1942
-
1943
- Unsupported stylesheet `@import` or `url()` syntax is an error rather than a silently skipped
1944
- dependency. **`publicDir` is disabled on every generated build target**: an asset that is not
1945
- reachable through the module graph is not silently copied past the boundary. The output plugin
1946
- fails during configuration when a caller attempts to enable `publicDir`. Published `srcBrowser`
1947
- targets use Vite library mode, where assets are always inlined and `assetsInlineLimit` is ignored,
1948
- so their generated shapes omit that ineffective option. The normal `appBrowser` build retains
1949
- `assetsInlineLimit: 0`, and the output plugin rejects a nonzero limit only for that non-library
1950
- browser build, keeping application asset bytes external and visible to output auditing before any
1951
- output directory mutation. A caller-supplied Rolldown `output.dir` or `output.file` is also rejected
1952
- during configuration; the exact generated `build.outDir` is the sole write root.
1953
-
1954
- **The policy suite.** [`tests/setupPolicy.ts`](../../tests/setupPolicy.ts) is a narrow structural
1955
- policy pass built on the official TypeScript compiler. It exists for exactly the laws a linter
1956
- cannot express — that a centralized module exports every top-level declaration it holds, that
1957
- implementation files hold one class and no stray module-scope declaration, that no function is
1958
- declared inside another function outside a directly-passed callback, that interface properties are
1959
- readonly, that privacy is a runtime `#` field rather than a TypeScript modifier, that a barrel
1960
- re-exports only through `export *`, that a core source never names a worker-only global the
1961
- `WebWorker` declarations expose, and that a computed dynamic import cannot smuggle a
1962
- cross-environment dependency past the declared import rules. Vue components are inspected for the
1963
- same evasions. A self-contained runtime entrypoint may be exempt from module-scope placement only
1964
- when it is not a centralized kind file and has at least one real `node:` value import. Erased
1965
- type-only imports may reference sibling contracts; any non-`node:` static value import,
1966
- `export … from` re-export, or dynamic `import(...)` disqualifies the exemption. An importless file
1967
- does not qualify, and centralized declarations remain subject to their export law. Every other
1968
- policy law still applies. It is a complement to lint and typecheck, never a second type system, and
1969
- it is not a general-purpose source analyzer. Generated workspaces receive the same exported policy
1970
- module as a host-origin file and run it as a dedicated Node-only `policy` test project over
1971
- `tests/policy.test.ts`.
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
-
1987
- **Real browser capability.** Browser test projects are gated on one centralized discovery chain:
1988
- Playwright's pinned Chromium executable first, then a managed Chromium alias or cached revision,
1989
- then stable system Chrome, then stable system Edge. Managed candidates must be executable regular
1990
- files. System channels are selected only when their executable exists at Playwright's standard
1991
- Linux, macOS, or Windows installation location; custom installations are not guessed. The generated
1992
- configuration test consumes the same discovery helpers and accepts either an executable managed path or the
1993
- stable `chrome` / `msedge` channel, so it does not maintain a second heuristic.
1994
-
1995
- A browser suite runs when any one of those real browser capabilities is available and is skipped
1996
- honestly when none is, rather than being faked. The gate is applied at registration, not inside the
1997
- real browser project: without a browser, each browser factory is replaced by a same-label
1998
- Node/no-test placeholder, so generated `--project <label>` and `--project=<label>` filters still
1999
- resolve while no browser code runs. The root permits an empty run only when every recognized exact
2000
- project filter names one of those gated placeholders; an unreadable or mixed filter keeps the
2001
- ordinary no-test failure semantics for its Node projects. One printed warning names every gated
2002
- project label and says no Playwright Chromium, Chrome, or Edge was found. A machine with a browser
2003
- registers and runs the real browser suites unchanged; a machine without one runs the remaining
2004
- projects and says so.
2005
-
2006
- **Consumer-owned global setup.** The single mechanism-named `tests/setupGlobal.ts` module may
2007
- prepare a shared integration registry, a real Node-side counterpart for source-browser tests such
2008
- as a WebSocket fixture server, or both. The scaffold does not emit or replace it. Derivation
2009
- records its exact-case physical presence as `global`, the single governing fact. Integration
2010
- consumes it only when `bin` and `integration` are also true; a declared `src/browser` independently
2011
- wires it to `srcBrowser`. Removing the file removes both eligible rows from regenerated
2012
- configuration byte-for-byte. Application browser, styles, and service readiness setup remain
2013
- isolated from this seam.
2014
-
2015
- **Continuous integration.** The generated workflow runs on push and pull request, on
2016
- `ubuntu-latest`, with read-only contents permission, a 60-minute timeout, and a matrix that **tests
2017
- Node `22.12.0` and `26`** with fail-fast disabled. Checkout and Node setup are pinned to immutable
2018
- action commits, and checkout does not persist credentials. Dependencies install with
2019
- `npm ci --ignore-scripts`; Chromium is installed only when the workspace selects a browser environment
2020
- or builds its own executable. The gates then run in order: `format:check`, `lint:check`, `check`,
2021
- `build`, `test`, and the workspace's selected proofs follow as their own named steps, in the order
2022
- the proof-gating table gives them.
2023
-
2024
- **Agent orchestration files.** The session hooks in the generated `.claude/settings.json` run the
2025
- dependency, model, and external-tool readiness scripts at session start. The **`Stop` hook runs only
2026
- `git diff --check`** — a whitespace and conflict-marker check over the working tree, nothing more.
2027
- Bash invocation and sensitive reads are controlled by the **settings permission list, not by a guard
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
2035
- orchestrator supplies their diff and status evidence. Bridge, writer, and verifier roles request
2036
- approval when their bounded shell work is needed. Read patterns covering environment files,
2037
- package-manager credentials, credential stores, private keys, key stores, SSH, cloud credentials,
2038
- container configuration, `.kube`, kubeconfig, and service-account JSON are denied. There is no
2039
- guard script in the vendored set, and none is expected.
2040
-
2041
- The generated `.codex/config.toml` and `.codex/agents/` mirror the same bounded research, design,
2042
- implementation, checking, and review roles for Codex. Codex has no repository settings/hook file:
2043
- each Codex agent's declared `sandbox_mode` is its mechanical permission floor, while the shared
2044
- `AGENTS.md`, rules, and skills provide the same writing and acceptance contract to both providers.
2045
-
2046
- ## The `scaffold` executable
2047
-
2048
- The bin is a thin command-line shell over the two library faces. It exports nothing, so it carries
2049
- no module API of its own. Seven verbs:
2050
-
2051
- | Verb | Purpose |
2052
- | --------- | -------------------------------------------------------- |
2053
- | `new` | scaffold a workspace into `./<name>` |
2054
- | `pull` | refresh vendored guides and versions, report drift |
2055
- | `mirror` | refresh every published Orkestrel package guide |
2056
- | `audit` | whole-plan conformance report |
2057
- | `repair` | restore missing canon; optionally replace drifted bytes |
2058
- | `fleet` | audit or repair every workspace under the cwd's children |
2059
- | `catalog` | regenerate the fleet package-catalog table |
2060
-
2061
- **Environment selection.** `new` takes `--src a,b` for published library environments and
2062
- `--app a,b` for private application environments. They are independent: `--src core,server` builds a library,
2063
- `--app core,browser,server` builds an application, and passing both builds a mixed workspace. Each
2064
- accepts any subset of `core`, `browser`, and `server`, and the gate rejects the one combination that
2065
- has no defined configuration class. `--deps x,y` adds runtime dependencies and requires each flag
2066
- token to use its full valid package name; only the interactive dependency prompt expands an
2067
- Orkestrel short name. Other npm packages are not a creation-time flag — add them to the generated
2068
- manifest's development dependencies afterwards, and they round-trip through `deriveBlueprint`'s
2069
- extras so the workspace stays audit-clean.
2070
-
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.
2075
- On `pull`, `--deps x,y` limits refresh to those declared Orkestrel dependencies; without it, every
2076
- declared dependency mirror is considered.
2077
- `mirror` accepts no dependency selection: its exact npm organization discovery is the operation's
2078
- scope, and it fetches guides without registry version or packument requests.
2079
- `--groups a,b` scopes an audit to artifact groups. `--live` adds an upstream freshness check to an
2080
- audit. `--strict` makes a pull or mirror throw on a network fault. `--offline` restricts a catalog to local
2081
- sources. `--prune` opts a repair or fleet run into deleting unexpected files under the three prune
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.
2087
- `--json` emits one machine-readable value. `--apply` writes, `--yes` skips the confirmation, and
2088
- `-h` or `--help` prints usage.
2089
-
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
2093
- confined to the working directory, so the instruction is to change into it first rather than to pass
2094
- a root. `repair` asks a second, separately defaulted question before deleting anything, and a
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.
2107
-
2108
- **Catalog markers.** `catalog` rewrites the block between `<!-- catalog:start -->` and
2109
- `<!-- catalog:end -->` in `CATALOG_AGENT_PATH`. **Ambiguous markers fail before any
2110
- mutation**: the file must contain exactly one ordered pair. A missing marker, a reversed pair, or a
2111
- repeated marker of either kind is a coded `TARGET` failure raised before the file is touched, and
2112
- the run reports the drift and any row-count shrink rather than rewriting a file it cannot bound.
2113
-
2114
- **Certificates.** **When the running Node release exposes the system-CA APIs**, the executable
2115
- merges the operating system trust store into the default certificates, so fetches behind a
2116
- TLS-inspecting proxy behave like other tooling instead of failing against the bundled list alone.
2117
- The check is a feature detection: **earlier supported Node 22 releases simply use Node's default
2118
- roots**. It only ever adds trusted issuers — nothing disables verification — and a failure is a
2119
- silent no-op rather than a crash. Custom PEMs are added through the standard environment variable.
2120
-
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
2131
- suggestion when one is sufficiently close.
2132
-
2133
- ## Package contents
2134
-
2135
- The published package is `@orkestrel/scaffold`. Its entry points are the core barrel at `.` and the
2136
- server barrel at `./server`, both with dual import and require conditions and matching declaration
2137
- files, plus `./package.json`. The `scaffold` binary maps to the built executable.
2138
-
2139
- The published file set is exactly `dist/src`, `dist/bin`, `dist/host`, and `README.md`. `dist/host`
2140
- is the vendored data root: the byte-preserved host files plus the `manifest.json` recording their
2141
- storage names, destinations, executable bits, directory roots, and membership digest. Storage names
2142
- are un-dotted, because a leading dot
2143
- does not survive packaging intact; the manifest is what maps a storage name back to its real
2144
- destination. That is also why the default host is resolved from the installed module's own
2145
- location — the package carries its host data with itself, and a caller-supplied raw repository root
2146
- is the explicit alternative, mapping sources 1:1 with no manifest indirection.
2147
-
2148
- Six runtime dependencies, all scoped: the contract toolkit behind the shape, guard, parser, and
2149
- safe-attempt primitives; the emitter behind every entity's observation channel; the markdown AST and
2150
- renderer behind the table and blockquote work; the template engine behind every template-origin
2151
- artifact; and, consumed only at the executable boundary, the terminal prompt toolkit and the console
2152
- reporter. The core face uses the first four and stays pure; the server face adds only `node:*`
2153
- builtins. Development dependencies are the shared tooling baseline plus the guide-parity toolkit
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
2158
- `>=22.12.0`, and the build emits ES and CJS for both library faces plus an ES executable.
2159
-
2160
- ## Patterns
2161
-
2162
- ### Authoring and validating a blueprint
2163
-
2164
- ```ts
2165
- import {
2166
- blueprint,
2167
- blueprintToMembers,
2168
- createBlueprint,
2169
- dependency,
2170
- hasBlueprintEnvironment,
2171
- hasValidBlueprintBytes,
2172
- hasValidOverrideBytes,
2173
- isWorkspaceName,
2174
- member,
2175
- override,
2176
- pascalCase,
2177
- validateBlueprint,
2178
- validateDependencyArray,
2179
- } from '@orkestrel/scaffold'
2180
-
2181
- const spec = blueprint('router', {
2182
- src: ['core', 'browser'],
2183
- dependencies: [dependency('@orkestrel/contract', '^0.0.7')],
2184
- peers: [dependency('@orkestrel/server', '^0.0.3', true)],
2185
- overrides: [override('README.md', '# router\n')],
2186
- })
2187
-
2188
- pascalCase('my-router') // 'MyRouter'
2189
- isWorkspaceName('router') // true
2190
- hasBlueprintEnvironment(spec) // true
2191
- hasValidBlueprintBytes(spec) // true
2192
- hasValidOverrideBytes(override('README.md', '# router\n')) // true
2193
- validateDependencyArray('dependencies', spec.dependencies).questions // []
2194
- validateBlueprint(spec).valid // true
2195
- blueprintToMembers(spec)[0] // { name: 'Router', category: 'entity', … }
2196
- member('RouterOptions', 'type', 'Options for creating a Router.')
2197
-
2198
- // The validating constructor throws instead of returning questions.
2199
- createBlueprint({ name: 'router', src: ['core'] })
2200
- ```
2201
-
2202
- ### Compiling, gating, and pinning
2203
-
2204
- ```ts
2205
- import {
2206
- applyOverrides,
2207
- blueprint,
2208
- blueprintToPlan,
2209
- computeHash,
2210
- createCompiler,
2211
- hasValidArtifactBytes,
2212
- hasValidArtifactHex,
2213
- hasValidPlanBytes,
2214
- hasValidPlanHex,
2215
- pinPlan,
2216
- planPayload,
2217
- stableStringify,
2218
- validatePlan,
2219
- } from '@orkestrel/scaffold'
2220
-
2221
- const compiler = createCompiler()
2222
- const spec = blueprint('router', { src: ['core'] })
2223
-
2224
- const scaffolding = compiler.compile(spec)
2225
- scaffolding.stages.map((record) => record.stage) // ['draft', 'gate', 'pin']
2226
-
2227
- const audit = compiler.audit(spec, {})
2228
- audit.missing // every artifact — nothing exists at the target yet
2229
-
2230
- const plan = pinPlan(blueprintToPlan(spec, ['manifest', 'configs']))
2231
- validatePlan(plan).valid // true
2232
- plan.trace?.includes('src:core · app:none') // true
2233
- planPayload(plan) === planPayload({ ...plan, trace: 'ignored by identity' }) // true
2234
- hasValidPlanHex(plan) // true
2235
- hasValidPlanBytes(plan) // true
2236
- plan.artifacts.every(hasValidArtifactHex) // true
2237
- plan.artifacts.every(hasValidArtifactBytes) // true
2238
- computeHash(stableStringify(plan.blueprint)) === computeHash(stableStringify(spec)) // true
2239
- applyOverrides(plan.artifacts, spec.overrides).length // unchanged when nothing matches
2240
-
2241
- compiler.destroy()
2242
- ```
2243
-
2244
- ### Registering plans by content hash
2245
-
2246
- ```ts
2247
- import { blueprint, blueprintToPlan, createPlanManager } from '@orkestrel/scaffold'
2248
-
2249
- const plans = createPlanManager()
2250
- const record = plans.add(blueprintToPlan(blueprint('router', { src: ['core'] })))
2251
-
2252
- record.id === record.hash // true — the id is minted from content
2253
- record.version // 1
2254
- plans.has(record.id) // true
2255
- plans.plan(record.id) // the record
2256
- plans.plans().length // 1
2257
- plans.remove([record.id]) // true — all-or-nothing over a list
2258
- plans.remove() // removes everything
2259
- plans.destroy()
2260
- ```
2261
-
2262
- ### Projecting a plan, an audit, and a report
2263
-
2264
- ```ts
2265
- import type { Audit, Plan, SyncReport } from '@orkestrel/scaffold'
2266
- import {
2267
- alignTable,
2268
- auditToReview,
2269
- catalogNames,
2270
- catalogToBlock,
2271
- delimiterCell,
2272
- guideMemberTable,
2273
- padCell,
2274
- planToReview,
2275
- planToSummary,
2276
- splitTableRow,
2277
- syncToReview,
2278
- } from '@orkestrel/scaffold'
2279
-
2280
- declare const plan: Plan
2281
- declare const audit: Audit
2282
- declare const report: SyncReport
2283
-
2284
- alignTable(['API', 'Kind'], [['`createRouter`', 'function']])
2285
- splitTableRow('| a | b |') // ['a', 'b']
2286
- padCell('ab', 5) // 'ab '
2287
- delimiterCell('left', 5) // ':----'
2288
-
2289
- catalogToBlock([{ name: '@orkestrel/router', version: '0.0.5', description: '' }])
2290
- catalogNames('| @orkestrel/router | 0.0.5 |') // ['@orkestrel/router']
2291
-
2292
- planToSummary(plan).artifacts // the artifact count
2293
- planToReview(plan) // the copy-ready dry-run review document
2294
- auditToReview(audit) // findings grouped by drift, aligned entries elided
2295
- syncToReview(report) // guides and versions, each in its own table
2296
- guideMemberTable('entity', [])
2297
- ```
2298
-
2299
- ### Exact bytes, snapshots, and drift
2300
-
2301
- ```ts
2302
- import type { Plan } from '@orkestrel/scaffold'
2303
- import {
2304
- bytesToHex,
2305
- contentByteLength,
2306
- contentCodePoint,
2307
- contentToBytes,
2308
- contentToHex,
2309
- diffPlan,
2310
- findFileConflict,
2311
- findPathConflict,
2312
- hasValidAuditBytes,
2313
- hasValidSnapshotBytes,
2314
- inferGroup,
2315
- snapshotOf,
2316
- } from '@orkestrel/scaffold'
2317
-
2318
- declare const plan: Plan
2319
-
2320
- contentCodePoint('a', 0) // 97
2321
- contentByteLength('ab') // 2
2322
- bytesToHex(contentToBytes('ab')) === contentToHex('ab') // true
2323
-
2324
- const current = snapshotOf({ 'package.json': '{}\n' })
2325
- hasValidSnapshotBytes(current) // true
2326
-
2327
- const audit = diffPlan(plan, current)
2328
- hasValidAuditBytes(audit) // true
2329
- inferGroup('src/core/index.ts') // 'source'
2330
-
2331
- findPathConflict(['a/b.ts', 'A/B.ts']) // the first case-insensitive collision
2332
- findFileConflict(['a', 'a/b.ts']) // a file nested inside another planned path
2333
- ```
2334
-
2335
- ### Format-stable JSON and generated text
2336
-
2337
- ```ts
2338
- import {
2339
- compareCodeUnit,
2340
- computeColumnWidth,
2341
- escapeHtmlText,
2342
- fitsPrintWidth,
2343
- formatJson,
2344
- renderArray,
2345
- renderObject,
2346
- renderValue,
2347
- serializeTypeScriptString,
2348
- } from '@orkestrel/scaffold'
2349
-
2350
- formatJson({ lib: ['ESNext', 'DOM'] }) // '{\n\t"lib": ["ESNext", "DOM"]\n}\n'
2351
- renderValue('ESNext', '', '', '') // '"ESNext"'
2352
- renderArray(['ESNext', 'DOM'], '', '', '') // '["ESNext", "DOM"]'
2353
- renderObject({ lib: ['ESNext'] }, '') // '{\n\t"lib": ["ESNext"]\n}'
2354
- computeColumnWidth('\t"a"') // 3
2355
- fitsPrintWidth('\t["ESNext"],') // true
2356
-
2357
- escapeHtmlText('<app & "team">') // '&lt;app &amp; &quot;team&quot;&gt;'
2358
- serializeTypeScriptString("app's") // "'app\\'s'"
2359
- const sorted = ['b', 'a'].sort(compareCodeUnit) // ['a', 'b']
2360
- ```
2361
-
2362
- ### Shapes, guards, and parsers
2363
-
2364
- ```ts
2365
- import {
2366
- artifactShape,
2367
- blueprintShape,
2368
- dependencyShape,
2369
- hasValidSyncReportBytes,
2370
- isArtifact,
2371
- isBlueprint,
2372
- isCompilerEventHooks,
2373
- isDependency,
2374
- isMember,
2375
- isOverride,
2376
- isPlan,
2377
- isPlanManagerEventHooks,
2378
- isScaffoldError,
2379
- isSyncReport,
2380
- memberShape,
2381
- overrideShape,
2382
- ownDataValue,
2383
- parseBoundedJSON,
2384
- parseCompilerOptions,
2385
- parseBlueprint,
2386
- parsePlan,
2387
- parsePlanIds,
2388
- parsePlanManagerOptions,
2389
- parseSyncReport,
2390
- planShape,
2391
- ScaffoldError,
2392
- snapshotPlan,
2393
- syncReportShape,
2394
- } from '@orkestrel/scaffold'
2395
-
2396
- declare const value: unknown
2397
-
2398
- dependencyShape()
2399
- overrideShape()
2400
- blueprintShape()
2401
- memberShape()
2402
- artifactShape()
2403
- planShape()
2404
- syncReportShape()
2405
-
2406
- isDependency({ name: '@orkestrel/contract', range: '^0.0.7' }) // true
2407
- isOverride({ path: 'README.md', content: '# router\n' }) // true
2408
- isMember({ name: 'Router', category: 'entity', summary: 'The Router entity.', environment: 'core' })
2409
- isArtifact({ path: 'README.md', group: 'docs', origin: 'template', content: '# router\n' })
2410
- ownDataValue({ name: 'router' }, 'name') // 'router'
2411
-
2412
- parseBoundedJSON('"ready"', (candidate): candidate is string => typeof candidate === 'string', 7)
2413
- parseCompilerOptions({ on: { destroy: () => undefined } })
2414
- parseBlueprint('{"not":"a blueprint"}') // undefined — never throws
2415
- parsePlan(undefined) // undefined
2416
- parsePlanIds(['first', 'second']) // frozen owned ids
2417
- parsePlanManagerOptions({ plans: [] }) // exact owned constructor options
2418
- parseSyncReport('{}') // undefined
2419
- const parsedPlan = parsePlan(value)
2420
- if (parsedPlan !== undefined) Object.isFrozen(snapshotPlan(parsedPlan).blueprint)
2421
-
2422
- if (isBlueprint(value)) value.src
2423
- if (isPlan(value)) value.artifacts
2424
- isCompilerEventHooks({ compile: () => undefined }) // true
2425
- isPlanManagerEventHooks({ add: (id) => id.length > 0 }) // true
2426
- if (isSyncReport(value)) hasValidSyncReportBytes(value)
2427
-
2428
- try {
2429
- throw new ScaffoldError('INVALID', 'Blueprint failed the exact-record contract')
2430
- } catch (error) {
2431
- if (isScaffoldError(error)) error.code // 'INVALID'
2432
- }
2433
- ```
2434
-
2435
- ### Drafting artifacts group by group
2436
-
2437
- ```ts
2438
- import {
2439
- applicationArtifacts,
2440
- blueprint,
2441
- blueprintToMembers,
2442
- ciWorkflow,
2443
- configArtifacts,
2444
- devDependenciesFor,
2445
- dualCondition,
2446
- entryFields,
2447
- exportsMap,
2448
- fillArtifact,
2449
- guideArtifacts,
2450
- guideMethods,
2451
- guideTests,
2452
- guideUsage,
2453
- hostGroup,
2454
- packageManifest,
2455
- paritySpecifiers,
2456
- selectHostPaths,
2457
- sourceArtifacts,
2458
- srcVariant,
2459
- testArtifacts,
2460
- } from '@orkestrel/scaffold'
2461
-
2462
- const spec = blueprint('router', { src: ['core'], app: ['core', 'server'] })
2463
- const members = blueprintToMembers(spec)
2464
-
2465
- hostGroup('AGENTS.md') // 'docs'
2466
- selectHostPaths(['guides/src/router.md', 'LICENSE'], spec.name) // ['LICENSE'] — never its own guide
2467
- srcVariant(['core', 'server']) // 'multi'
2468
- entryFields(['browser']).main // './dist/src/browser/index.js'
2469
- dualCondition('./dist/src/core/index')
2470
- exportsMap(['core'])['.']
2471
- devDependenciesFor(spec).typescript
2472
- packageManifest(spec) // the whole manifest, newline-terminated
2473
-
2474
- configArtifacts(spec).length
2475
- sourceArtifacts(spec, 'Router').length
2476
- applicationArtifacts(spec).length
2477
- testArtifacts(spec, 'Router').length
2478
- guideArtifacts(spec, 'Router', members).length
2479
- paritySpecifiers(spec).includes('SELF_SPECIFIERS') // true
2480
- guideUsage(spec, 'Router')
2481
- guideMethods(spec)
2482
- guideTests(spec, 'Router')
2483
- ciWorkflow(spec).includes("node: ['22.12.0', '26']") // true
2484
-
2485
- fillArtifact('README.md', 'docs', 'readme', {
2486
- name: 'router',
2487
- title: '@orkestrel/router',
2488
- description: 'A tiny hash router.',
2489
- install: '',
2490
- usage: '',
2491
- })
2492
- ```
2493
-
2494
- ### Emitting the generated build and check configuration
2495
-
2496
- ```ts
2497
- import {
2498
- appTsconfig,
2499
- appViteConfig,
2500
- applicationViteConfig,
2501
- binViteProject,
2502
- configViteProject,
2503
- coreTsconfig,
2504
- coreViteConfig,
2505
- guidesViteProject,
2506
- integrationViteProject,
2507
- policyViteProject,
2508
- renderViteTest,
2509
- rootTsconfig,
2510
- rootViteConfig,
2511
- serviceViteProject,
2512
- singleSrcViteConfig,
2513
- srcTsconfig,
2514
- srcViteConfig,
2515
- viteHeader,
2516
- viteMachinery,
2517
- viteProjectDefinitions,
2518
- viteProjectRegistrations,
2519
- } from '@orkestrel/scaffold'
2520
-
2521
- rootTsconfig(['core'], ['core', 'server'])
2522
- coreTsconfig()
2523
- srcTsconfig('server')
2524
- appTsconfig('browser', true)
2525
-
2526
- viteMachinery(['core']) // { browser: false, vue: false, output: true, showcase: false }
2527
- viteMachinery([], ['core', 'browser']) // { browser: true, vue: true, output: true, showcase: false }
2528
- renderViteTest([{ project: 'srcCore' }], false).includes('projects: [srcCore]') // true
2529
- viteHeader(viteMachinery([], ['core', 'browser'])) // the shared header, with browser and Vue support
2530
- coreViteConfig()
2531
- srcViteConfig('browser')
2532
- appViteConfig('server')
2533
- policyViteProject()
2534
- configViteProject()
2535
- guidesViteProject()
2536
- binViteProject()
2537
- integrationViteProject({ bin: true, integration: true, global: true })
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']
2546
-
2547
- rootViteConfig(['core', 'server'], { bin: true })
2548
- singleSrcViteConfig('server').includes('srcServer') // true
2549
- applicationViteConfig([], ['core', 'server']).includes('appServer') // true
2550
- ```
2551
-
2552
- ### Reading declared dependencies and comparing freshness
2553
-
2554
- ```ts
2555
- import {
2556
- isBehind,
2557
- manifestToDependencies,
2558
- manifestToName,
2559
- rangeToFreshness,
2560
- } from '@orkestrel/scaffold'
2561
- import {
2562
- guideStub,
2563
- packageShortName,
2564
- readGuideReferences,
2565
- syncReportOf,
2566
- } from '@orkestrel/scaffold/server'
2567
-
2568
- manifestToDependencies('{"dependencies":{"@orkestrel/contract":"^0.0.7"}}')
2569
- manifestToName('{"name":"@orkestrel/router"}') // '@orkestrel/router' — the target's own name
2570
- rangeToFreshness('^0.0.7', '0.0.7') // 'current'
2571
- isBehind(rangeToFreshness('^0.0.7', '0.0.9')) // true
2572
-
2573
- packageShortName('@orkestrel/contract') // 'contract'
2574
- guideStub('guides/src/contract.md') // the local pointer content
2575
- readGuideReferences('./packages/router', ['@orkestrel/contract'])
2576
- syncReportOf('./packages/router', [], []) // { clean: true, failed: 0, … }
2577
- ```
2578
-
2579
- ### Materializing, repairing, and pruning a target
2580
-
2581
- ```ts
2582
- import { blueprint, blueprintToPlan, diffPlan } from '@orkestrel/scaffold'
2583
- import {
2584
- createMaterializer,
2585
- digestHostManifest,
2586
- hostRoot,
2587
- hydratePlan,
2588
- isVacant,
2589
- locateHostSource,
2590
- readHostManifest,
2591
- readManifest,
2592
- readTarget,
2593
- remapArtifactPath,
2594
- stageHost,
2595
- storagePath,
2596
- } from '@orkestrel/scaffold/server'
2597
-
2598
- const host = hostRoot()
2599
- digestHostManifest([], []) // exact empty manifest membership digest
2600
- readHostManifest(host) // the vendored manifest, or undefined for a raw root
2601
- storagePath('.claude/agents/reviewer.md') // 'claude/agents/reviewer.md'
2602
- locateHostSource(undefined, 'package.json', host)
2603
-
2604
- const plan = hydratePlan(blueprintToPlan(blueprint('router', { src: ['core'] })), host)
2605
- remapArtifactPath(
2606
- { path: '.claude/agents', group: 'orchestration', origin: 'host' },
2607
- '.claude/agents',
2608
- )
2609
-
2610
- const materializer = createMaterializer()
2611
- isVacant('./packages/router-new') // true — absent, empty, or only a .git dir
2612
- materializer.materialize(plan, './packages/router-new')
2613
-
2614
- readManifest('./packages/router')
2615
- const current = readTarget(
2616
- './packages/router',
2617
- plan.artifacts.map((artifact) => artifact.path),
2618
- )
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
2621
- materializer.prune('./packages/router', {})
2622
- materializer.destroy()
2623
-
2624
- stageHost(process.cwd(), 'dist/host').length // the number of files staged
2625
- ```
2626
-
2627
- ### Pulling guides and versions
2628
-
2629
- ```ts
2630
- import { createSync } from '@orkestrel/scaffold/server'
2631
-
2632
- const sync = createSync({ concurrency: 4, retries: 1 })
2633
-
2634
- await sync.lookup(['@orkestrel/contract'])
2635
- const report = await sync.pull('.')
2636
- if (report.failed === 0) await sync.write(report, '.')
2637
-
2638
- const deps = [{ name: '@orkestrel/contract', range: '^0.0.7' }]
2639
- await sync.guides(deps)
2640
- await sync.versions(deps)
2641
- await sync.catalog()
2642
-
2643
- const mirror = await sync.mirror('.')
2644
- if (mirror.failed === 0) await sync.write(mirror, '.')
2645
-
2646
- sync.destroy()
2647
- ```
2648
-
2649
- Refresh the entire published guide mirror from an installed package:
2650
-
2651
- ```sh
2652
- npx scaffold mirror --apply --yes
2653
- ```
2654
-
2655
- Or from this checkout after building:
2656
-
2657
- ```sh
2658
- node ./dist/bin/scaffold.js mirror --apply --yes
2659
- ```
2660
-
2661
- ### Fleet discovery, prune scanning, and the local catalog
2662
-
2663
- ```ts
2664
- import {
2665
- catalogPackages,
2666
- consumeCatalogAllowance,
2667
- deriveBlueprint,
2668
- discoverPackages,
2669
- guideToDescription,
2670
- isRealDirectory,
2671
- isRealFile,
2672
- listDirectories,
2673
- listFiles,
2674
- pruneTargets,
2675
- selectOrkestrelEntries,
2676
- vendoredPruneSet,
2677
- } from '@orkestrel/scaffold/server'
2678
-
2679
- const catalogAllowance = new Float64Array([2])
2680
- consumeCatalogAllowance(catalogAllowance, './packages') // one aggregate slot remains
2681
- discoverPackages('./packages') // every scoped workspace directly under the root
2682
- deriveBlueprint('./packages/router') // the faithful inverse an audit diffs against
2683
- selectOrkestrelEntries({ '@orkestrel/contract': '^0.0.7', vite: '^8.1.5' })
2684
-
2685
- isRealDirectory('./packages/router')
2686
- isRealFile('./packages/router/package.json')
2687
- listFiles('./packages/router/.claude/agents')
2688
- listDirectories('./packages/router/.claude')
2689
-
2690
- vendoredPruneSet('./dist/host', '.claude/agents')
2691
- pruneTargets('./packages/router', './dist/host') // never deletes; reports only
2692
-
2693
- guideToDescription('> A tiny hash router.\n>\n> More detail.') // 'A tiny hash router.'
2694
- catalogPackages(['./packages'], 4_096)
2695
- ```
2696
-
2697
- ### The write-transaction boundary
2698
-
2699
- ```ts
2700
- import {
2701
- commitWriteTransaction,
2702
- createWriteDirectory,
2703
- digestFile,
2704
- digestHex,
2705
- digestText,
2706
- discardWriteTransaction,
2707
- readFileHex,
2708
- readFileText,
2709
- replaceDirectory,
2710
- resolveContainedPath,
2711
- resolveGuideWrites,
2712
- resolvePhysicalPath,
2713
- resolveRealPath,
2714
- restoreFiles,
2715
- validateWriteAnchor,
2716
- validateWriteDirectories,
2717
- validateWriteTarget,
2718
- WriteTransaction,
2719
- } from '@orkestrel/scaffold/server'
2720
-
2721
- resolveRealPath('./packages/router/src')
2722
- resolveContainedPath('./packages/router', 'src/core/index.ts', 'TARGET', 'target')
2723
- const full = resolvePhysicalPath('./packages/router', 'package.json', 'TARGET', 'target')
2724
-
2725
- digestText(readFileText('./packages/router', 'package.json', 'TARGET', 'target')) ===
2726
- digestFile(full)
2727
- digestHex(readFileHex('./packages/router', 'package.json', 'TARGET', 'target'))
2728
-
2729
- const transaction = WriteTransaction.create('./packages/router', ['package.json'])
2730
- validateWriteAnchor(transaction.anchor, 'anchor')
2731
- validateWriteDirectories(transaction)
2732
- validateWriteTarget(transaction, undefined)
2733
- createWriteDirectory(transaction.stage, 'staging')
2734
-
2735
- try {
2736
- commitWriteTransaction(transaction, ['package.json'])
2737
- } catch {
2738
- restoreFiles(transaction, ['package.json'])
2739
- discardWriteTransaction(transaction)
2740
- }
2741
-
2742
- replaceDirectory('./staged', './target', './backup')
2743
- resolveGuideWrites([], './packages/router') // preflighted destinations, before any write
2744
- ```
2745
-
2746
- ### Server boundary parsing and guards
2747
-
2748
- ```ts
2749
- import { hasOnlyDataProperties, isDenseDataArray, isEmitterErrorHandler } from '@orkestrel/scaffold'
2750
- import {
2751
- isCatalogAllowance,
2752
- isCatalogDescription,
2753
- isDependencyData,
2754
- isFilesystemPath,
2755
- isHostManifest,
2756
- isManifestEntry,
2757
- isMaterializerEventHooks,
2758
- isMissingPathError,
2759
- isPortablePath,
2760
- isReservedTargetPath,
2761
- isSensitiveHostPath,
2762
- isSyncEventHooks,
2763
- isTerminalText,
2764
- isWritePrecondition,
2765
- materializerOptionsContract,
2766
- materializerOptionsShape,
2767
- parseFilesystemPaths,
2768
- parseMaterializerOptions,
2769
- parsePortablePaths,
2770
- parseSyncBase,
2771
- parseSyncBranch,
2772
- parseSyncCurrent,
2773
- parseSyncDependencies,
2774
- parseSyncNames,
2775
- parseSyncOptions,
2776
- parseWritePreconditions,
2777
- syncGuideOptionsShape,
2778
- syncOptionsContract,
2779
- syncOptionsShape,
2780
- syncRegistryOptionsShape,
2781
- } from '@orkestrel/scaffold/server'
2782
-
2783
- declare const caught: unknown
2784
-
2785
- syncGuideOptionsShape()
2786
- syncRegistryOptionsShape()
2787
- syncOptionsShape()
2788
- materializerOptionsShape()
2789
- syncOptionsContract.parse({ concurrency: 4 })
2790
- materializerOptionsContract.parse({ host: './dist/host' })
2791
-
2792
- parseSyncOptions({ guides: { branch: 'main' }, registry: { timeout: 5_000 } })
2793
- parseMaterializerOptions({ host: './dist/host' })
2794
- parseSyncBase('registry.npmjs.org') // 'https://registry.npmjs.org'
2795
- parseSyncBranch('main')
2796
- parseSyncCurrent({ '@orkestrel/contract': '# contract\n' }, ['@orkestrel/contract'], 16_777_216)
2797
- parseSyncNames(['@orkestrel/contract', 'zod'])
2798
- parseSyncDependencies([{ name: '@orkestrel/contract', range: '^0.0.7' }], false)
2799
- parsePortablePaths(['src/core/index.ts'], 1_000)
2800
- parseFilesystemPaths(['./packages'], 1_000)
2801
- parseWritePreconditions([{ path: 'package.json', shape: 'absent' }], 1_000)
2802
-
2803
- isPortablePath('src/core/index.ts') // true
2804
- isFilesystemPath('./packages/router') // true
2805
- isTerminalText('router') // true
2806
- isDependencyData({ name: '@orkestrel/contract', range: '^0.0.7' }) // true
2807
- isSensitiveHostPath('.env.local') // true
2808
- isReservedTargetPath('.git/config') // true
2809
- isCatalogAllowance(new Float64Array([1])) // true
2810
- isCatalogDescription('A tiny hash router.') // true
2811
- hasOnlyDataProperties({ a: 1 }) // true
2812
- isDenseDataArray(['a'], 10, isPortablePath) // true
2813
- isWritePrecondition({ path: 'package.json', shape: 'absent' }) // true
2814
- isManifestEntry({ storage: 'AGENTS.md', destination: 'AGENTS.md', executable: false }) // true
2815
- isHostManifest({
2816
- entries: [],
2817
- roots: [],
2818
- digest: 'f98e1531d9fd8fab7e301d1cc944249913d93f48c918a11a753048b877211679',
2819
- }) // true
2820
- isSyncEventHooks({ done: () => undefined }) // true
2821
- isMaterializerEventHooks({ done: () => undefined }) // true
2822
- isEmitterErrorHandler(() => undefined) // true
2823
- isMissingPathError(caught) // true only for an ENOENT error
2824
- ```
2825
-
2826
- ## Tests
2827
-
2828
- - [`tests/src/core/helpers.test.ts`](../../tests/src/core/helpers.test.ts) — the pure leaves: table
2829
- alignment, byte encoding, snapshots, host selection, conflicts, projections, hashing, and
2830
- format-stable JSON.
2831
- - [`tests/src/core/builders.test.ts`](../../tests/src/core/builders.test.ts) — the blueprint,
2832
- dependency, override, and member builders, including optional-field omission.
2833
- - [`tests/src/core/validators.test.ts`](../../tests/src/core/validators.test.ts) — every guard and
2834
- refinement against valid, off-contract, hostile, and boundary input.
2835
- - [`tests/src/core/shapers.test.ts`](../../tests/src/core/shapers.test.ts) — per-shape guard
2836
- exactness, schema essentials, seeded generation, and parse round-trips.
2837
- - [`tests/src/core/compilers.test.ts`](../../tests/src/core/compilers.test.ts) — every drafted
2838
- group, the manifest and exports combination rules, the one-owner guide law for a workspace that
2839
- names a line guide, and the emitted configuration text.
2840
- - [`tests/src/core/Compiler.test.ts`](../../tests/src/core/Compiler.test.ts) — the three-stage
2841
- pipeline, the fail-closed gate, the emission sequences, and post-destroy behavior.
2842
- - [`tests/src/core/PlanManager.test.ts`](../../tests/src/core/PlanManager.test.ts) — content-hash
2843
- ids, the batch-overload semantics, and all-or-nothing list removal.
2844
- - [`tests/src/core/policy.test.ts`](../../tests/src/core/policy.test.ts) — the repository coding-law
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.
2848
- - [`tests/src/server/helpers.test.ts`](../../tests/src/server/helpers.test.ts) — containment,
2849
- digests, host staging, hydration, derivation, prune scanning, and the local catalog.
2850
- - [`tests/src/server/validators.test.ts`](../../tests/src/server/validators.test.ts) — the portable
2851
- path law, data-only reflection, and the exact-shape record guards.
2852
- - [`tests/src/server/Materializer.test.ts`](../../tests/src/server/Materializer.test.ts) —
2853
- green-field writes, scoped repair, prune quarantine and rollback, and every fail-closed preflight.
2854
- - [`tests/src/server/Sync.test.ts`](../../tests/src/server/Sync.test.ts) — freshness verdicts,
2855
- bounded concurrency, redirect and oversize handling, strict mode, pull, write, and catalog against
2856
- protocol-faithful fixture servers.
2857
- - [`tests/src/server/integration.test.ts`](../../tests/src/server/integration.test.ts) — the whole
2858
- compile, materialize, audit, repair round trip against real directories.
2859
- - [`tests/src/bin/helpers.test.ts`](../../tests/src/bin/helpers.test.ts) — the executable's rendered
2860
- verdicts, tables, notes, and suggestion machinery.
2861
- - [`tests/src/bin/parsers.test.ts`](../../tests/src/bin/parsers.test.ts) — argument parsing, token
2862
- splitting, and pull-selection resolution against a target's declared dependencies.
2863
- - [`tests/src/bin/validators.test.ts`](../../tests/src/bin/validators.test.ts) — the verb
2864
- vocabulary.
2865
- - [`tests/src/bin/errors.test.ts`](../../tests/src/bin/errors.test.ts) — the executable's exit
2866
- signalling.
2867
- - [`tests/src/bin/scaffold.test.ts`](../../tests/src/bin/scaffold.test.ts) — each verb's dry-run,
2868
- confirm, apply, and JSON paths.
2869
- - [`tests/src/bin/e2e.test.ts`](../../tests/src/bin/e2e.test.ts) — the built executable driven end
2870
- to end over real directories.
2871
- - [`tests/guides/src/parity.test.ts`](../../tests/guides/src/parity.test.ts) — this guide against
2872
- the two barrels: every export documented, every documented symbol real, every interface method
2873
- matched, every documented function exampled, and every link resolvable.
2874
-
2875
- ## See also
2876
-
2877
- - [`AGENTS.md`](../../AGENTS.md) — the coding contract every generated workspace inherits.
2878
- - [`README.md`](../README.md) — the guides index.
2879
- - [`contract.md`](contract.md) — the shape, guard, parser, and outcome primitives the blueprint and
2880
- plan contracts compile through.
2881
- - [`emitter.md`](emitter.md) — the observation channel every entity here composes.
2882
- - [`markdown.md`](markdown.md) — the AST and renderer behind the table and blockquote work.
2883
- - [`template.md`](template.md) — the pure fill engine behind every template-origin artifact.
2884
- - [`terminal.md`](terminal.md) and [`console.md`](console.md) — the prompt and reporter toolkits
2885
- consumed only at the executable boundary.
2886
- - [`guide.md`](guide.md) — the guides-parity toolkit this guide is checked with.