@orkestrel/scaffold 0.0.23 → 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.
- package/README.md +84 -99
- package/dist/bin/main.js +1094 -0
- package/dist/bin/main.js.map +1 -0
- package/dist/host/CLAUDE.md +3 -1
- package/dist/host/agents/orchestration.md +61 -4
- package/dist/host/agents/skills/orkestrel-align-packages/SKILL.md +1 -1
- package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +7 -5
- package/dist/host/agents/skills/orkestrel-harden-package/SKILL.md +1 -1
- package/dist/host/agents/skills/orkestrel-harden-package/references/contract.md +1 -1
- package/dist/host/claude/agents/orkestrel.md +4 -4
- package/dist/host/claude/rules/architecture.md +45 -3
- package/dist/host/claude/rules/quality.md +4 -0
- package/dist/host/claude/rules/tests.md +57 -1
- package/dist/host/claude/rules/workspace.md +50 -17
- package/dist/host/codex/agents/orkestrel.toml +1 -1
- package/dist/host/configs/helpers.ts +762 -0
- package/dist/host/dotfiles/oxlintrc.json +2 -1
- package/dist/host/guides/scaffold.md +862 -0
- package/dist/host/manifest.json +40 -33
- package/dist/host/tests/config.test.ts +544 -0
- package/dist/host/tests/policy.test.ts +46 -0
- package/dist/host/tests/setupPolicy.ts +529 -701
- package/dist/src/core/index.cjs +3568 -10576
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +2361 -2800
- package/dist/src/core/index.d.ts +2361 -2800
- package/dist/src/core/index.js +3512 -10440
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +2855 -3765
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +1915 -1330
- package/dist/src/server/index.d.ts +1915 -1330
- package/dist/src/server/index.js +2812 -3680
- package/dist/src/server/index.js.map +1 -1
- package/package.json +16 -23
- package/dist/bin/scaffold.js +0 -1896
- package/dist/bin/scaffold.js.map +0 -1
- package/dist/host/guides/src/scaffold.md +0 -2922
- /package/dist/host/guides/{src/guide.md → guide.md} +0 -0
|
@@ -1,2922 +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. `srcViteConfig` takes the environment plus the blueprint's `name` and `src`, because
|
|
1265
|
-
its face declaration has to reach `src/core` through a specifier the published tarball carries.
|
|
1266
|
-
`bundleTypes` rolls the face up through API Extractor, which leaves each core re-export behind a
|
|
1267
|
-
relative `../core/index.ts` path no consumer can resolve; a workspace declaring `core` therefore
|
|
1268
|
-
emits a `beforeWriteFile` rewrite turning that path into `@orkestrel/<name>`, its own published root
|
|
1269
|
-
export. The rewrite matches the final face roll-up only — applying it to the intermediate
|
|
1270
|
-
declarations makes API Extractor analyse `src/core`'s source and abort — and a workspace with no
|
|
1271
|
-
`core` emits no rewrite at all. `binTsconfig` emits the executable declaration scope;
|
|
1272
|
-
`rootViteConfig`,
|
|
1273
|
-
`singleSrcViteConfig`, and `applicationViteConfig` emit the root configuration for a library-only,
|
|
1274
|
-
single non-core `src` environment, and application-bearing workspace respectively; and
|
|
1275
|
-
`policyViteProject`, `configViteProject`, `guidesViteProject`, `integrationViteProject`, and
|
|
1276
|
-
`serviceViteProject` emit the standalone Node proof projects, with `binViteProject` the single
|
|
1277
|
-
executable-project emitter. A
|
|
1278
|
-
proof project is structurally derived from the directory holding its tests and never wraps a source
|
|
1279
|
-
or application environment project. The guides project therefore uses only `tests/setup.ts`, never
|
|
1280
|
-
`setupServer.ts`, `setupBrowser.ts`, or a vendor readiness module; and its `tests/src/**/*.test.ts` and
|
|
1281
|
-
`tests/app/**/*.test.ts` exclude rows are uniform across all root shapes by design, including
|
|
1282
|
-
core-only workspaces where one row cannot currently match. Integration and service use 120-second
|
|
1283
|
-
test and hook timeouts with file parallelism disabled. Each service project layers
|
|
1284
|
-
`tests/setupServer.ts` and `tests/service/<vendor>/setup.ts` onto the shared setup, carries the
|
|
1285
|
-
server environment boundary, and may exercise either the `src` or `app` axis. The integration project wires
|
|
1286
|
-
`tests/setupGlobal.ts` for the shared template-registry harness exactly when `bin`, `integration`,
|
|
1287
|
-
and `global` are all true. Independently, a `global` source-browser project places
|
|
1288
|
-
`globalSetup: ['./tests/setupGlobal.ts']` immediately before its ordinary `setupFiles` row (and
|
|
1289
|
-
after the core-test exclusion where that row exists). Application browser projects never receive
|
|
1290
|
-
that field.
|
|
1291
|
-
|
|
1292
|
-
`configArtifacts`, `sourceArtifacts`, `applicationArtifacts`, `testArtifacts`, and `guideArtifacts`
|
|
1293
|
-
are the per-group drafters. When `bin` is selected, `configArtifacts` includes
|
|
1294
|
-
`configs/src/tsconfig.bin.json` and `configs/src/vite.bin.config.ts` beside the declared environment
|
|
1295
|
-
configuration pairs. When `showcase` is selected, it includes the computed thin
|
|
1296
|
-
`configs/app/vite.showcase.config.ts` wrapper beside the ordinary application browser pair.
|
|
1297
|
-
`paritySpecifiers` computes the self-specifier and module map the
|
|
1298
|
-
generated parity suite resolves fence imports through. `guideMemberTable`, `guideUsage`,
|
|
1299
|
-
`guideMethods`, and `guideTests` render the generated guide's member tables, usage examples, method
|
|
1300
|
-
contract, and test inventory. `fillArtifact` fills one template entry into a `template`-origin
|
|
1301
|
-
artifact with missing placeholders treated as an error, and `hostGroup` resolves which group a
|
|
1302
|
-
byte-copied host path belongs to — splitting by what a path governs rather than where it sits, so
|
|
1303
|
-
that both MCP registrations (`.mcp.json` and `.cursor/mcp.json`) group with the harness bridges as
|
|
1304
|
-
`orchestration` rather than with the root dotfiles they sit beside. `applyOverrides` replaces a matching artifact's content in place
|
|
1305
|
-
and deliberately leaves an unmatched, host-owned, or `package.json` override unapplied, because the
|
|
1306
|
-
gate reports it as a blocking question. `ciWorkflow` renders the generated workflow.
|
|
1307
|
-
|
|
1308
|
-
### Factories
|
|
1309
|
-
|
|
1310
|
-
From [`factories.ts`](../../src/core/factories.ts) and
|
|
1311
|
-
[`factories.ts`](../../src/server/factories.ts).
|
|
1312
|
-
|
|
1313
|
-
| Name | Kind |
|
|
1314
|
-
| -------------------- | -------- |
|
|
1315
|
-
| `createCompiler` | function |
|
|
1316
|
-
| `createPlanManager` | function |
|
|
1317
|
-
| `createBlueprint` | function |
|
|
1318
|
-
| `createMaterializer` | function |
|
|
1319
|
-
| `createSync` | function |
|
|
1320
|
-
|
|
1321
|
-
`createBlueprint` is the validating constructor: it fills the builder defaults and then checks both
|
|
1322
|
-
the exact-record shape and the semantic pass, throwing `INVALID` when either fails. The other four
|
|
1323
|
-
construct their entities from their options records.
|
|
1324
|
-
|
|
1325
|
-
### `Compiler`
|
|
1326
|
-
|
|
1327
|
-
The compilation orchestrator, from [`Compiler.ts`](../../src/core/Compiler.ts). It runs the fixed
|
|
1328
|
-
three-stage `draft → gate → pin` pipeline over a blueprint and owns a typed emitter whose event map
|
|
1329
|
-
is `compile`, `audit`, `block`, `error`, and `destroy`. Both public methods are genuinely
|
|
1330
|
-
synchronous and pure. `compile` emits `compile` only for a complete compilation and `block` for a
|
|
1331
|
-
gated one; `audit` emits `block` when gated and then always emits `audit`, never `compile`. After
|
|
1332
|
-
`destroy()` every method other than the getter and `destroy` itself throws `DESTROYED`, and teardown
|
|
1333
|
-
is idempotent with the emitter destroyed last.
|
|
1334
|
-
|
|
1335
|
-
### `PlanManager`
|
|
1336
|
-
|
|
1337
|
-
The versioned, content-hashed plan registry, from
|
|
1338
|
-
[`PlanManager.ts`](../../src/core/PlanManager.ts). Its event map is `add`, `remove`, and `destroy`.
|
|
1339
|
-
Construction parses its exact options before allocating the emitter. `add` re-pins an immutable,
|
|
1340
|
-
detached plan snapshot and mints the record id from that content hash, so re-adding an unchanged plan
|
|
1341
|
-
resolves to the same frozen record, a changed plan mints a fresh id, and a distinct canonical payload
|
|
1342
|
-
with a colliding digest throws `INVALID` before mutation or emission. `remove` follows the
|
|
1343
|
-
batch-overload convention with the array overload declared first. Its list form is all-or-nothing:
|
|
1344
|
-
if any listed id is unregistered the collection is untouched and `false` is returned; on success all
|
|
1345
|
-
selected records are removed before the first stable-order event, so synchronous listeners observe
|
|
1346
|
-
the committed state and cannot create reentrant duplicate removals. After `destroy()` every method
|
|
1347
|
-
other than the getters and `destroy` throws `DESTROYED`.
|
|
1348
|
-
|
|
1349
|
-
### `Materializer`
|
|
1350
|
-
|
|
1351
|
-
The materialization entity, from [`Materializer.ts`](../../src/server/Materializer.ts) — the only
|
|
1352
|
-
filesystem writer in the package. Its event map is `copy`, `write`, `remove`, `done`, `error`, and
|
|
1353
|
-
`destroy`. Every call preflights completely before mutating: a structural plan match, the semantic
|
|
1354
|
-
and contextual validation result, portable-path checks on every artifact path and source, collision
|
|
1355
|
-
detection, destination-shape checks, and realpath-anchored containment against both the target and
|
|
1356
|
-
the host root. Only then does staging begin, inside a private same-volume write transaction that is
|
|
1357
|
-
promoted atomically and rolled back on any failure. After `destroy()` every method throws
|
|
1358
|
-
`DESTROYED`.
|
|
1359
|
-
|
|
1360
|
-
### `Sync`
|
|
1361
|
-
|
|
1362
|
-
The upstream-synchronization entity, from [`Sync.ts`](../../src/server/Sync.ts) — the only network
|
|
1363
|
-
reader in the package. Its event map is `guide`, `version`, `package`, `write`, `done`, `error`, and
|
|
1364
|
-
`destroy`. Every request runs under a per-request abort timeout and a bounded worker pool rather
|
|
1365
|
-
than an unbounded parallel await, follows no redirects, sends no credentials, and reads its response
|
|
1366
|
-
body incrementally against both the per-response limit and a shared cumulative allowance. The
|
|
1367
|
-
default posture collects failures into the result as `missing` or `failed` verdicts; `strict` mode
|
|
1368
|
-
turns those into a thrown `FETCH` naming the failing URL. `destroy()` aborts every in-flight request
|
|
1369
|
-
and is idempotent, and every method afterwards throws `DESTROYED`.
|
|
1370
|
-
|
|
1371
|
-
### `WriteTransaction`
|
|
1372
|
-
|
|
1373
|
-
The nominal, same-volume write-transaction state, from
|
|
1374
|
-
[`WriteTransaction.ts`](../../src/server/WriteTransaction.ts). It is constructed only through its
|
|
1375
|
-
static `create`, which derives every filesystem path from a target plus portable relative paths — a
|
|
1376
|
-
caller can neither supply a deletion root nor mutate the captured arrays. Creation snapshots every
|
|
1377
|
-
destination into a frozen `WriteExpectation`, verifies any supplied preconditions against what is
|
|
1378
|
-
actually on disk, captures the parent anchor identity, and creates private staging and backup
|
|
1379
|
-
directories with restrictive permissions. Its readonly getters — `target`, `root`, `stage`,
|
|
1380
|
-
`backup`, `expectations`, `parents`, `directories`, `anchor`, and `existing` — are the only way to
|
|
1381
|
-
observe it; every operation over it lives in the exported transaction helpers.
|
|
1382
|
-
|
|
1383
|
-
## Methods
|
|
1384
|
-
|
|
1385
|
-
The public methods of each behavioral interface, one table per type.
|
|
1386
|
-
|
|
1387
|
-
#### `CompilerInterface`
|
|
1388
|
-
|
|
1389
|
-
| Method | Returns |
|
|
1390
|
-
| --------- | ------------- |
|
|
1391
|
-
| `compile` | `Scaffolding` |
|
|
1392
|
-
| `audit` | `Audit` |
|
|
1393
|
-
| `destroy` | `void` |
|
|
1394
|
-
|
|
1395
|
-
`compile(blueprint, groups?)` runs the pipeline and returns a complete or visibly incomplete
|
|
1396
|
-
`Scaffolding`; the optional group selection scopes the plan to those artifact groups.
|
|
1397
|
-
`audit(blueprint, current, groups?)` compiles and then diffs the resulting plan against the
|
|
1398
|
-
caller-supplied current content; a gated blueprint returns `complete: false` with the gate's
|
|
1399
|
-
blocking questions and zero findings, and a complete one carries the gate's advisories on that same
|
|
1400
|
-
`questions` field. Because this core-only method performs no host I/O, its compiled host artifacts
|
|
1401
|
-
have no `hex` and are audited by presence. Callers that need host-byte verdicts hydrate the compiled
|
|
1402
|
-
plan through the server face and call `diffPlan`, which is the path every executable audit uses.
|
|
1403
|
-
`destroy()` is idempotent teardown. The interface also exposes the readonly
|
|
1404
|
-
`emitter`.
|
|
1405
|
-
|
|
1406
|
-
#### `PlanManagerInterface`
|
|
1407
|
-
|
|
1408
|
-
| Method | Returns |
|
|
1409
|
-
| --------- | ------------------------- |
|
|
1410
|
-
| `has` | `boolean` |
|
|
1411
|
-
| `plan` | `PlanRecord \| undefined` |
|
|
1412
|
-
| `plans` | `readonly PlanRecord[]` |
|
|
1413
|
-
| `add` | `PlanRecord` |
|
|
1414
|
-
| `remove` | `boolean \| void` |
|
|
1415
|
-
| `destroy` | `void` |
|
|
1416
|
-
|
|
1417
|
-
`has(id)` tests registration. `plan(id)` is the singular accessor and returns `undefined` for an
|
|
1418
|
-
unregistered id; `plans()` is the plural accessor and returns a snapshot array. `add(plan)`
|
|
1419
|
-
registers or re-registers one plan. `remove()` removes every plan and returns `void`; `remove(id)`
|
|
1420
|
-
removes one and returns whether it existed; `remove(ids)` is all-or-nothing over a list. The
|
|
1421
|
-
interface also exposes the readonly `emitter` and `size` properties.
|
|
1422
|
-
|
|
1423
|
-
#### `MaterializerInterface`
|
|
1424
|
-
|
|
1425
|
-
| Method | Returns |
|
|
1426
|
-
| ------------- | ------------------- |
|
|
1427
|
-
| `materialize` | `MaterializeResult` |
|
|
1428
|
-
| `repair` | `MaterializeResult` |
|
|
1429
|
-
| `prune` | `MaterializeResult` |
|
|
1430
|
-
| `destroy` | `void` |
|
|
1431
|
-
|
|
1432
|
-
`materialize(plan, target)` is green-field: it refuses any target `isVacant` rejects, then copies
|
|
1433
|
-
each host artifact and writes each template and computed artifact. `repair(plan, audit, target,
|
|
1434
|
-
replace?)` is into-existing: it skips the vacancy check, re-verifies that the target still matches
|
|
1435
|
-
the audit preview, and writes missing artifacts. Stale artifacts are report-only and returned as
|
|
1436
|
-
`skipped` by default; passing `true` for `replace` explicitly replaces their bytes and discards their
|
|
1437
|
-
local changes. Aligned artifacts are always `skipped`. `prune(target, expected)` deletes exactly the
|
|
1438
|
-
unexpected files the vendored host no longer declares under the prune directories, and only after
|
|
1439
|
-
the observed bytes still match the `expected` snapshot it was previewed with. `destroy()` is
|
|
1440
|
-
idempotent teardown. The interface also exposes the readonly `emitter`.
|
|
1441
|
-
|
|
1442
|
-
#### `SyncInterface`
|
|
1443
|
-
|
|
1444
|
-
| Method | Returns |
|
|
1445
|
-
| ---------- | ----------------------------------- |
|
|
1446
|
-
| `lookup` | `Promise<readonly VersionLookup[]>` |
|
|
1447
|
-
| `guides` | `Promise<readonly GuideSync[]>` |
|
|
1448
|
-
| `versions` | `Promise<readonly VersionSync[]>` |
|
|
1449
|
-
| `catalog` | `Promise<readonly CatalogEntry[]>` |
|
|
1450
|
-
| `pull` | `Promise<SyncReport>` |
|
|
1451
|
-
| `mirror` | `Promise<SyncReport>` |
|
|
1452
|
-
| `write` | `Promise<readonly string[]>` |
|
|
1453
|
-
| `destroy` | `void` |
|
|
1454
|
-
|
|
1455
|
-
`lookup(names)` resolves registry versions from bare package names, with no declaration range
|
|
1456
|
-
required or synthesized. `guides(deps, current?)` fetches each dependency's upstream guide. The optional `current` map is
|
|
1457
|
-
keyed by dependency name: with it, a fetched guide byte-equal to its entry verdicts `current` and
|
|
1458
|
-
anything else verdicts `behind`; without it, every successful fetch verdicts `behind`, because no
|
|
1459
|
-
reference means it needs syncing. `versions(deps)` compares each declared range to the registry
|
|
1460
|
-
latest. `catalog()` enumerates the fleet from the registry's exact organization package list — an
|
|
1461
|
-
unreachable or malformed list is always a coded failure, since without it there is no catalog — then
|
|
1462
|
-
degrades gracefully per package. `pull(target, dependencies?)` builds the reference map from the
|
|
1463
|
-
target's own mirrors, so its verdicts are target-relative, and rejects a selection the target does
|
|
1464
|
-
not declare. `mirror(target)` reuses the exact organization enumeration without catalog's
|
|
1465
|
-
per-package packument reads, sorts the names, excludes the target's own manifest name, and builds a
|
|
1466
|
-
guide-only report with no versions. `write(report, target)` commits only the `behind` guides. `destroy()` aborts every
|
|
1467
|
-
in-flight request. The interface also exposes the readonly `emitter`.
|
|
1468
|
-
|
|
1469
|
-
## The compile pipeline
|
|
1470
|
-
|
|
1471
|
-
`compile` runs three stages in fixed order and records each as a `CompileRecord` carrying its input,
|
|
1472
|
-
its output, whether it failed, and any error text.
|
|
1473
|
-
|
|
1474
|
-
1. **draft** — `blueprintToPlan` selects the covered groups, drafts each group's artifacts, carries
|
|
1475
|
-
the selected host set — every vendored host path except the workspace's own guide — applies
|
|
1476
|
-
overrides, and pins the draft. A throw here records a `draft` failure coded
|
|
1477
|
-
`INVALID`, emits `error`, marks the remaining two stages skipped, and returns incomplete.
|
|
1478
|
-
2. **gate** — `validatePlan` runs the semantic pass over the blueprint and checks every override
|
|
1479
|
-
against the drafted artifact set. Blocking questions fail the stage; an accepted override and a
|
|
1480
|
-
dependency outside the vendored guide set each contribute a non-blocking advisory question
|
|
1481
|
-
instead.
|
|
1482
|
-
3. **pin** — a host-origin pointer artifact is appended for each non-vendored dependency, and
|
|
1483
|
-
`pinPlan` fills `trace` and `hash` from the plan's own content.
|
|
1484
|
-
|
|
1485
|
-
The gate fails closed. A blueprint that fails validation, or that carries an override matching no
|
|
1486
|
-
planned artifact or targeting a host-origin path, yields a visible incomplete `Scaffolding` — `plan`
|
|
1487
|
-
absent, `questions` populated, a `BLOCKED` failure marker recorded — rather than throwing and rather
|
|
1488
|
-
than emitting a half-formed workspace. A half-formed workspace is worse than a question.
|
|
1489
|
-
|
|
1490
|
-
`validateBlueprint` is the semantic law in one place. It checks the name against `NAME_PATTERN` and
|
|
1491
|
-
the length bound, the version and engines patterns, and that the declared engines floor is not below
|
|
1492
|
-
the supported Node minimum. It requires at least one selected environment across the two axes, keeps
|
|
1493
|
-
both axes on-vocabulary with no repeats, and blocks the one combination that has no defined
|
|
1494
|
-
configuration class: `browser` plus `server` without `core` in the same axis. It validates each
|
|
1495
|
-
dependency array
|
|
1496
|
-
for a well-formed name and range with no duplicates — scoped names for `dependencies` and `peers`,
|
|
1497
|
-
any valid npm name for `extras` — and blocks a name declared in two of the three arrays. It bounds
|
|
1498
|
-
the description and every override by the per-item and aggregate byte limits, and blocks a repeated
|
|
1499
|
-
or empty override path.
|
|
1500
|
-
|
|
1501
|
-
Because `pinPlan` derives `hash` from a canonical, key-order-independent serialization of the
|
|
1502
|
-
blueprint, groups, and artifacts, two logically equal blueprints built in different field orders
|
|
1503
|
-
produce the same digest — and a `PlanManager` id is that digest.
|
|
1504
|
-
|
|
1505
|
-
## Origin and ownership
|
|
1506
|
-
|
|
1507
|
-
`origin` is the ownership axis, and it decides everything downstream: how an artifact is produced,
|
|
1508
|
-
how it is audited, and whether it may ever be overwritten.
|
|
1509
|
-
|
|
1510
|
-
- **`host`** — byte-copied from the vendored data root. These are the shared files a whole fleet
|
|
1511
|
-
keeps identical: the root instruction documents and licence, the canonical orchestration contract
|
|
1512
|
-
and the three harness bridges that point at it, the agent, rule, and skill directories, the
|
|
1513
|
-
session scripts, the repository coding-law policy module, the byte-identical root dotfiles, and
|
|
1514
|
-
the two line guide mirrors a workspace carries for contracts other than its own. `HOST_PATHS` is
|
|
1515
|
-
the exact vendored list; what a given plan carries is `selectHostPaths` of it.
|
|
1516
|
-
- **`template`** — filled from a frozen template definition by a pure fill engine. These are
|
|
1517
|
-
starter files: source stubs, test stubs, the starter guide, the README.
|
|
1518
|
-
- **`computed`** — derived by this package's own combination logic. These are the structural files:
|
|
1519
|
-
the manifest, the tsconfigs, the build configuration, the generated CI workflow.
|
|
1520
|
-
|
|
1521
|
-
Audit semantics follow directly from that.
|
|
1522
|
-
|
|
1523
|
-
- A **template** artifact is birth-only and audit-exempt. It is always reported `aligned`, whatever
|
|
1524
|
-
the target holds. Starter files are written once and are legitimately outgrown — real code
|
|
1525
|
-
replaces the stub, a hand-authored guide replaces the scaffold prose, an entity gets renamed.
|
|
1526
|
-
Comparing a mature workspace against its birth stub is a category error, and it would make any
|
|
1527
|
-
unscoped repair a data-loss hazard. Template findings therefore never contribute to the drifted,
|
|
1528
|
-
missing, or clean tallies.
|
|
1529
|
-
- A **computed** artifact is content-aware canon: `missing`, `aligned`, or `stale`, and it gates the
|
|
1530
|
-
audit like any other drift.
|
|
1531
|
-
- A **host** artifact with canonical `hex` is content-compared exactly like a computed artifact and
|
|
1532
|
-
can be `stale`. Without `hex`, it is presence-owned: present is `aligned`, absent is `missing`.
|
|
1533
|
-
The catalog agent is explicitly presence-owned because `catalog` is its sole content writer.
|
|
1534
|
-
Hydration expands a directory-shaped host artifact into one artifact per declared file and verifies
|
|
1535
|
-
that the manifest digest matches the manifest's current membership before expansion. That detects
|
|
1536
|
-
stale-digest truncation; a self-consistently rewritten manifest defines a smaller valid inventory,
|
|
1537
|
-
so the digest alone cannot authenticate omitted membership.
|
|
1538
|
-
- In a caller-supplied snapshot, a path the plan does not own is `foreign`, and `inferGroup`
|
|
1539
|
-
classifies it by its leading path segment. The executable supplies unexpected paths only from
|
|
1540
|
-
`.claude/agents`, `.codex/agents`, and `scripts`, because those prune-owned directories are the
|
|
1541
|
-
only regions scaffold has authority to delete from; unplanned files elsewhere are not reported
|
|
1542
|
-
as foreign.
|
|
1543
|
-
|
|
1544
|
-
The same ownership boundary is what makes mutation safe. **`fleet` and default `repair` both scope
|
|
1545
|
-
the compiled plan to host origin before hydrating, diffing, or applying.** Missing files in that
|
|
1546
|
-
scope are restored, but stale files are report-only unless `--replace` explicitly authorizes byte
|
|
1547
|
-
replacement. `--generated` widens the selected ownership scope to generated canon. It keeps the
|
|
1548
|
-
`package.json` publication boundary protected except for the generated service-script keys needed
|
|
1549
|
-
when the derived service set changes; it composes with `--replace` and does not itself authorize
|
|
1550
|
-
replacement. Template
|
|
1551
|
-
artifacts remain birth-only in either scope, except that an absent service provisioner and absent
|
|
1552
|
-
service conformance test are promoted to missing-file repair artifacts. A present customized copy
|
|
1553
|
-
is never compared or replaced. A mature workspace's hand-written source, tests, and guides are
|
|
1554
|
-
therefore never overwritten with a stub. The generated
|
|
1555
|
-
`.github/workflows/ci.yml` is a **computed** artifact, so user-owned CI stands by default and is
|
|
1556
|
-
restored only when both `--generated` and `--replace` are passed.
|
|
1557
|
-
Audit always compares it because computed artifacts are content-aware canon. A legitimate
|
|
1558
|
-
difference that the blueprint cannot express is a canon gap: add the missing blueprint axis rather
|
|
1559
|
-
than forking the computed file in one repository.
|
|
1560
|
-
|
|
1561
|
-
Overrides respect the same boundary from the other direction. `applyOverrides` never replaces a
|
|
1562
|
-
host-origin artifact and never replaces `package.json`; the gate turns either attempt — and an
|
|
1563
|
-
override matching no planned artifact at all — into a blocking question rather than a silent no-op.
|
|
1564
|
-
What survives those three refusals is applied and announced: the gate carries a non-blocking
|
|
1565
|
-
advisory naming each replaced path, and that advisory rides the `Scaffolding` and the `Audit` all
|
|
1566
|
-
the way through the library result.
|
|
1567
|
-
|
|
1568
|
-
Guide mirrors are the one place ownership is conditional, and the law is one owner per guide path.
|
|
1569
|
-
**A workspace mirrors every line guide except its own.** When the name matches — the guide package
|
|
1570
|
-
on `guides/src/guide.md`, this package on `guides/src/scaffold.md` — the workspace itself is the
|
|
1571
|
-
owner, keeping that path as its **template**-origin starter guide, and `selectHostPaths` drops the
|
|
1572
|
-
vendored mirror so the path is contributed exactly once. For every other contract the mirror is the
|
|
1573
|
-
owner: a dependency this package vendors a byte-identical mirror for gets a real host-origin copy of
|
|
1574
|
-
`guides/src/<short>.md`, contributed once whether it arrives through the host set or through the
|
|
1575
|
-
dependency, so a package depending on `@orkestrel/guide` plans one `guides/src/guide.md` rather than
|
|
1576
|
-
two. Any other dependency gets a host-origin _pointer_ artifact plus a non-blocking question, never
|
|
1577
|
-
a fabricated mirror; on materialization that pointer degrades to a short stub, and `scaffold pull`
|
|
1578
|
-
fetches the real thing. Hydration marks that permanent pointer state presence-owned, so both the
|
|
1579
|
-
birth stub and a later pulled guide audit clean while present; `pull` refreshes content but is not a
|
|
1580
|
-
remedy for an audit state. That degrade is scoped exactly to guide pointers. A manifest whose
|
|
1581
|
-
membership changes without a matching digest is rejected, while any other undeclared or unreadable
|
|
1582
|
-
source is rejected with a coded `TARGET` failure. Selection is the law and
|
|
1583
|
-
`findFileConflict` is its backstop: two artifacts at one path refuse the plan rather than racing to
|
|
1584
|
-
be the last writer.
|
|
1585
|
-
|
|
1586
|
-
## Audit, repair, and prune
|
|
1587
|
-
|
|
1588
|
-
An audit is a pure function of a plan and a snapshot, so the same engine that creates a workspace
|
|
1589
|
-
checks one. `readTarget` supplies the snapshot as exact bytes; `diffPlan` returns findings as data;
|
|
1590
|
-
`auditToReview` renders them for a human. Nothing in that path writes.
|
|
1591
|
-
|
|
1592
|
-
The executable's physical unexpected-file scan treats exactly `scripts/service.sh` as an expected
|
|
1593
|
-
workspace-owned seam when the derived blueprint has at least one service. That exclusion is
|
|
1594
|
-
warranted because the promoted plan reports an absent file as missing while a present file is
|
|
1595
|
-
consumer-owned. A workspace with no services still reports the same path as foreign.
|
|
1596
|
-
|
|
1597
|
-
`repair` turns those findings back into the narrowest possible write. It re-reads the target,
|
|
1598
|
-
re-diffs it, and refuses to proceed if the findings changed since the preview it was given — a
|
|
1599
|
-
target that moved under the caller is a `TARGET` failure, not a race to win. It then derives a write
|
|
1600
|
-
precondition per artifact from the audit itself: a `missing` finding requires the destination to
|
|
1601
|
-
still be absent. A `stale` finding remains untouched and is reported as skipped unless the caller
|
|
1602
|
-
passes `replace`; an authorized stale replacement requires the destination to still carry exactly
|
|
1603
|
-
the bytes that were observed. Those preconditions are checked again inside the write transaction
|
|
1604
|
-
before any promotion. A skipped stale path keeps the executable at exit `1`, because the selected
|
|
1605
|
-
workspace remains drifted; a clean run and a run that fully applies its findings exit `0`. An
|
|
1606
|
-
interactive audit repair hand-off forwards both `--generated` and `--replace` when those flags were
|
|
1607
|
-
present on `audit`.
|
|
1608
|
-
|
|
1609
|
-
That boundary governs the executable's words too. Every drift line states what a command will do
|
|
1610
|
-
rather than how a file came to differ: the executable cannot know whether a generated file was
|
|
1611
|
-
hand-edited, and a consumer whose blueprint cannot yet express what it needs legitimately edits one.
|
|
1612
|
-
Every cost is stated where it can still be declined, and nowhere else: a run with nothing to write
|
|
1613
|
-
states no boundary it is not about to act on, because a warning attached to a no-op only trains
|
|
1614
|
-
operators to ignore warnings. `repair` states its scope when the audit found something to repair;
|
|
1615
|
-
`fleet` states its scope, its repository count, and the same replacement cost once `--apply` has
|
|
1616
|
-
authorized a write; neither states it over a dry run. Each run closes on the tally of what it did,
|
|
1617
|
-
including a run that writes nothing — and a drifted file left alone is counted apart from an
|
|
1618
|
-
aligned one, because `unchanged` is already the audit table's word for a file that matches canon.
|
|
1619
|
-
|
|
1620
|
-
**Four paths discard content a consumer may own, and each names its cost before it acts.**
|
|
1621
|
-
|
|
1622
|
-
| Path | What it discards | Ownership boundary |
|
|
1623
|
-
| --------------------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
1624
|
-
| `--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` |
|
|
1625
|
-
| `--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 |
|
|
1626
|
-
| `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 |
|
|
1627
|
-
| `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 |
|
|
1628
|
-
|
|
1629
|
-
`--replace` is the only one of the four that is an opt-in modifier rather than a verb, so it is the
|
|
1630
|
-
one whose cost is repeated in every line that offers it: the scope line, the repair verdict, the
|
|
1631
|
-
audit's drift guidance, and the hand-off question itself. `--apply` authorizes all four and nothing
|
|
1632
|
-
else does — `--yes` only skips a confirmation it can no longer stand in for, and an unexpected-file
|
|
1633
|
-
hint that recommended `repair --prune` without it would name a command that deletes nothing. `new`
|
|
1634
|
-
is absent from the table on purpose: it refuses any target `isVacant` rejects, so it has no local
|
|
1635
|
-
content to discard.
|
|
1636
|
-
|
|
1637
|
-
The catalog agent has a narrower ownership exception in `diffPlan` itself.
|
|
1638
|
-
`CATALOG_AGENT_PATH` remains presence-owned after host hydration: repair can restore the
|
|
1639
|
-
absent file, but audit, repair, fleet, and direct library consumers never compare or replace its
|
|
1640
|
-
existing bytes, even under `--replace`. `catalog` is the sole content writer and continues to
|
|
1641
|
-
replace only the uniquely bounded marker region. Thus
|
|
1642
|
-
`repair` → `catalog` → `repair` converges without restoring a stale embedded catalog snapshot over
|
|
1643
|
-
the current fleet table.
|
|
1644
|
-
|
|
1645
|
-
`prune` is the deletion arm, and it is deliberately narrow. Its candidate set comes from
|
|
1646
|
-
`pruneTargets`, which is also what the executable's merged report and preview read. Repair's
|
|
1647
|
-
optimistic-concurrency recheck receives the raw plan audit, while the separate foreign findings
|
|
1648
|
-
remain attached to reporting and exit status; after that recheck succeeds, the same preview snapshot
|
|
1649
|
-
drives deletion. Only the three prune directories are in scope; the allowlist must be
|
|
1650
|
-
positively established from the vendored host, or the call fails closed rather than treating an
|
|
1651
|
-
unresolved host as "vendors nothing" and proposing to delete everything. Each candidate is verified
|
|
1652
|
-
as a plain physical file whose bytes still match the preview, moved into a private quarantine rather
|
|
1653
|
-
than unlinked, re-verified after the move, and only then reported as removed — with a full restore
|
|
1654
|
-
attempt if any candidate fails mid-way.
|
|
1655
|
-
|
|
1656
|
-
## Upstream sync, pull, mirror, and catalog
|
|
1657
|
-
|
|
1658
|
-
`Sync` is the only network reader, and its posture is conservative by construction.
|
|
1659
|
-
|
|
1660
|
-
Every request is unauthenticated: no token, no authorization header, anywhere. Every fleet
|
|
1661
|
-
repository is public, so plain reachability is the only signal, and a guide `404` degrades
|
|
1662
|
-
gracefully instead of needing credentials. Redirects are never followed — a 3xx, or the opaque
|
|
1663
|
-
response a manual redirect policy produces, is treated as a distinct named transport fault, so a
|
|
1664
|
-
compromised or misconfigured endpoint cannot silently redirect cross-host. Guide URLs are therefore
|
|
1665
|
-
built in their canonical form directly rather than relying on a redirect to reach it.
|
|
1666
|
-
|
|
1667
|
-
Concurrency is bounded by a worker pool over a shared cursor, never an unbounded parallel await. The
|
|
1668
|
-
pool preserves input order, stops issuing new work after the first error, awaits every worker so a
|
|
1669
|
-
sibling rejection is always observed, and then rethrows the first error. Response bodies are read
|
|
1670
|
-
incrementally against both the per-response limit and a shared cumulative allowance; a declared
|
|
1671
|
-
oversized content length short-circuits before any read. An oversized body is a transport fault like
|
|
1672
|
-
any other — retry-eligible, then `failed`, or a thrown `FETCH` under `strict`.
|
|
1673
|
-
|
|
1674
|
-
Every non-clean outcome carries a `note` explaining the cause: a transport error message with the
|
|
1675
|
-
underlying socket code appended when the runtime attaches one, an HTTP status, the fixed
|
|
1676
|
-
redirect-blocked string, or the oversized-body message. `current` and `behind` carry no note,
|
|
1677
|
-
because there is nothing to explain.
|
|
1678
|
-
|
|
1679
|
-
`pull` is the dependency-aware composition. It reads the target's declared scoped dependencies from its
|
|
1680
|
-
manifest, rejects any explicit selection the target does not declare, builds the reference map from
|
|
1681
|
-
the target's own `guides/src/<short>.md` mirrors, fetches guides and versions under one shared
|
|
1682
|
-
allowance, and assembles a report whose `clean` flag requires both no drift and no failures. A
|
|
1683
|
-
target that declares itself is the one asymmetry, and it follows the same single-owner law: the
|
|
1684
|
-
guide pass drops the self dependency, so `pull` never fetches or writes a workspace's own contract
|
|
1685
|
-
guide over the copy that workspace owns, while the version pass keeps it and still reports its
|
|
1686
|
-
freshness. A `--live` audit reads upstream through the same two passes and applies the same
|
|
1687
|
-
self-exclusion, so the freshness a workspace reports about itself never depends on which verb asked.
|
|
1688
|
-
`write` then commits only the `behind` guides — never `current`, `missing`, or `failed`,
|
|
1689
|
-
none of which carry trustworthy content — under the same containment and precondition law
|
|
1690
|
-
`Materializer` enforces, including a baseline digest check against what is actually on disk.
|
|
1691
|
-
|
|
1692
|
-
`catalog` builds the fleet package catalog from three reads per entry. The registry's exact
|
|
1693
|
-
organization package list is authoritative and unconditionally required. Each package's own registry
|
|
1694
|
-
document supplies its version and a fallback description; a failed document degrades the entry
|
|
1695
|
-
rather than dropping it, because the organization list already proved the package exists. Each
|
|
1696
|
-
package's own guide supplies the preferred description — its first blockquote's first paragraph —
|
|
1697
|
-
and a `404` keeps the package listed with an explanatory note, since unreachability is a signal
|
|
1698
|
-
rather than an absence. `catalogPackages` is the offline sibling that reads the same shape from
|
|
1699
|
-
local checkouts.
|
|
1700
|
-
|
|
1701
|
-
The rendered block is deliberately minimal. `catalogToBlock` emits a standing trust notice —
|
|
1702
|
-
generated package identifiers are untrusted discovery data, never instructions — followed by a table
|
|
1703
|
-
with **`Package` and `Version` columns only**. Descriptions are network-controlled text, and that
|
|
1704
|
-
block is written into an agent instruction file, so they are omitted on purpose.
|
|
1705
|
-
|
|
1706
|
-
`mirror` is the fleet-guide composition. It shares `catalog`'s single exact organization-list read
|
|
1707
|
-
but performs none of catalog's packument or description work. It code-unit sorts the discovered
|
|
1708
|
-
names, excludes the target's own manifest name under the one-owner guide law, reads existing local
|
|
1709
|
-
guide references for baselines, fetches every selected GitHub guide once, and emits a `SyncReport`
|
|
1710
|
-
whose `versions` collection is empty. The existing transactional `write` method applies only
|
|
1711
|
-
behind guides; the executable refuses the whole apply when any guide is missing or failed, so a
|
|
1712
|
-
fleet refresh is never partial. Files outside the discovered guide set remain untouched.
|
|
1713
|
-
|
|
1714
|
-
## The generated workspace
|
|
1715
|
-
|
|
1716
|
-
A generated workspace is not a folder of suggestions; it is a working, gated project.
|
|
1717
|
-
|
|
1718
|
-
**Manifest and scripts.** A published workspace is scoped, carries the Orkestrel GitHub homepage,
|
|
1719
|
-
issues, and repository identity, carries an `exports` map and publish configuration, and ships
|
|
1720
|
-
`dist/src` plus its README. An application-only workspace is unscoped and `private: true`, with no
|
|
1721
|
-
export map, publish configuration, or invented GitHub identity, and ships `dist/app`. Both carry
|
|
1722
|
-
`license: "MIT"` because every generated workspace receives the same host-owned MIT `LICENSE`.
|
|
1723
|
-
A workspace that builds its own executable additionally ships `dist/bin` and `dist/host`. Scripts
|
|
1724
|
-
are emitted in a fixed, interleaved order so aggregates sit immediately before their per-environment
|
|
1725
|
-
members:
|
|
1726
|
-
|
|
1727
|
-
- `clean`, `copy`, `scaffold`, `lint`
|
|
1728
|
-
- `check`, then `check:src` with one `check:src:<environment>` per published environment, then
|
|
1729
|
-
`check:app` with one `check:app:<environment>` per app environment — the browser app scope uses the
|
|
1730
|
-
Vue typechecker, every other scope uses plain `tsc`
|
|
1731
|
-
- `format`, `format:check`, `lint:check`
|
|
1732
|
-
- `test`, then `test:src` and its per-environment scopes, the optional `test:integration`,
|
|
1733
|
-
`test:equivalence`, and `test:service` aggregate followed by its sorted per-vendor proofs,
|
|
1734
|
-
`test:app` and its per-environment scopes, then
|
|
1735
|
-
`test:policy`, `test:config`, and `test:guides`
|
|
1736
|
-
- `build`, then `build:src` and its per-environment targets, `build:app` and its runtime targets, and
|
|
1737
|
-
`build:host` for a bin workspace
|
|
1738
|
-
- `dev` when a browser application is selected; `serve` and `serve:build` when a server application
|
|
1739
|
-
is selected
|
|
1740
|
-
- `showcase`, `build:showcase`, and `show` only when the physical showcase wrapper is present;
|
|
1741
|
-
`show` formats, then builds, then copies `dist/showcase/index.html` to `demo/showcase.html`
|
|
1742
|
-
- `prepublishOnly` chaining `format:check → lint:check → check → build → test`, followed by
|
|
1743
|
-
`test:integration` when selected and finally `test:service` when any service is selected
|
|
1744
|
-
|
|
1745
|
-
**Proof gating.** The opt-in proofs are predictable from the axes alone. `test:integration` rides
|
|
1746
|
-
the `integration` axis and `test:service` a nonempty `services` axis, while `test:equivalence` is emitted
|
|
1747
|
-
only where `bin` and `integration` are both set:
|
|
1748
|
-
|
|
1749
|
-
| Proof | `npm test` | `prepublishOnly` | CI |
|
|
1750
|
-
| ------------------ | ---------- | ------------------- | -------------------------- |
|
|
1751
|
-
| `test:integration` | no | yes, before service | after the standard gates |
|
|
1752
|
-
| `test:equivalence` | no | no | no |
|
|
1753
|
-
| `test:service` | no | yes, last | after `scripts/service.sh` |
|
|
1754
|
-
|
|
1755
|
-
No opt-in proof joins the default chain: `npm test` runs the source, application, policy,
|
|
1756
|
-
configuration, and guide projects, and nothing there needs a build artifact or a foreign process.
|
|
1757
|
-
Publication is the one asymmetry: `prepublishOnly` appends integration and then service proofs.
|
|
1758
|
-
A package that claims to drive a vendor has not proved that claim unless publishing runs against
|
|
1759
|
-
it, despite the provisioning cost. Neither default testing nor publication starts a foreign
|
|
1760
|
-
process; publication requires the caller to provision one first.
|
|
1761
|
-
The showcase is likewise outside `build`, `test`, and `prepublishOnly`; it is an explicit projection
|
|
1762
|
-
of `app/browser`, not an environment, test-project row, or source/demo artifact.
|
|
1763
|
-
Its copied `demo/showcase.html` is generated and minified, so the mirrored `.prettierignore` keeps it
|
|
1764
|
-
outside the whole-tree formatter while source and configuration files remain fully gated.
|
|
1765
|
-
|
|
1766
|
-
When a prerequisite is absent the proof fails rather than skipping. `test:integration` reads the
|
|
1767
|
-
workspace's own built output, so it belongs after `build` — which is exactly where `prepublishOnly`
|
|
1768
|
-
and CI put it. `test:service` refuses to start against an unprovisioned service: its setup throws at
|
|
1769
|
-
module load, which is why CI runs `bash scripts/service.sh` immediately before it. And a script the
|
|
1770
|
-
axes do not emit is simply not there: `test:equivalence` in a workspace that is not both `bin` and
|
|
1771
|
-
`integration` is an unknown script rather than a quietly passing one.
|
|
1772
|
-
|
|
1773
|
-
The equivalence proof is a dual-path re-run rather than a separate suite. Run
|
|
1774
|
-
`npm run test:equivalence` after changing the persistent boundary build driver; it invokes the
|
|
1775
|
-
integration project in dual-path mode and proves each programmatic driver verdict against the
|
|
1776
|
-
spawned npm-script reference. Ordinary integration runs keep the faster driver-only path.
|
|
1777
|
-
|
|
1778
|
-
**Consumer-owned service seams.** Each vendor owns its readiness module at
|
|
1779
|
-
`tests/service/<vendor>/setup.ts`. It probes and warms that vendor at module load, throwing a clear
|
|
1780
|
-
error when unavailable so only that vendor project fails readiness. The scaffold never generates
|
|
1781
|
-
these modules because an inert readiness check would be a false proof.
|
|
1782
|
-
|
|
1783
|
-
`scripts/service.sh`, named once by `SERVICE_SCRIPT_PATH`, is shared provisioning for every vendor.
|
|
1784
|
-
The scaffold emits a template skeleton that exits nonzero until the workspace replaces it with
|
|
1785
|
-
idempotent provisioning; an already-provisioned vendor must be a no-op, and any vendor that cannot
|
|
1786
|
-
be prepared must make the script fail. The skeleton is written at birth when services are already
|
|
1787
|
-
declared, or by repair when a post-birth vendor declaration makes it newly absent. Once present it
|
|
1788
|
-
is consumer-owned and never replaced. CI invokes it once before the aggregate project run.
|
|
1789
|
-
|
|
1790
|
-
The configuration conformance test lives in the ordinary `config` project, so `npm test` checks the
|
|
1791
|
-
directory names, readiness files, project declarations, scripts, default-test omission, and
|
|
1792
|
-
publication suffix without contacting a vendor. Like the provisioner it is repaired only when
|
|
1793
|
-
absent, then remains workspace-owned and audit-exempt. Service adoption under `--generated`
|
|
1794
|
-
regenerates the Vite and CI canon and merges only `test:service`, the per-vendor service scripts,
|
|
1795
|
-
and the `prepublishOnly` service suffix into `package.json`; publication metadata and unrelated
|
|
1796
|
-
scripts retain their existing values.
|
|
1797
|
-
|
|
1798
|
-
The audit expects the script rather than reporting it foreign, on the derive-time warrant the audit
|
|
1799
|
-
section gives. Repair pruning applies the same exclusion, so it never proposes or removes that
|
|
1800
|
-
required workspace-owned provisioner.
|
|
1801
|
-
|
|
1802
|
-
**Environment isolation.** Scoped TypeScript projects remove the wrong host's globals from each
|
|
1803
|
-
environment: core scopes carry the WHATWG web-interop surface and no host at all — no DOM, no Node,
|
|
1804
|
-
no `vite/client`; browser scopes carry DOM and no Node; server scopes carry Node and no DOM. The
|
|
1805
|
-
worker-only globals the `WebWorker` declarations would otherwise admit — `name`, `onrtctransform`,
|
|
1806
|
-
`close`, `postMessage`, `dispatchEvent`, `location`, `onerror`, `onlanguagechange`, `onoffline`,
|
|
1807
|
-
`ononline`, `onrejectionhandled`, `onunhandledrejection`, `self`, `importScripts`, `fonts`, `caches`,
|
|
1808
|
-
`crossOriginIsolated`, `indexedDB`, `isSecureContext`, `origin`, `scheduler`, `createImageBitmap`,
|
|
1809
|
-
`reportError`, `cancelAnimationFrame`, `requestAnimationFrame`, `onmessage`, `onmessageerror`,
|
|
1810
|
-
`addEventListener`, and `removeEventListener` — are fenced out of `src/core` and `app/core` sources
|
|
1811
|
-
by the policy suite, so the declarations widen what a host-independent module may call without
|
|
1812
|
-
widening where it may run. On every TypeScript bump, derive this list from the module-scope
|
|
1813
|
-
global-object `declare var` and `declare function` declarations in the installed
|
|
1814
|
-
`lib.webworker.d.ts`, then subtract values supplied by `lib.esnext*` or current Node globals. Lint
|
|
1815
|
-
restricts declared package, alias, and conventional relative imports in the same directions.
|
|
1816
|
-
Neither replaces the other, and neither replaces the build.
|
|
1817
|
-
|
|
1818
|
-
**The generated build boundary.** The emitted configuration carries an environment-boundary plugin
|
|
1819
|
-
that resolves the real module graph rather than re-implementing a parser. **TypeScript and
|
|
1820
|
-
JavaScript references are read through Vite's own Oxc/Rolldown AST**; **Vue single-file components
|
|
1821
|
-
are read through the official SFC compiler**, block by block, including `src`-referenced blocks;
|
|
1822
|
-
**CSS dependencies are parsed by Vite's bundled Lightning CSS analyzer**; and **HTML attributes,
|
|
1823
|
-
entities, candidate lists, and metadata use Vite's own HTML parser callbacks**. The plugin runs at
|
|
1824
|
-
resolve and transform time, checks the finished module graph at build end, rescans every emitted
|
|
1825
|
-
JavaScript chunk's remaining dynamic imports after optimization and tree-shaking, and audits every
|
|
1826
|
-
emitted asset's physical source path. Source-level asset URLs are checked before Vite transforms
|
|
1827
|
-
them, so generated runtime `new URL(...)` expressions are not mistaken for caller input. HTML
|
|
1828
|
-
`vite-ignore` tokens are reversibly encoded as an HTML character reference before Vite parses the
|
|
1829
|
-
document, so the attribute cannot opt an element out of Vite's normal HTML graph while the same
|
|
1830
|
-
text inside a resource URL still decodes to its original filename before resolution. Existing
|
|
1831
|
-
equivalent character references are shifted before encoding and unshifted afterward, which keeps
|
|
1832
|
-
comments, text, raw blocks, attributes, adjacent tokens, casing, and user-authored entity spelling
|
|
1833
|
-
byte-stable. The trusted preparation hook owns the final pre-parse phase; inline proxy code is
|
|
1834
|
-
restored before module analysis, and the first normal post-parse hook restores the original HTML
|
|
1835
|
-
spelling. The browser entry begins with a generated, byte-stable security prologue: the doctype,
|
|
1836
|
-
head opening, and `Content-Security-Policy` meta markup, ordering, and indentation are exact. The
|
|
1837
|
-
opening `html` start tag is parsed by `@orkestrel/html`'s fail-closed `parseStartTag` boundary,
|
|
1838
|
-
so ASCII case and well-formed attributes such as `lang`, `data-bs-theme`, and `data-bs-core`
|
|
1839
|
-
may vary without weakening the position of the following head and policy. A malformed, incomplete,
|
|
1840
|
-
duplicate-attribute, wrong-name, or syntactically slashed root still fails closed. Preparation owns
|
|
1841
|
-
that positional check while the document is still generated bytes; the final trusted post-hook
|
|
1842
|
-
checks only that the exact
|
|
1843
|
-
policy survived because Vite may legitimately inject into the head. CRLF and LF files are both
|
|
1844
|
-
accepted. Vite's
|
|
1845
|
-
`%ENV%` HTML substitution is rejected
|
|
1846
|
-
before parsing because Vite performs that expansion after every plugin pre-hook, where it could
|
|
1847
|
-
otherwise create a late control attribute. The guard walks the exact left-to-right `%(\S+?)%`
|
|
1848
|
-
tokens Vite recognizes instead of performing a substring search, and each preparation plugin owns
|
|
1849
|
-
the resolved environment/definition keys for its configuration, so one build cannot contaminate
|
|
1850
|
-
another and overlapping percent text remains ordinary text. Read environment values from the
|
|
1851
|
-
application's module graph through `import.meta.env` instead.
|
|
1852
|
-
Asset URLs that force `?inline` are rejected before Vite can read them outside that auditable output
|
|
1853
|
-
graph. Dynamic imports must use a static quoted string or expression-free template string; even
|
|
1854
|
-
`/* @vite-ignore */` static values repeat the same environment and containment checks inside the
|
|
1855
|
-
transform boundary, including inline HTML proxy modules. The transform, load, resolution, emitted
|
|
1856
|
-
asset, and finished-module-graph passes apply that law only to workspace-owned `src/*` and `app/*`
|
|
1857
|
-
modules. Resolved ids under any `node_modules` segment, Vite/Vitest virtual ids, and tooling client
|
|
1858
|
-
injections remain owned by their toolchain and are exempt.
|
|
1859
|
-
|
|
1860
|
-
Browser application scripts are modules. Vite's parsed HTML asset callback rejects a classic
|
|
1861
|
-
external `<script src>` before resolution and directs the author to `type="module"`. A module
|
|
1862
|
-
script URL must be a non-empty local Vite-graph URL: schemes, protocol-relative URLs, data URLs,
|
|
1863
|
-
fragments, surrounding URL whitespace, and ASCII C0 controls or DEL are rejected rather than left
|
|
1864
|
-
as unaudited browser loads. This is deliberately broader than the URL parser's edge stripping.
|
|
1865
|
-
Numeric
|
|
1866
|
-
HTML character references and semicolon-terminated named references are rejected before resolution,
|
|
1867
|
-
so neither control references nor entity-built scheme characters can bypass the boundary. Vite can
|
|
1868
|
-
begin resolving an entity-decoded module URL before its parsed per-asset callback runs; that earlier
|
|
1869
|
-
path remains Vite-owned and passes through the environment resolver, which rejects the same full
|
|
1870
|
-
ASCII control range and every non-Node URL scheme before loading or output. No second HTML parser or global
|
|
1871
|
-
reference rewrite is involved, so comments, text, non-script attributes, and entity-spelled asset
|
|
1872
|
-
filenames retain Vite's native parsing and resolution behavior.
|
|
1873
|
-
The resolver leaves NUL-prefixed and `virtual:` Rolldown/Vite module IDs, tooling client injections,
|
|
1874
|
-
and every resolved `node_modules` module to the tool that owns that namespace; author module and
|
|
1875
|
-
asset URLs are extracted and validated before they reach those resolver exceptions.
|
|
1876
|
-
SVG script `href` and `xlink:href` attributes are parsed too and rejected as classic script loads.
|
|
1877
|
-
Inline module scripts enter Vite's HTML proxy graph and receive the same Oxc boundary analysis as
|
|
1878
|
-
module files. Classic inline scripts cannot enter that graph, so the required security prologue places
|
|
1879
|
-
`Content-Security-Policy` before every author-controlled document token with `script-src 'self'`
|
|
1880
|
-
and `script-src-attr 'none'`: inline classic code and inline event handlers cannot execute, while
|
|
1881
|
-
Vite's same-origin external module entry remains usable. `appBrowser()` accepts no configuration
|
|
1882
|
-
arguments. The returned Vite configuration is one closed trusted unit: its Vue and boundary
|
|
1883
|
-
plugins, CSS analyzer, dependency optimizer, environment, builder, output pipeline, and HTML asset
|
|
1884
|
-
callbacks cannot be extended or replaced through the factory. This deliberately excludes arbitrary
|
|
1885
|
-
Vite, Rolldown, esbuild, PostCSS, worker, environment, builder, externalization, output-injection,
|
|
1886
|
-
and URL-rewrite hooks that could mutate a dependency, worker graph, bundle, or final asset after
|
|
1887
|
-
the boundary has inspected it. The computed root Vite configuration is trusted generated code:
|
|
1888
|
-
wrapping, mutating, or replacing the object returned by `appBrowser()` is outside the factory
|
|
1889
|
-
contract and is reported as computed-artifact drift by `scaffold audit`. The output-boundary plugin
|
|
1890
|
-
still rejects public directories, browser asset inlining, and output path overrides in a
|
|
1891
|
-
post-factory composition as defense in depth; that narrow check is not a general extension seam.
|
|
1892
|
-
|
|
1893
|
-
When the showcase fact is present, the generated root also exports closed
|
|
1894
|
-
`appShowcase(...config: never[])`; both factories reject every argument at runtime. The
|
|
1895
|
-
ordinary factory retains its strict
|
|
1896
|
-
`script-src 'self'` policy, external asset auditing, and `dist/app/browser` output. The showcase
|
|
1897
|
-
factory is a standalone configuration with `base: './'`, unlimited asset inlining, and
|
|
1898
|
-
`dist/showcase` output. It applies `viteSingleFile` with
|
|
1899
|
-
`removeViteModuleLoader: true` and `useRecommendedBuildConfig: true`, uses Oxc and Lightning CSS
|
|
1900
|
-
minification for an `esnext` build without source maps or module preload, and inserts a SHA-256
|
|
1901
|
-
`build-id` derived from the secured, fully inlined document. An unchanged document therefore keeps
|
|
1902
|
-
the same id, while any changed byte changes it. The showcase development CSP keeps scripts
|
|
1903
|
-
same-origin and permits Vue's injected inline styles. Its built CSP swaps that script permission to
|
|
1904
|
-
inline and admits only inline styles plus data images and fonts, while both policies retain
|
|
1905
|
-
`default-src 'none'`, `script-src-attr 'none'`, `object-src 'none'`, and `base-uri 'none'`.
|
|
1906
|
-
|
|
1907
|
-
The showcase fact also emits its own entry pair, `app/browser/showcase.html` and
|
|
1908
|
-
`app/browser/showcase.ts`, beside the application's `index.html` and `main.ts`. Both HTML entries
|
|
1909
|
-
open with a generated security prologue: the application carries the ordinary strict policy and the
|
|
1910
|
-
showcase carries its development policy. The boundary plugins select and validate the matching
|
|
1911
|
-
prologue; the showcase build alone swaps in the self-contained policy before hashing and renames its
|
|
1912
|
-
single HTML output to `index.html`, which is what `show` copies to `demo/showcase.html`. The showcase entry
|
|
1913
|
-
mounts `mountShowcaseApplication`, and `app/browser/seeders.ts` exports exactly one seeder,
|
|
1914
|
-
`seedApplication`, returning a frozen identity of the same shape the shipped root view receives.
|
|
1915
|
-
The two mount factories differ in the seed expression alone. Both explicitly pass
|
|
1916
|
-
`{ name: seed.name }` to the same `createBrowserApplication` root: the showcase seed comes from
|
|
1917
|
-
`seedApplication()`, while the shipped application seed comes from `readApplicationHealth` with
|
|
1918
|
-
the configured identity as its fallback.
|
|
1919
|
-
|
|
1920
|
-
The browser development server applies the same trust boundary before Vite's internal middleware.
|
|
1921
|
-
Its explicit filesystem allowlist contains only browser/core source roots, browser tests, their
|
|
1922
|
-
exact setup files, and installed dependencies. The pre-internal middleware decodes direct,
|
|
1923
|
-
alias-shaped, and `/@fs/` requests, resolves existing targets through their physical paths, and
|
|
1924
|
-
returns a path-free 403 response unless the target remains in one of those roots. It also rejects
|
|
1925
|
-
an allowed root whose physical identity escapes the workspace, so neither a nested symlink nor a
|
|
1926
|
-
linked root can expose `app/server`, `src/server`, repository metadata, or unrelated files.
|
|
1927
|
-
|
|
1928
|
-
What it allows is deliberately real-world:
|
|
1929
|
-
|
|
1930
|
-
- safe stylesheet `@import`s and `url()` assets;
|
|
1931
|
-
- HTML-referenced assets, including candidate lists, inline style blocks, and inline module scripts;
|
|
1932
|
-
- static `new URL('./asset', import.meta.url)` asset references;
|
|
1933
|
-
- static-string dynamic imports whose decoded source passes the same environment and containment law.
|
|
1934
|
-
|
|
1935
|
-
What it rejects is equally deliberate:
|
|
1936
|
-
|
|
1937
|
-
- a published `src/*` module reaching into private `app/*`;
|
|
1938
|
-
- a core module reaching a stylesheet, a browser module, a server module, a Node builtin, or a
|
|
1939
|
-
browser or server package subpath;
|
|
1940
|
-
- a browser module reaching a Node builtin or a server subpath;
|
|
1941
|
-
- a server module reaching a stylesheet, Vue, or a browser subpath;
|
|
1942
|
-
- a workspace-relative import that resolves outside the workspace;
|
|
1943
|
-
- an HTML reference carrying `vite-ignore` that violates the same environment or containment law
|
|
1944
|
-
as an ordinary reference, a Vite `%ENV%` HTML substitution, a classic external script, or a
|
|
1945
|
-
computed dynamic import in the module graph that would bypass graph resolution;
|
|
1946
|
-
- a computed or expression-bearing `new URL` asset source that could escape at runtime;
|
|
1947
|
-
- malformed URI encoding, encoded traversal segments, or a local `file:` URL outside the owning
|
|
1948
|
-
environment/package root; file schemes are matched case-insensitively and converted to physical
|
|
1949
|
-
paths before containment.
|
|
1950
|
-
|
|
1951
|
-
Unsupported stylesheet `@import` or `url()` syntax is an error rather than a silently skipped
|
|
1952
|
-
dependency. **`publicDir` is disabled on every generated build target**: an asset that is not
|
|
1953
|
-
reachable through the module graph is not silently copied past the boundary. The output plugin
|
|
1954
|
-
fails during configuration when a caller attempts to enable `publicDir`. Published `srcBrowser`
|
|
1955
|
-
targets use Vite library mode, where assets are always inlined and `assetsInlineLimit` is ignored,
|
|
1956
|
-
so their generated shapes omit that ineffective option. The normal `appBrowser` build retains
|
|
1957
|
-
`assetsInlineLimit: 0`, and the output plugin rejects a nonzero limit only for that non-library
|
|
1958
|
-
browser build, keeping application asset bytes external and visible to output auditing before any
|
|
1959
|
-
output directory mutation. A caller-supplied Rolldown `output.dir` or `output.file` is also rejected
|
|
1960
|
-
during configuration; the exact generated `build.outDir` is the sole write root.
|
|
1961
|
-
|
|
1962
|
-
**The policy suite.** [`tests/setupPolicy.ts`](../../tests/setupPolicy.ts) is a narrow structural
|
|
1963
|
-
policy pass built on the official TypeScript compiler. It exists for exactly the laws a linter
|
|
1964
|
-
cannot express — that a centralized module exports every top-level declaration it holds, that
|
|
1965
|
-
implementation files hold one class and no stray module-scope declaration, that no function is
|
|
1966
|
-
declared inside another function outside a directly-passed callback, that interface properties are
|
|
1967
|
-
readonly, that privacy is a runtime `#` field rather than a TypeScript modifier, that a barrel
|
|
1968
|
-
re-exports only through `export *`, that a core source never names a worker-only global the
|
|
1969
|
-
`WebWorker` declarations expose, and that a computed dynamic import cannot smuggle a
|
|
1970
|
-
cross-environment dependency past the declared import rules. Vue components are inspected for the
|
|
1971
|
-
same evasions. A self-contained runtime entrypoint may be exempt from module-scope placement only
|
|
1972
|
-
when it is not a centralized kind file and has at least one real `node:` value import. Erased
|
|
1973
|
-
type-only imports may reference sibling contracts; any non-`node:` static value import,
|
|
1974
|
-
`export … from` re-export, or dynamic `import(...)` disqualifies the exemption. An importless file
|
|
1975
|
-
does not qualify, and centralized declarations remain subject to their export law. Every other
|
|
1976
|
-
policy law still applies. It is a complement to lint and typecheck, never a second type system, and
|
|
1977
|
-
it is not a general-purpose source analyzer. Generated workspaces receive the same exported policy
|
|
1978
|
-
module as a host-origin file and run it as a dedicated Node-only `policy` test project over
|
|
1979
|
-
`tests/policy.test.ts`.
|
|
1980
|
-
|
|
1981
|
-
**Fleet policy purity.** Both policy files are fleet-owned: scaffold copies its own
|
|
1982
|
-
[`tests/setupPolicy.ts`](../../tests/setupPolicy.ts) into every workspace verbatim and regenerates
|
|
1983
|
-
[`tests/policy.test.ts`](../../tests/policy.test.ts) from the shipped template, so a local addition
|
|
1984
|
-
to either is discarded at the next bump. A second pass therefore guards them against accumulating
|
|
1985
|
-
any one package's architecture. It derives the forbidden identifier tokens from the consuming
|
|
1986
|
-
workspace's own declared package name — the short name's upper-snake and Pascal spellings, deduped —
|
|
1987
|
-
so the pass states no package literal and can never report itself. It reports every identifier that
|
|
1988
|
-
begins with one of those tokens, and it rejects any string or template literal naming a
|
|
1989
|
-
source-environment path under the `src/` prefix. Policy names an environment without that prefix
|
|
1990
|
-
when it must name one at all, so the rule does not fight the files' real needs. The generated test
|
|
1991
|
-
sweeps both files and plants a violation built from the same derived token, so a clean sweep is
|
|
1992
|
-
evidence rather than an instrument that has never fired.
|
|
1993
|
-
|
|
1994
|
-
**Why the token match is a prefix.** A package's architecture reaches these files as identifiers
|
|
1995
|
-
named for the package — `MCP_PATH`, `RouterPlan` — so the identifier must _begin_ with the token.
|
|
1996
|
-
A word that merely holds the token somewhere inside it is fleet vocabulary, not one package's
|
|
1997
|
-
architecture, and the pass leaves it alone. That is the rule on its own terms, and it also settles
|
|
1998
|
-
the collision the fleet actually has: `@orkestrel/contract` derives `Contract`, which
|
|
1999
|
-
[`tests/setupPolicy.ts`](../../tests/setupPolicy.ts) holds inside `isContractProperty`, and that
|
|
2000
|
-
workspace now adopts the pass unchanged. The match stays case-sensitive, so `mcpValue` is not the
|
|
2001
|
-
`MCP` token.
|
|
2002
|
-
|
|
2003
|
-
One collision stays open, and its report is correct rather than a false positive. `@orkestrel/policy`
|
|
2004
|
-
derives `POLICY`, which both files use as an identifier prefix many times over — the pass would be
|
|
2005
|
-
reporting the vocabulary it is built from. A workspace cannot be named for that and adopt this pass;
|
|
2006
|
-
it renames or omits the pass. Every other name in the line derives tokens no policy identifier
|
|
2007
|
-
begins with.
|
|
2008
|
-
|
|
2009
|
-
**The configuration suite.** Policy reads source, the `config` project exercises the root
|
|
2010
|
-
configuration, and integration builds for real. Every generated workspace therefore receives a
|
|
2011
|
-
universal Node-only
|
|
2012
|
-
`config` project over `tests/config/**/*.test.ts`. Its base cases execute the root module's physical
|
|
2013
|
-
workspace containment and environment-direction helpers; conditional cases exercise output
|
|
2014
|
-
containment when the workspace builds, managed/system browser discovery when a browser environment
|
|
2015
|
-
exists, and the HTML/CSP boundary only for an application browser. Those cases import the generated
|
|
2016
|
-
root `vite.config.ts` itself, so a failure is repaired in the generator rather than patched into a
|
|
2017
|
-
consumer. The generated-consumer integration matrix remains the fidelity boundary for real builds;
|
|
2018
|
-
the configuration suite supplies deterministic edge coverage without duplicating build orchestration.
|
|
2019
|
-
When scaffold changes a generated configuration invariant, an existing consumer's `vite.config.ts`
|
|
2020
|
-
is intentionally reported stale until that consumer accepts the regenerated configuration and its
|
|
2021
|
-
matching config test.
|
|
2022
|
-
|
|
2023
|
-
**Real browser capability.** Browser test projects are gated on one centralized discovery chain:
|
|
2024
|
-
Playwright's pinned Chromium executable first, then a managed Chromium alias or cached revision,
|
|
2025
|
-
then stable system Chrome, then stable system Edge. Managed candidates must be executable regular
|
|
2026
|
-
files. System channels are selected only when their executable exists at Playwright's standard
|
|
2027
|
-
Linux, macOS, or Windows installation location; custom installations are not guessed. The generated
|
|
2028
|
-
configuration test consumes the same discovery helpers and accepts either an executable managed path or the
|
|
2029
|
-
stable `chrome` / `msedge` channel, so it does not maintain a second heuristic.
|
|
2030
|
-
|
|
2031
|
-
A browser suite runs when any one of those real browser capabilities is available and is skipped
|
|
2032
|
-
honestly when none is, rather than being faked. The gate is applied at registration, not inside the
|
|
2033
|
-
real browser project: without a browser, each browser factory is replaced by a same-label
|
|
2034
|
-
Node/no-test placeholder, so generated `--project <label>` and `--project=<label>` filters still
|
|
2035
|
-
resolve while no browser code runs. The root permits an empty run only when every recognized exact
|
|
2036
|
-
project filter names one of those gated placeholders; an unreadable or mixed filter keeps the
|
|
2037
|
-
ordinary no-test failure semantics for its Node projects. One printed warning names every gated
|
|
2038
|
-
project label and says no Playwright Chromium, Chrome, or Edge was found. A machine with a browser
|
|
2039
|
-
registers and runs the real browser suites unchanged; a machine without one runs the remaining
|
|
2040
|
-
projects and says so.
|
|
2041
|
-
|
|
2042
|
-
**Consumer-owned global setup.** The single mechanism-named `tests/setupGlobal.ts` module may
|
|
2043
|
-
prepare a shared integration registry, a real Node-side counterpart for source-browser tests such
|
|
2044
|
-
as a WebSocket fixture server, or both. The scaffold does not emit or replace it. Derivation
|
|
2045
|
-
records its exact-case physical presence as `global`, the single governing fact. Integration
|
|
2046
|
-
consumes it only when `bin` and `integration` are also true; a declared `src/browser` independently
|
|
2047
|
-
wires it to `srcBrowser`. Removing the file removes both eligible rows from regenerated
|
|
2048
|
-
configuration byte-for-byte. Application browser, styles, and service readiness setup remain
|
|
2049
|
-
isolated from this seam.
|
|
2050
|
-
|
|
2051
|
-
**Continuous integration.** The generated workflow runs on push and pull request, on
|
|
2052
|
-
`ubuntu-latest`, with read-only contents permission, a 60-minute timeout, and a matrix that **tests
|
|
2053
|
-
Node `22.12.0` and `26`** with fail-fast disabled. Checkout and Node setup are pinned to immutable
|
|
2054
|
-
action commits, and checkout does not persist credentials. Dependencies install with
|
|
2055
|
-
`npm ci --ignore-scripts`; Chromium is installed only when the workspace selects a browser environment
|
|
2056
|
-
or builds its own executable. The gates then run in order: `format:check`, `lint:check`, `check`,
|
|
2057
|
-
`build`, `test`, and the workspace's selected proofs follow as their own named steps, in the order
|
|
2058
|
-
the proof-gating table gives them.
|
|
2059
|
-
|
|
2060
|
-
**Agent orchestration files.** The session hooks in the generated `.claude/settings.json` run the
|
|
2061
|
-
dependency, model, and external-tool readiness scripts at session start. The **`Stop` hook runs only
|
|
2062
|
-
`git diff --check`** — a whitespace and conflict-marker check over the working tree, nothing more.
|
|
2063
|
-
Bash invocation and sensitive reads are controlled by the **settings permission list, not by a guard
|
|
2064
|
-
script**. The allow list is closed and holds exactly two entries — `Bash(codex --version)` and
|
|
2065
|
-
`Bash(codex login *)` — because the orchestration contract requires a bench-liveness probe and a
|
|
2066
|
-
device-login recovery at session start, and prompting for those would stall every session before
|
|
2067
|
-
planning. Every other Bash command requires explicit approval, including commands Claude Code
|
|
2068
|
-
otherwise classifies as read-only. That list is inherited by every workspace in the line, so a
|
|
2069
|
-
machine-local grant belongs in `settings.local.json`, which `SENSITIVE_HOST_PATH_PATTERN` keeps out
|
|
2070
|
-
of every vendored host. Read-only reviewer, checker, and ecosystem roles carry no Bash tool; the
|
|
2071
|
-
orchestrator supplies their diff and status evidence. Bridge, writer, and verifier roles request
|
|
2072
|
-
approval when their bounded shell work is needed. Read patterns covering environment files,
|
|
2073
|
-
package-manager credentials, credential stores, private keys, key stores, SSH, cloud credentials,
|
|
2074
|
-
container configuration, `.kube`, kubeconfig, and service-account JSON are denied. There is no
|
|
2075
|
-
guard script in the vendored set, and none is expected.
|
|
2076
|
-
|
|
2077
|
-
The generated `.codex/config.toml` and `.codex/agents/` mirror the same bounded research, design,
|
|
2078
|
-
implementation, checking, and review roles for Codex. Codex has no repository settings/hook file:
|
|
2079
|
-
each Codex agent's declared `sandbox_mode` is its mechanical permission floor, while the shared
|
|
2080
|
-
`AGENTS.md`, rules, and skills provide the same writing and acceptance contract to both providers.
|
|
2081
|
-
|
|
2082
|
-
## The `scaffold` executable
|
|
2083
|
-
|
|
2084
|
-
The bin is a thin command-line shell over the two library faces. It exports nothing, so it carries
|
|
2085
|
-
no module API of its own. Seven verbs:
|
|
2086
|
-
|
|
2087
|
-
| Verb | Purpose |
|
|
2088
|
-
| --------- | -------------------------------------------------------- |
|
|
2089
|
-
| `new` | scaffold a workspace into `./<name>` |
|
|
2090
|
-
| `pull` | refresh vendored guides and versions, report drift |
|
|
2091
|
-
| `mirror` | refresh every published Orkestrel package guide |
|
|
2092
|
-
| `audit` | whole-plan conformance report |
|
|
2093
|
-
| `repair` | restore missing canon; optionally replace drifted bytes |
|
|
2094
|
-
| `fleet` | audit or repair every workspace under the cwd's children |
|
|
2095
|
-
| `catalog` | regenerate the fleet package-catalog table |
|
|
2096
|
-
|
|
2097
|
-
**Environment selection.** `new` takes `--src a,b` for published library environments and
|
|
2098
|
-
`--app a,b` for private application environments. They are independent: `--src core,server` builds a library,
|
|
2099
|
-
`--app core,browser,server` builds an application, and passing both builds a mixed workspace. Each
|
|
2100
|
-
accepts any subset of `core`, `browser`, and `server`, and the gate rejects the one combination that
|
|
2101
|
-
has no defined configuration class. `--deps x,y` adds runtime dependencies and requires each flag
|
|
2102
|
-
token to use its full valid package name; only the interactive dependency prompt expands an
|
|
2103
|
-
Orkestrel short name. Other npm packages are not a creation-time flag — add them to the generated
|
|
2104
|
-
manifest's development dependencies afterwards, and they round-trip through `deriveBlueprint`'s
|
|
2105
|
-
extras so the workspace stays audit-clean.
|
|
2106
|
-
|
|
2107
|
-
**Other flags.** `--target <path>` selects the directory a single-workspace verb operates on;
|
|
2108
|
-
`fleet --target` is a usage error because fleet's root is always the current directory.
|
|
2109
|
-
`--from <path>` points at a local template source instead of the bundled one and may be passed once
|
|
2110
|
-
to those verbs. It is repeatable only for `catalog`, where each occurrence adds one catalog source.
|
|
2111
|
-
On `pull`, `--deps x,y` limits refresh to those declared Orkestrel dependencies; without it, every
|
|
2112
|
-
declared dependency mirror is considered.
|
|
2113
|
-
`mirror` accepts no dependency selection: its exact npm organization discovery is the operation's
|
|
2114
|
-
scope, and it fetches guides without registry version or packument requests.
|
|
2115
|
-
`--groups a,b` scopes an audit to artifact groups. `--live` adds an upstream freshness check to an
|
|
2116
|
-
audit. `--strict` makes a pull or mirror throw on a network fault. `--offline` restricts a catalog to local
|
|
2117
|
-
sources. `--prune` opts a repair or fleet run into deleting unexpected files under the three prune
|
|
2118
|
-
directories. `--generated` opts a repair or fleet run into including generated canon while
|
|
2119
|
-
protecting `package.json` outside its generated service-script keys; on `audit`, it is inherited if
|
|
2120
|
-
the interactive repair hand-off is accepted.
|
|
2121
|
-
`--replace` authorizes repair to discard local changes in the drifted files named by its report; it
|
|
2122
|
-
composes with `--generated`, and is likewise inherited by an accepted audit hand-off.
|
|
2123
|
-
`--json` emits one machine-readable value. `--apply` writes, `--yes` skips the confirmation, and
|
|
2124
|
-
`-h` or `--help` prints usage.
|
|
2125
|
-
|
|
2126
|
-
**Safety model.** Every verb is a dry run by default. `--apply` is the sole write authorization;
|
|
2127
|
-
`--yes` only skips a confirmation and never authorizes a write or deletion by itself. On a terminal
|
|
2128
|
-
an authorized write asks for confirmation first, defaulting to no; scripts do not prompt. Every write is
|
|
2129
|
-
confined to the working directory, so the instruction is to change into it first rather than to pass
|
|
2130
|
-
a root. `repair` asks a second, separately defaulted question before deleting anything, and a
|
|
2131
|
-
session without `--apply` skips pruning regardless of `--yes`. `fleet` operates on the immediate
|
|
2132
|
-
children of the working directory and never on the directory itself. It has no root flag at all:
|
|
2133
|
-
passing `--target` is rejected with exit `2` instead of being silently ignored. `repair` is the
|
|
2134
|
-
single-workspace tool.
|
|
2135
|
-
|
|
2136
|
-
`fleet` and default `repair` are scoped to host-origin artifacts plus absent service-owned starter
|
|
2137
|
-
seams. Both state that selected scope in the output before they act — `repair` once its audit found
|
|
2138
|
-
something to repair, `fleet` once `--apply` authorized a write, naming the number of repositories
|
|
2139
|
-
that write covers. `--generated` widens both verbs to generated
|
|
2140
|
-
files and the manifest's generated service-script keys while still excluding present starter files
|
|
2141
|
-
and package publication metadata. Within either scope, missing files are safe to restore, stale
|
|
2142
|
-
files are report-only by default, and `--replace` is the explicit destructive opt-in.
|
|
2143
|
-
|
|
2144
|
-
**Catalog markers.** `catalog` rewrites the block between `<!-- catalog:start -->` and
|
|
2145
|
-
`<!-- catalog:end -->` in `CATALOG_AGENT_PATH`. **Ambiguous markers fail before any
|
|
2146
|
-
mutation**: the file must contain exactly one ordered pair. A missing marker, a reversed pair, or a
|
|
2147
|
-
repeated marker of either kind is a coded `TARGET` failure raised before the file is touched, and
|
|
2148
|
-
the run reports the drift and any row-count shrink rather than rewriting a file it cannot bound.
|
|
2149
|
-
|
|
2150
|
-
**Certificates.** **When the running Node release exposes the system-CA APIs**, the executable
|
|
2151
|
-
merges the operating system trust store into the default certificates, so fetches behind a
|
|
2152
|
-
TLS-inspecting proxy behave like other tooling instead of failing against the bundled list alone.
|
|
2153
|
-
The check is a feature detection: **earlier supported Node 22 releases simply use Node's default
|
|
2154
|
-
roots**. It only ever adds trusted issuers — nothing disables verification — and a failure is a
|
|
2155
|
-
silent no-op rather than a crash. Custom PEMs are added through the standard environment variable.
|
|
2156
|
-
|
|
2157
|
-
**Exit codes.** `0` is clean or successful, `1` is drift or failure, `2` is a usage error. Repair
|
|
2158
|
-
and fleet use the same dirty-repository predicate: selected-scope drift or any full-plan finding
|
|
2159
|
-
outside that scope keeps exit `1`. A repair that skips stale files therefore exits `1`; a repair
|
|
2160
|
-
exits `0` only when its selected audit and its reported outside scope are both clean. An audit exits
|
|
2161
|
-
non-zero on any drift, foreign files included, which makes it usable directly as a CI gate.
|
|
2162
|
-
`repair --json` carries that same terminal audit after any authorized write, while its `result`
|
|
2163
|
-
records the files the write copied, wrote, skipped, and removed. A pull exits non-zero on any drift
|
|
2164
|
-
or failure whether or not `--strict` was passed, including when
|
|
2165
|
-
other entries were applied successfully; `--strict` additionally throws on a network fault. Every
|
|
2166
|
-
unknown verb is a usage error and gets a nearest-match
|
|
2167
|
-
suggestion when one is sufficiently close.
|
|
2168
|
-
|
|
2169
|
-
## Package contents
|
|
2170
|
-
|
|
2171
|
-
The published package is `@orkestrel/scaffold`. Its entry points are the core barrel at `.` and the
|
|
2172
|
-
server barrel at `./server`, both with dual import and require conditions and matching declaration
|
|
2173
|
-
files, plus `./package.json`. The `scaffold` binary maps to the built executable.
|
|
2174
|
-
|
|
2175
|
-
The published file set is exactly `dist/src`, `dist/bin`, `dist/host`, and `README.md`. `dist/host`
|
|
2176
|
-
is the vendored data root: the byte-preserved host files plus the `manifest.json` recording their
|
|
2177
|
-
storage names, destinations, executable bits, directory roots, and membership digest. Storage names
|
|
2178
|
-
are un-dotted, because a leading dot
|
|
2179
|
-
does not survive packaging intact; the manifest is what maps a storage name back to its real
|
|
2180
|
-
destination. That is also why the default host is resolved from the installed module's own
|
|
2181
|
-
location — the package carries its host data with itself, and a caller-supplied raw repository root
|
|
2182
|
-
is the explicit alternative, mapping sources 1:1 with no manifest indirection.
|
|
2183
|
-
|
|
2184
|
-
Six runtime dependencies, all scoped: the contract toolkit behind the shape, guard, parser, and
|
|
2185
|
-
safe-attempt primitives; the emitter behind every entity's observation channel; the markdown AST and
|
|
2186
|
-
renderer behind the table and blockquote work; the template engine behind every template-origin
|
|
2187
|
-
artifact; and, consumed only at the executable boundary, the terminal prompt toolkit and the console
|
|
2188
|
-
reporter. The core face uses the first four and stays pure; the server face adds only `node:*`
|
|
2189
|
-
builtins. Development dependencies are the shared tooling baseline plus the guide-parity toolkit
|
|
2190
|
-
that drives [`parity.test.ts`](../../tests/guides/src/parity.test.ts) and `@orkestrel/html`,
|
|
2191
|
-
which this package's real emitted-configuration tests execute. Generated manifests keep that HTML
|
|
2192
|
-
dependency scoped to `app/browser`; source-only, `app/core`, and `app/server` workspaces do not
|
|
2193
|
-
receive it. The engines floor is Node
|
|
2194
|
-
`>=22.12.0`, and the build emits ES and CJS for both library faces plus an ES executable.
|
|
2195
|
-
|
|
2196
|
-
## Patterns
|
|
2197
|
-
|
|
2198
|
-
### Authoring and validating a blueprint
|
|
2199
|
-
|
|
2200
|
-
```ts
|
|
2201
|
-
import {
|
|
2202
|
-
blueprint,
|
|
2203
|
-
blueprintToMembers,
|
|
2204
|
-
createBlueprint,
|
|
2205
|
-
dependency,
|
|
2206
|
-
hasBlueprintEnvironment,
|
|
2207
|
-
hasValidBlueprintBytes,
|
|
2208
|
-
hasValidOverrideBytes,
|
|
2209
|
-
isWorkspaceName,
|
|
2210
|
-
member,
|
|
2211
|
-
override,
|
|
2212
|
-
pascalCase,
|
|
2213
|
-
validateBlueprint,
|
|
2214
|
-
validateDependencyArray,
|
|
2215
|
-
} from '@orkestrel/scaffold'
|
|
2216
|
-
|
|
2217
|
-
const spec = blueprint('router', {
|
|
2218
|
-
src: ['core', 'browser'],
|
|
2219
|
-
dependencies: [dependency('@orkestrel/contract', '^0.0.7')],
|
|
2220
|
-
peers: [dependency('@orkestrel/server', '^0.0.3', true)],
|
|
2221
|
-
overrides: [override('README.md', '# router\n')],
|
|
2222
|
-
})
|
|
2223
|
-
|
|
2224
|
-
pascalCase('my-router') // 'MyRouter'
|
|
2225
|
-
isWorkspaceName('router') // true
|
|
2226
|
-
hasBlueprintEnvironment(spec) // true
|
|
2227
|
-
hasValidBlueprintBytes(spec) // true
|
|
2228
|
-
hasValidOverrideBytes(override('README.md', '# router\n')) // true
|
|
2229
|
-
validateDependencyArray('dependencies', spec.dependencies).questions // []
|
|
2230
|
-
validateBlueprint(spec).valid // true
|
|
2231
|
-
blueprintToMembers(spec)[0] // { name: 'Router', category: 'entity', … }
|
|
2232
|
-
member('RouterOptions', 'type', 'Options for creating a Router.')
|
|
2233
|
-
|
|
2234
|
-
// The validating constructor throws instead of returning questions.
|
|
2235
|
-
createBlueprint({ name: 'router', src: ['core'] })
|
|
2236
|
-
```
|
|
2237
|
-
|
|
2238
|
-
### Compiling, gating, and pinning
|
|
2239
|
-
|
|
2240
|
-
```ts
|
|
2241
|
-
import {
|
|
2242
|
-
applyOverrides,
|
|
2243
|
-
blueprint,
|
|
2244
|
-
blueprintToPlan,
|
|
2245
|
-
computeHash,
|
|
2246
|
-
createCompiler,
|
|
2247
|
-
hasValidArtifactBytes,
|
|
2248
|
-
hasValidArtifactHex,
|
|
2249
|
-
hasValidPlanBytes,
|
|
2250
|
-
hasValidPlanHex,
|
|
2251
|
-
pinPlan,
|
|
2252
|
-
planPayload,
|
|
2253
|
-
stableStringify,
|
|
2254
|
-
validatePlan,
|
|
2255
|
-
} from '@orkestrel/scaffold'
|
|
2256
|
-
|
|
2257
|
-
const compiler = createCompiler()
|
|
2258
|
-
const spec = blueprint('router', { src: ['core'] })
|
|
2259
|
-
|
|
2260
|
-
const scaffolding = compiler.compile(spec)
|
|
2261
|
-
scaffolding.stages.map((record) => record.stage) // ['draft', 'gate', 'pin']
|
|
2262
|
-
|
|
2263
|
-
const audit = compiler.audit(spec, {})
|
|
2264
|
-
audit.missing // every artifact — nothing exists at the target yet
|
|
2265
|
-
|
|
2266
|
-
const plan = pinPlan(blueprintToPlan(spec, ['manifest', 'configs']))
|
|
2267
|
-
validatePlan(plan).valid // true
|
|
2268
|
-
plan.trace?.includes('src:core · app:none') // true
|
|
2269
|
-
planPayload(plan) === planPayload({ ...plan, trace: 'ignored by identity' }) // true
|
|
2270
|
-
hasValidPlanHex(plan) // true
|
|
2271
|
-
hasValidPlanBytes(plan) // true
|
|
2272
|
-
plan.artifacts.every(hasValidArtifactHex) // true
|
|
2273
|
-
plan.artifacts.every(hasValidArtifactBytes) // true
|
|
2274
|
-
computeHash(stableStringify(plan.blueprint)) === computeHash(stableStringify(spec)) // true
|
|
2275
|
-
applyOverrides(plan.artifacts, spec.overrides).length // unchanged when nothing matches
|
|
2276
|
-
|
|
2277
|
-
compiler.destroy()
|
|
2278
|
-
```
|
|
2279
|
-
|
|
2280
|
-
### Registering plans by content hash
|
|
2281
|
-
|
|
2282
|
-
```ts
|
|
2283
|
-
import { blueprint, blueprintToPlan, createPlanManager } from '@orkestrel/scaffold'
|
|
2284
|
-
|
|
2285
|
-
const plans = createPlanManager()
|
|
2286
|
-
const record = plans.add(blueprintToPlan(blueprint('router', { src: ['core'] })))
|
|
2287
|
-
|
|
2288
|
-
record.id === record.hash // true — the id is minted from content
|
|
2289
|
-
record.version // 1
|
|
2290
|
-
plans.has(record.id) // true
|
|
2291
|
-
plans.plan(record.id) // the record
|
|
2292
|
-
plans.plans().length // 1
|
|
2293
|
-
plans.remove([record.id]) // true — all-or-nothing over a list
|
|
2294
|
-
plans.remove() // removes everything
|
|
2295
|
-
plans.destroy()
|
|
2296
|
-
```
|
|
2297
|
-
|
|
2298
|
-
### Projecting a plan, an audit, and a report
|
|
2299
|
-
|
|
2300
|
-
```ts
|
|
2301
|
-
import type { Audit, Plan, SyncReport } from '@orkestrel/scaffold'
|
|
2302
|
-
import {
|
|
2303
|
-
alignTable,
|
|
2304
|
-
auditToReview,
|
|
2305
|
-
catalogNames,
|
|
2306
|
-
catalogToBlock,
|
|
2307
|
-
delimiterCell,
|
|
2308
|
-
guideMemberTable,
|
|
2309
|
-
padCell,
|
|
2310
|
-
planToReview,
|
|
2311
|
-
planToSummary,
|
|
2312
|
-
splitTableRow,
|
|
2313
|
-
syncToReview,
|
|
2314
|
-
} from '@orkestrel/scaffold'
|
|
2315
|
-
|
|
2316
|
-
declare const plan: Plan
|
|
2317
|
-
declare const audit: Audit
|
|
2318
|
-
declare const report: SyncReport
|
|
2319
|
-
|
|
2320
|
-
alignTable(['API', 'Kind'], [['`createRouter`', 'function']])
|
|
2321
|
-
splitTableRow('| a | b |') // ['a', 'b']
|
|
2322
|
-
padCell('ab', 5) // 'ab '
|
|
2323
|
-
delimiterCell('left', 5) // ':----'
|
|
2324
|
-
|
|
2325
|
-
catalogToBlock([{ name: '@orkestrel/router', version: '0.0.5', description: '' }])
|
|
2326
|
-
catalogNames('| @orkestrel/router | 0.0.5 |') // ['@orkestrel/router']
|
|
2327
|
-
|
|
2328
|
-
planToSummary(plan).artifacts // the artifact count
|
|
2329
|
-
planToReview(plan) // the copy-ready dry-run review document
|
|
2330
|
-
auditToReview(audit) // findings grouped by drift, aligned entries elided
|
|
2331
|
-
syncToReview(report) // guides and versions, each in its own table
|
|
2332
|
-
guideMemberTable('entity', [])
|
|
2333
|
-
```
|
|
2334
|
-
|
|
2335
|
-
### Exact bytes, snapshots, and drift
|
|
2336
|
-
|
|
2337
|
-
```ts
|
|
2338
|
-
import type { Plan } from '@orkestrel/scaffold'
|
|
2339
|
-
import {
|
|
2340
|
-
bytesToHex,
|
|
2341
|
-
contentByteLength,
|
|
2342
|
-
contentCodePoint,
|
|
2343
|
-
contentToBytes,
|
|
2344
|
-
contentToHex,
|
|
2345
|
-
diffPlan,
|
|
2346
|
-
findFileConflict,
|
|
2347
|
-
findPathConflict,
|
|
2348
|
-
hasValidAuditBytes,
|
|
2349
|
-
hasValidSnapshotBytes,
|
|
2350
|
-
inferGroup,
|
|
2351
|
-
snapshotOf,
|
|
2352
|
-
} from '@orkestrel/scaffold'
|
|
2353
|
-
|
|
2354
|
-
declare const plan: Plan
|
|
2355
|
-
|
|
2356
|
-
contentCodePoint('a', 0) // 97
|
|
2357
|
-
contentByteLength('ab') // 2
|
|
2358
|
-
bytesToHex(contentToBytes('ab')) === contentToHex('ab') // true
|
|
2359
|
-
|
|
2360
|
-
const current = snapshotOf({ 'package.json': '{}\n' })
|
|
2361
|
-
hasValidSnapshotBytes(current) // true
|
|
2362
|
-
|
|
2363
|
-
const audit = diffPlan(plan, current)
|
|
2364
|
-
hasValidAuditBytes(audit) // true
|
|
2365
|
-
inferGroup('src/core/index.ts') // 'source'
|
|
2366
|
-
|
|
2367
|
-
findPathConflict(['a/b.ts', 'A/B.ts']) // the first case-insensitive collision
|
|
2368
|
-
findFileConflict(['a', 'a/b.ts']) // a file nested inside another planned path
|
|
2369
|
-
```
|
|
2370
|
-
|
|
2371
|
-
### Format-stable JSON and generated text
|
|
2372
|
-
|
|
2373
|
-
```ts
|
|
2374
|
-
import {
|
|
2375
|
-
compareCodeUnit,
|
|
2376
|
-
computeColumnWidth,
|
|
2377
|
-
escapeHtmlText,
|
|
2378
|
-
fitsPrintWidth,
|
|
2379
|
-
formatJson,
|
|
2380
|
-
renderArray,
|
|
2381
|
-
renderObject,
|
|
2382
|
-
renderValue,
|
|
2383
|
-
serializeTypeScriptString,
|
|
2384
|
-
} from '@orkestrel/scaffold'
|
|
2385
|
-
|
|
2386
|
-
formatJson({ lib: ['ESNext', 'DOM'] }) // '{\n\t"lib": ["ESNext", "DOM"]\n}\n'
|
|
2387
|
-
renderValue('ESNext', '', '', '') // '"ESNext"'
|
|
2388
|
-
renderArray(['ESNext', 'DOM'], '', '', '') // '["ESNext", "DOM"]'
|
|
2389
|
-
renderObject({ lib: ['ESNext'] }, '') // '{\n\t"lib": ["ESNext"]\n}'
|
|
2390
|
-
computeColumnWidth('\t"a"') // 3
|
|
2391
|
-
fitsPrintWidth('\t["ESNext"],') // true
|
|
2392
|
-
|
|
2393
|
-
escapeHtmlText('<app & "team">') // '<app & "team">'
|
|
2394
|
-
serializeTypeScriptString("app's") // "'app\\'s'"
|
|
2395
|
-
const sorted = ['b', 'a'].sort(compareCodeUnit) // ['a', 'b']
|
|
2396
|
-
```
|
|
2397
|
-
|
|
2398
|
-
### Shapes, guards, and parsers
|
|
2399
|
-
|
|
2400
|
-
```ts
|
|
2401
|
-
import {
|
|
2402
|
-
artifactShape,
|
|
2403
|
-
blueprintShape,
|
|
2404
|
-
dependencyShape,
|
|
2405
|
-
hasValidSyncReportBytes,
|
|
2406
|
-
isArtifact,
|
|
2407
|
-
isBlueprint,
|
|
2408
|
-
isCompilerEventHooks,
|
|
2409
|
-
isDependency,
|
|
2410
|
-
isMember,
|
|
2411
|
-
isOverride,
|
|
2412
|
-
isPlan,
|
|
2413
|
-
isPlanManagerEventHooks,
|
|
2414
|
-
isScaffoldError,
|
|
2415
|
-
isSyncReport,
|
|
2416
|
-
memberShape,
|
|
2417
|
-
overrideShape,
|
|
2418
|
-
ownDataValue,
|
|
2419
|
-
parseBoundedJSON,
|
|
2420
|
-
parseCompilerOptions,
|
|
2421
|
-
parseBlueprint,
|
|
2422
|
-
parsePlan,
|
|
2423
|
-
parsePlanIds,
|
|
2424
|
-
parsePlanManagerOptions,
|
|
2425
|
-
parseSyncReport,
|
|
2426
|
-
planShape,
|
|
2427
|
-
ScaffoldError,
|
|
2428
|
-
snapshotPlan,
|
|
2429
|
-
syncReportShape,
|
|
2430
|
-
} from '@orkestrel/scaffold'
|
|
2431
|
-
|
|
2432
|
-
declare const value: unknown
|
|
2433
|
-
|
|
2434
|
-
dependencyShape()
|
|
2435
|
-
overrideShape()
|
|
2436
|
-
blueprintShape()
|
|
2437
|
-
memberShape()
|
|
2438
|
-
artifactShape()
|
|
2439
|
-
planShape()
|
|
2440
|
-
syncReportShape()
|
|
2441
|
-
|
|
2442
|
-
isDependency({ name: '@orkestrel/contract', range: '^0.0.7' }) // true
|
|
2443
|
-
isOverride({ path: 'README.md', content: '# router\n' }) // true
|
|
2444
|
-
isMember({ name: 'Router', category: 'entity', summary: 'The Router entity.', environment: 'core' })
|
|
2445
|
-
isArtifact({ path: 'README.md', group: 'docs', origin: 'template', content: '# router\n' })
|
|
2446
|
-
ownDataValue({ name: 'router' }, 'name') // 'router'
|
|
2447
|
-
|
|
2448
|
-
parseBoundedJSON('"ready"', (candidate): candidate is string => typeof candidate === 'string', 7)
|
|
2449
|
-
parseCompilerOptions({ on: { destroy: () => undefined } })
|
|
2450
|
-
parseBlueprint('{"not":"a blueprint"}') // undefined — never throws
|
|
2451
|
-
parsePlan(undefined) // undefined
|
|
2452
|
-
parsePlanIds(['first', 'second']) // frozen owned ids
|
|
2453
|
-
parsePlanManagerOptions({ plans: [] }) // exact owned constructor options
|
|
2454
|
-
parseSyncReport('{}') // undefined
|
|
2455
|
-
const parsedPlan = parsePlan(value)
|
|
2456
|
-
if (parsedPlan !== undefined) Object.isFrozen(snapshotPlan(parsedPlan).blueprint)
|
|
2457
|
-
|
|
2458
|
-
if (isBlueprint(value)) value.src
|
|
2459
|
-
if (isPlan(value)) value.artifacts
|
|
2460
|
-
isCompilerEventHooks({ compile: () => undefined }) // true
|
|
2461
|
-
isPlanManagerEventHooks({ add: (id) => id.length > 0 }) // true
|
|
2462
|
-
if (isSyncReport(value)) hasValidSyncReportBytes(value)
|
|
2463
|
-
|
|
2464
|
-
try {
|
|
2465
|
-
throw new ScaffoldError('INVALID', 'Blueprint failed the exact-record contract')
|
|
2466
|
-
} catch (error) {
|
|
2467
|
-
if (isScaffoldError(error)) error.code // 'INVALID'
|
|
2468
|
-
}
|
|
2469
|
-
```
|
|
2470
|
-
|
|
2471
|
-
### Drafting artifacts group by group
|
|
2472
|
-
|
|
2473
|
-
```ts
|
|
2474
|
-
import {
|
|
2475
|
-
applicationArtifacts,
|
|
2476
|
-
blueprint,
|
|
2477
|
-
blueprintToMembers,
|
|
2478
|
-
ciWorkflow,
|
|
2479
|
-
configArtifacts,
|
|
2480
|
-
devDependenciesFor,
|
|
2481
|
-
dualCondition,
|
|
2482
|
-
entryFields,
|
|
2483
|
-
exportsMap,
|
|
2484
|
-
fillArtifact,
|
|
2485
|
-
guideArtifacts,
|
|
2486
|
-
guideMethods,
|
|
2487
|
-
guideTests,
|
|
2488
|
-
guideUsage,
|
|
2489
|
-
hostGroup,
|
|
2490
|
-
packageManifest,
|
|
2491
|
-
paritySpecifiers,
|
|
2492
|
-
selectHostPaths,
|
|
2493
|
-
sourceArtifacts,
|
|
2494
|
-
srcVariant,
|
|
2495
|
-
testArtifacts,
|
|
2496
|
-
} from '@orkestrel/scaffold'
|
|
2497
|
-
|
|
2498
|
-
const spec = blueprint('router', { src: ['core'], app: ['core', 'server'] })
|
|
2499
|
-
const members = blueprintToMembers(spec)
|
|
2500
|
-
|
|
2501
|
-
hostGroup('AGENTS.md') // 'docs'
|
|
2502
|
-
selectHostPaths(['guides/src/router.md', 'LICENSE'], spec.name) // ['LICENSE'] — never its own guide
|
|
2503
|
-
srcVariant(['core', 'server']) // 'multi'
|
|
2504
|
-
entryFields(['browser']).main // './dist/src/browser/index.js'
|
|
2505
|
-
dualCondition('./dist/src/core/index')
|
|
2506
|
-
exportsMap(['core'])['.']
|
|
2507
|
-
devDependenciesFor(spec).typescript
|
|
2508
|
-
packageManifest(spec) // the whole manifest, newline-terminated
|
|
2509
|
-
|
|
2510
|
-
configArtifacts(spec).length
|
|
2511
|
-
sourceArtifacts(spec, 'Router').length
|
|
2512
|
-
applicationArtifacts(spec).length
|
|
2513
|
-
testArtifacts(spec, 'Router').length
|
|
2514
|
-
guideArtifacts(spec, 'Router', members).length
|
|
2515
|
-
paritySpecifiers(spec).includes('SELF_SPECIFIERS') // true
|
|
2516
|
-
guideUsage(spec, 'Router')
|
|
2517
|
-
guideMethods(spec)
|
|
2518
|
-
guideTests(spec, 'Router')
|
|
2519
|
-
ciWorkflow(spec).includes("node: ['22.12.0', '26']") // true
|
|
2520
|
-
|
|
2521
|
-
fillArtifact('README.md', 'docs', 'readme', {
|
|
2522
|
-
name: 'router',
|
|
2523
|
-
title: '@orkestrel/router',
|
|
2524
|
-
description: 'A tiny hash router.',
|
|
2525
|
-
install: '',
|
|
2526
|
-
usage: '',
|
|
2527
|
-
})
|
|
2528
|
-
```
|
|
2529
|
-
|
|
2530
|
-
### Emitting the generated build and check configuration
|
|
2531
|
-
|
|
2532
|
-
```ts
|
|
2533
|
-
import {
|
|
2534
|
-
appTsconfig,
|
|
2535
|
-
appViteConfig,
|
|
2536
|
-
applicationViteConfig,
|
|
2537
|
-
binViteProject,
|
|
2538
|
-
configViteProject,
|
|
2539
|
-
coreTsconfig,
|
|
2540
|
-
coreViteConfig,
|
|
2541
|
-
guidesViteProject,
|
|
2542
|
-
integrationViteProject,
|
|
2543
|
-
policyViteProject,
|
|
2544
|
-
renderViteTest,
|
|
2545
|
-
rootTsconfig,
|
|
2546
|
-
rootViteConfig,
|
|
2547
|
-
serviceViteProject,
|
|
2548
|
-
singleSrcViteConfig,
|
|
2549
|
-
srcTsconfig,
|
|
2550
|
-
srcViteConfig,
|
|
2551
|
-
viteHeader,
|
|
2552
|
-
viteMachinery,
|
|
2553
|
-
viteProjectDefinitions,
|
|
2554
|
-
viteProjectRegistrations,
|
|
2555
|
-
} from '@orkestrel/scaffold'
|
|
2556
|
-
|
|
2557
|
-
rootTsconfig(['core'], ['core', 'server'])
|
|
2558
|
-
coreTsconfig()
|
|
2559
|
-
srcTsconfig('server')
|
|
2560
|
-
appTsconfig('browser', true)
|
|
2561
|
-
|
|
2562
|
-
viteMachinery(['core']) // { browser: false, vue: false, output: true, showcase: false }
|
|
2563
|
-
viteMachinery([], ['core', 'browser']) // { browser: true, vue: true, output: true, showcase: false }
|
|
2564
|
-
renderViteTest([{ project: 'srcCore' }], false).includes('projects: [srcCore]') // true
|
|
2565
|
-
viteHeader(viteMachinery([], ['core', 'browser'])) // the shared header, with browser and Vue support
|
|
2566
|
-
coreViteConfig()
|
|
2567
|
-
srcViteConfig('browser', { name: 'router', src: ['core', 'browser'] })
|
|
2568
|
-
appViteConfig('server')
|
|
2569
|
-
policyViteProject()
|
|
2570
|
-
configViteProject()
|
|
2571
|
-
guidesViteProject()
|
|
2572
|
-
binViteProject()
|
|
2573
|
-
integrationViteProject({ bin: true, integration: true, global: true })
|
|
2574
|
-
serviceViteProject('claude')
|
|
2575
|
-
viteProjectDefinitions({ integration: true, services: ['claude'] }).includes(
|
|
2576
|
-
'export const serviceClaude =',
|
|
2577
|
-
) // true
|
|
2578
|
-
viteProjectRegistrations(['core'], [], { integration: true, services: ['claude'] }).map(
|
|
2579
|
-
({ project }) => project,
|
|
2580
|
-
)
|
|
2581
|
-
// ['srcCore', 'policy', 'config', 'guides', 'integration', 'serviceClaude']
|
|
2582
|
-
|
|
2583
|
-
rootViteConfig(['core', 'server'], { bin: true })
|
|
2584
|
-
singleSrcViteConfig('server').includes('srcServer') // true
|
|
2585
|
-
applicationViteConfig([], ['core', 'server']).includes('appServer') // true
|
|
2586
|
-
```
|
|
2587
|
-
|
|
2588
|
-
### Reading declared dependencies and comparing freshness
|
|
2589
|
-
|
|
2590
|
-
```ts
|
|
2591
|
-
import {
|
|
2592
|
-
isBehind,
|
|
2593
|
-
manifestToDependencies,
|
|
2594
|
-
manifestToName,
|
|
2595
|
-
rangeToFreshness,
|
|
2596
|
-
} from '@orkestrel/scaffold'
|
|
2597
|
-
import {
|
|
2598
|
-
guideStub,
|
|
2599
|
-
packageShortName,
|
|
2600
|
-
readGuideReferences,
|
|
2601
|
-
syncReportOf,
|
|
2602
|
-
} from '@orkestrel/scaffold/server'
|
|
2603
|
-
|
|
2604
|
-
manifestToDependencies('{"dependencies":{"@orkestrel/contract":"^0.0.7"}}')
|
|
2605
|
-
manifestToName('{"name":"@orkestrel/router"}') // '@orkestrel/router' — the target's own name
|
|
2606
|
-
rangeToFreshness('^0.0.7', '0.0.7') // 'current'
|
|
2607
|
-
isBehind(rangeToFreshness('^0.0.7', '0.0.9')) // true
|
|
2608
|
-
|
|
2609
|
-
packageShortName('@orkestrel/contract') // 'contract'
|
|
2610
|
-
guideStub('guides/src/contract.md') // the local pointer content
|
|
2611
|
-
readGuideReferences('./packages/router', ['@orkestrel/contract'])
|
|
2612
|
-
syncReportOf('./packages/router', [], []) // { clean: true, failed: 0, … }
|
|
2613
|
-
```
|
|
2614
|
-
|
|
2615
|
-
### Materializing, repairing, and pruning a target
|
|
2616
|
-
|
|
2617
|
-
```ts
|
|
2618
|
-
import { blueprint, blueprintToPlan, diffPlan } from '@orkestrel/scaffold'
|
|
2619
|
-
import {
|
|
2620
|
-
createMaterializer,
|
|
2621
|
-
digestHostManifest,
|
|
2622
|
-
hostRoot,
|
|
2623
|
-
hydratePlan,
|
|
2624
|
-
isVacant,
|
|
2625
|
-
locateHostSource,
|
|
2626
|
-
readHostManifest,
|
|
2627
|
-
readManifest,
|
|
2628
|
-
readTarget,
|
|
2629
|
-
remapArtifactPath,
|
|
2630
|
-
stageHost,
|
|
2631
|
-
storagePath,
|
|
2632
|
-
} from '@orkestrel/scaffold/server'
|
|
2633
|
-
|
|
2634
|
-
const host = hostRoot()
|
|
2635
|
-
digestHostManifest([], []) // exact empty manifest membership digest
|
|
2636
|
-
readHostManifest(host) // the vendored manifest, or undefined for a raw root
|
|
2637
|
-
storagePath('.claude/agents/reviewer.md') // 'claude/agents/reviewer.md'
|
|
2638
|
-
locateHostSource(undefined, 'package.json', host)
|
|
2639
|
-
|
|
2640
|
-
const plan = hydratePlan(blueprintToPlan(blueprint('router', { src: ['core'] })), host)
|
|
2641
|
-
remapArtifactPath(
|
|
2642
|
-
{ path: '.claude/agents', group: 'orchestration', origin: 'host' },
|
|
2643
|
-
'.claude/agents',
|
|
2644
|
-
)
|
|
2645
|
-
|
|
2646
|
-
const materializer = createMaterializer()
|
|
2647
|
-
isVacant('./packages/router-new') // true — absent, empty, or only a .git dir
|
|
2648
|
-
materializer.materialize(plan, './packages/router-new')
|
|
2649
|
-
|
|
2650
|
-
readManifest('./packages/router')
|
|
2651
|
-
const current = readTarget(
|
|
2652
|
-
'./packages/router',
|
|
2653
|
-
plan.artifacts.map((artifact) => artifact.path),
|
|
2654
|
-
)
|
|
2655
|
-
materializer.repair(plan, diffPlan(plan, current), './packages/router') // missing only; stale is skipped
|
|
2656
|
-
materializer.repair(plan, diffPlan(plan, current), './packages/router', true) // replace stale bytes
|
|
2657
|
-
materializer.prune('./packages/router', {})
|
|
2658
|
-
materializer.destroy()
|
|
2659
|
-
|
|
2660
|
-
stageHost(process.cwd(), 'dist/host').length // the number of files staged
|
|
2661
|
-
```
|
|
2662
|
-
|
|
2663
|
-
### Pulling guides and versions
|
|
2664
|
-
|
|
2665
|
-
```ts
|
|
2666
|
-
import { createSync } from '@orkestrel/scaffold/server'
|
|
2667
|
-
|
|
2668
|
-
const sync = createSync({ concurrency: 4, retries: 1 })
|
|
2669
|
-
|
|
2670
|
-
await sync.lookup(['@orkestrel/contract'])
|
|
2671
|
-
const report = await sync.pull('.')
|
|
2672
|
-
if (report.failed === 0) await sync.write(report, '.')
|
|
2673
|
-
|
|
2674
|
-
const deps = [{ name: '@orkestrel/contract', range: '^0.0.7' }]
|
|
2675
|
-
await sync.guides(deps)
|
|
2676
|
-
await sync.versions(deps)
|
|
2677
|
-
await sync.catalog()
|
|
2678
|
-
|
|
2679
|
-
const mirror = await sync.mirror('.')
|
|
2680
|
-
if (mirror.failed === 0) await sync.write(mirror, '.')
|
|
2681
|
-
|
|
2682
|
-
sync.destroy()
|
|
2683
|
-
```
|
|
2684
|
-
|
|
2685
|
-
Refresh the entire published guide mirror from an installed package:
|
|
2686
|
-
|
|
2687
|
-
```sh
|
|
2688
|
-
npx scaffold mirror --apply --yes
|
|
2689
|
-
```
|
|
2690
|
-
|
|
2691
|
-
Or from this checkout after building:
|
|
2692
|
-
|
|
2693
|
-
```sh
|
|
2694
|
-
node ./dist/bin/scaffold.js mirror --apply --yes
|
|
2695
|
-
```
|
|
2696
|
-
|
|
2697
|
-
### Fleet discovery, prune scanning, and the local catalog
|
|
2698
|
-
|
|
2699
|
-
```ts
|
|
2700
|
-
import {
|
|
2701
|
-
catalogPackages,
|
|
2702
|
-
consumeCatalogAllowance,
|
|
2703
|
-
deriveBlueprint,
|
|
2704
|
-
discoverPackages,
|
|
2705
|
-
guideToDescription,
|
|
2706
|
-
isRealDirectory,
|
|
2707
|
-
isRealFile,
|
|
2708
|
-
listDirectories,
|
|
2709
|
-
listFiles,
|
|
2710
|
-
pruneTargets,
|
|
2711
|
-
selectOrkestrelEntries,
|
|
2712
|
-
vendoredPruneSet,
|
|
2713
|
-
} from '@orkestrel/scaffold/server'
|
|
2714
|
-
|
|
2715
|
-
const catalogAllowance = new Float64Array([2])
|
|
2716
|
-
consumeCatalogAllowance(catalogAllowance, './packages') // one aggregate slot remains
|
|
2717
|
-
discoverPackages('./packages') // every scoped workspace directly under the root
|
|
2718
|
-
deriveBlueprint('./packages/router') // the faithful inverse an audit diffs against
|
|
2719
|
-
selectOrkestrelEntries({ '@orkestrel/contract': '^0.0.7', vite: '^8.1.5' })
|
|
2720
|
-
|
|
2721
|
-
isRealDirectory('./packages/router')
|
|
2722
|
-
isRealFile('./packages/router/package.json')
|
|
2723
|
-
listFiles('./packages/router/.claude/agents')
|
|
2724
|
-
listDirectories('./packages/router/.claude')
|
|
2725
|
-
|
|
2726
|
-
vendoredPruneSet('./dist/host', '.claude/agents')
|
|
2727
|
-
pruneTargets('./packages/router', './dist/host') // never deletes; reports only
|
|
2728
|
-
|
|
2729
|
-
guideToDescription('> A tiny hash router.\n>\n> More detail.') // 'A tiny hash router.'
|
|
2730
|
-
catalogPackages(['./packages'], 4_096)
|
|
2731
|
-
```
|
|
2732
|
-
|
|
2733
|
-
### The write-transaction boundary
|
|
2734
|
-
|
|
2735
|
-
```ts
|
|
2736
|
-
import {
|
|
2737
|
-
commitWriteTransaction,
|
|
2738
|
-
createWriteDirectory,
|
|
2739
|
-
digestFile,
|
|
2740
|
-
digestHex,
|
|
2741
|
-
digestText,
|
|
2742
|
-
discardWriteTransaction,
|
|
2743
|
-
readFileHex,
|
|
2744
|
-
readFileText,
|
|
2745
|
-
replaceDirectory,
|
|
2746
|
-
resolveContainedPath,
|
|
2747
|
-
resolveGuideWrites,
|
|
2748
|
-
resolvePhysicalPath,
|
|
2749
|
-
resolveRealPath,
|
|
2750
|
-
restoreFiles,
|
|
2751
|
-
validateWriteAnchor,
|
|
2752
|
-
validateWriteDirectories,
|
|
2753
|
-
validateWriteTarget,
|
|
2754
|
-
WriteTransaction,
|
|
2755
|
-
} from '@orkestrel/scaffold/server'
|
|
2756
|
-
|
|
2757
|
-
resolveRealPath('./packages/router/src')
|
|
2758
|
-
resolveContainedPath('./packages/router', 'src/core/index.ts', 'TARGET', 'target')
|
|
2759
|
-
const full = resolvePhysicalPath('./packages/router', 'package.json', 'TARGET', 'target')
|
|
2760
|
-
|
|
2761
|
-
digestText(readFileText('./packages/router', 'package.json', 'TARGET', 'target')) ===
|
|
2762
|
-
digestFile(full)
|
|
2763
|
-
digestHex(readFileHex('./packages/router', 'package.json', 'TARGET', 'target'))
|
|
2764
|
-
|
|
2765
|
-
const transaction = WriteTransaction.create('./packages/router', ['package.json'])
|
|
2766
|
-
validateWriteAnchor(transaction.anchor, 'anchor')
|
|
2767
|
-
validateWriteDirectories(transaction)
|
|
2768
|
-
validateWriteTarget(transaction, undefined)
|
|
2769
|
-
createWriteDirectory(transaction.stage, 'staging')
|
|
2770
|
-
|
|
2771
|
-
try {
|
|
2772
|
-
commitWriteTransaction(transaction, ['package.json'])
|
|
2773
|
-
} catch {
|
|
2774
|
-
restoreFiles(transaction, ['package.json'])
|
|
2775
|
-
discardWriteTransaction(transaction)
|
|
2776
|
-
}
|
|
2777
|
-
|
|
2778
|
-
replaceDirectory('./staged', './target', './backup')
|
|
2779
|
-
resolveGuideWrites([], './packages/router') // preflighted destinations, before any write
|
|
2780
|
-
```
|
|
2781
|
-
|
|
2782
|
-
### Server boundary parsing and guards
|
|
2783
|
-
|
|
2784
|
-
```ts
|
|
2785
|
-
import { hasOnlyDataProperties, isDenseDataArray, isEmitterErrorHandler } from '@orkestrel/scaffold'
|
|
2786
|
-
import {
|
|
2787
|
-
isCatalogAllowance,
|
|
2788
|
-
isCatalogDescription,
|
|
2789
|
-
isDependencyData,
|
|
2790
|
-
isFilesystemPath,
|
|
2791
|
-
isHostManifest,
|
|
2792
|
-
isManifestEntry,
|
|
2793
|
-
isMaterializerEventHooks,
|
|
2794
|
-
isMissingPathError,
|
|
2795
|
-
isPortablePath,
|
|
2796
|
-
isReservedTargetPath,
|
|
2797
|
-
isSensitiveHostPath,
|
|
2798
|
-
isSyncEventHooks,
|
|
2799
|
-
isTerminalText,
|
|
2800
|
-
isWritePrecondition,
|
|
2801
|
-
materializerOptionsContract,
|
|
2802
|
-
materializerOptionsShape,
|
|
2803
|
-
parseFilesystemPaths,
|
|
2804
|
-
parseMaterializerOptions,
|
|
2805
|
-
parsePortablePaths,
|
|
2806
|
-
parseSyncBase,
|
|
2807
|
-
parseSyncBranch,
|
|
2808
|
-
parseSyncCurrent,
|
|
2809
|
-
parseSyncDependencies,
|
|
2810
|
-
parseSyncNames,
|
|
2811
|
-
parseSyncOptions,
|
|
2812
|
-
parseWritePreconditions,
|
|
2813
|
-
syncGuideOptionsShape,
|
|
2814
|
-
syncOptionsContract,
|
|
2815
|
-
syncOptionsShape,
|
|
2816
|
-
syncRegistryOptionsShape,
|
|
2817
|
-
} from '@orkestrel/scaffold/server'
|
|
2818
|
-
|
|
2819
|
-
declare const caught: unknown
|
|
2820
|
-
|
|
2821
|
-
syncGuideOptionsShape()
|
|
2822
|
-
syncRegistryOptionsShape()
|
|
2823
|
-
syncOptionsShape()
|
|
2824
|
-
materializerOptionsShape()
|
|
2825
|
-
syncOptionsContract.parse({ concurrency: 4 })
|
|
2826
|
-
materializerOptionsContract.parse({ host: './dist/host' })
|
|
2827
|
-
|
|
2828
|
-
parseSyncOptions({ guides: { branch: 'main' }, registry: { timeout: 5_000 } })
|
|
2829
|
-
parseMaterializerOptions({ host: './dist/host' })
|
|
2830
|
-
parseSyncBase('registry.npmjs.org') // 'https://registry.npmjs.org'
|
|
2831
|
-
parseSyncBranch('main')
|
|
2832
|
-
parseSyncCurrent({ '@orkestrel/contract': '# contract\n' }, ['@orkestrel/contract'], 16_777_216)
|
|
2833
|
-
parseSyncNames(['@orkestrel/contract', 'zod'])
|
|
2834
|
-
parseSyncDependencies([{ name: '@orkestrel/contract', range: '^0.0.7' }], false)
|
|
2835
|
-
parsePortablePaths(['src/core/index.ts'], 1_000)
|
|
2836
|
-
parseFilesystemPaths(['./packages'], 1_000)
|
|
2837
|
-
parseWritePreconditions([{ path: 'package.json', shape: 'absent' }], 1_000)
|
|
2838
|
-
|
|
2839
|
-
isPortablePath('src/core/index.ts') // true
|
|
2840
|
-
isFilesystemPath('./packages/router') // true
|
|
2841
|
-
isTerminalText('router') // true
|
|
2842
|
-
isDependencyData({ name: '@orkestrel/contract', range: '^0.0.7' }) // true
|
|
2843
|
-
isSensitiveHostPath('.env.local') // true
|
|
2844
|
-
isReservedTargetPath('.git/config') // true
|
|
2845
|
-
isCatalogAllowance(new Float64Array([1])) // true
|
|
2846
|
-
isCatalogDescription('A tiny hash router.') // true
|
|
2847
|
-
hasOnlyDataProperties({ a: 1 }) // true
|
|
2848
|
-
isDenseDataArray(['a'], 10, isPortablePath) // true
|
|
2849
|
-
isWritePrecondition({ path: 'package.json', shape: 'absent' }) // true
|
|
2850
|
-
isManifestEntry({ storage: 'AGENTS.md', destination: 'AGENTS.md', executable: false }) // true
|
|
2851
|
-
isHostManifest({
|
|
2852
|
-
entries: [],
|
|
2853
|
-
roots: [],
|
|
2854
|
-
digest: 'f98e1531d9fd8fab7e301d1cc944249913d93f48c918a11a753048b877211679',
|
|
2855
|
-
}) // true
|
|
2856
|
-
isSyncEventHooks({ done: () => undefined }) // true
|
|
2857
|
-
isMaterializerEventHooks({ done: () => undefined }) // true
|
|
2858
|
-
isEmitterErrorHandler(() => undefined) // true
|
|
2859
|
-
isMissingPathError(caught) // true only for an ENOENT error
|
|
2860
|
-
```
|
|
2861
|
-
|
|
2862
|
-
## Tests
|
|
2863
|
-
|
|
2864
|
-
- [`tests/src/core/helpers.test.ts`](../../tests/src/core/helpers.test.ts) — the pure leaves: table
|
|
2865
|
-
alignment, byte encoding, snapshots, host selection, conflicts, projections, hashing, and
|
|
2866
|
-
format-stable JSON.
|
|
2867
|
-
- [`tests/src/core/builders.test.ts`](../../tests/src/core/builders.test.ts) — the blueprint,
|
|
2868
|
-
dependency, override, and member builders, including optional-field omission.
|
|
2869
|
-
- [`tests/src/core/validators.test.ts`](../../tests/src/core/validators.test.ts) — every guard and
|
|
2870
|
-
refinement against valid, off-contract, hostile, and boundary input.
|
|
2871
|
-
- [`tests/src/core/shapers.test.ts`](../../tests/src/core/shapers.test.ts) — per-shape guard
|
|
2872
|
-
exactness, schema essentials, seeded generation, and parse round-trips.
|
|
2873
|
-
- [`tests/src/core/compilers.test.ts`](../../tests/src/core/compilers.test.ts) — every drafted
|
|
2874
|
-
group, the manifest and exports combination rules, the one-owner guide law for a workspace that
|
|
2875
|
-
names a line guide, and the emitted configuration text.
|
|
2876
|
-
- [`tests/src/core/Compiler.test.ts`](../../tests/src/core/Compiler.test.ts) — the three-stage
|
|
2877
|
-
pipeline, the fail-closed gate, the emission sequences, and post-destroy behavior.
|
|
2878
|
-
- [`tests/src/core/PlanManager.test.ts`](../../tests/src/core/PlanManager.test.ts) — content-hash
|
|
2879
|
-
ids, the batch-overload semantics, and all-or-nothing list removal.
|
|
2880
|
-
- [`tests/src/core/policy.test.ts`](../../tests/src/core/policy.test.ts) — the repository coding-law
|
|
2881
|
-
policy module against this workspace and against deliberately hostile fixtures.
|
|
2882
|
-
- [`tests/config/vite.test.ts`](../../tests/config/vite.test.ts) — the executable root Vite
|
|
2883
|
-
invariants for workspace, environment, and output containment.
|
|
2884
|
-
- [`tests/src/server/helpers.test.ts`](../../tests/src/server/helpers.test.ts) — containment,
|
|
2885
|
-
digests, host staging, hydration, derivation, prune scanning, and the local catalog.
|
|
2886
|
-
- [`tests/src/server/validators.test.ts`](../../tests/src/server/validators.test.ts) — the portable
|
|
2887
|
-
path law, data-only reflection, and the exact-shape record guards.
|
|
2888
|
-
- [`tests/src/server/Materializer.test.ts`](../../tests/src/server/Materializer.test.ts) —
|
|
2889
|
-
green-field writes, scoped repair, prune quarantine and rollback, and every fail-closed preflight.
|
|
2890
|
-
- [`tests/src/server/Sync.test.ts`](../../tests/src/server/Sync.test.ts) — freshness verdicts,
|
|
2891
|
-
bounded concurrency, redirect and oversize handling, strict mode, pull, write, and catalog against
|
|
2892
|
-
protocol-faithful fixture servers.
|
|
2893
|
-
- [`tests/src/server/integration.test.ts`](../../tests/src/server/integration.test.ts) — the whole
|
|
2894
|
-
compile, materialize, audit, repair round trip against real directories.
|
|
2895
|
-
- [`tests/src/bin/helpers.test.ts`](../../tests/src/bin/helpers.test.ts) — the executable's rendered
|
|
2896
|
-
verdicts, tables, notes, and suggestion machinery.
|
|
2897
|
-
- [`tests/src/bin/parsers.test.ts`](../../tests/src/bin/parsers.test.ts) — argument parsing, token
|
|
2898
|
-
splitting, and pull-selection resolution against a target's declared dependencies.
|
|
2899
|
-
- [`tests/src/bin/validators.test.ts`](../../tests/src/bin/validators.test.ts) — the verb
|
|
2900
|
-
vocabulary.
|
|
2901
|
-
- [`tests/src/bin/errors.test.ts`](../../tests/src/bin/errors.test.ts) — the executable's exit
|
|
2902
|
-
signalling.
|
|
2903
|
-
- [`tests/src/bin/scaffold.test.ts`](../../tests/src/bin/scaffold.test.ts) — each verb's dry-run,
|
|
2904
|
-
confirm, apply, and JSON paths.
|
|
2905
|
-
- [`tests/src/bin/e2e.test.ts`](../../tests/src/bin/e2e.test.ts) — the built executable driven end
|
|
2906
|
-
to end over real directories.
|
|
2907
|
-
- [`tests/guides/src/parity.test.ts`](../../tests/guides/src/parity.test.ts) — this guide against
|
|
2908
|
-
the two barrels: every export documented, every documented symbol real, every interface method
|
|
2909
|
-
matched, every documented function exampled, and every link resolvable.
|
|
2910
|
-
|
|
2911
|
-
## See also
|
|
2912
|
-
|
|
2913
|
-
- [`AGENTS.md`](../../AGENTS.md) — the coding contract every generated workspace inherits.
|
|
2914
|
-
- [`README.md`](../README.md) — the guides index.
|
|
2915
|
-
- [`contract.md`](contract.md) — the shape, guard, parser, and outcome primitives the blueprint and
|
|
2916
|
-
plan contracts compile through.
|
|
2917
|
-
- [`emitter.md`](emitter.md) — the observation channel every entity here composes.
|
|
2918
|
-
- [`markdown.md`](markdown.md) — the AST and renderer behind the table and blockquote work.
|
|
2919
|
-
- [`template.md`](template.md) — the pure fill engine behind every template-origin artifact.
|
|
2920
|
-
- [`terminal.md`](terminal.md) and [`console.md`](console.md) — the prompt and reporter toolkits
|
|
2921
|
-
consumed only at the executable boundary.
|
|
2922
|
-
- [`guide.md`](guide.md) — the guides-parity toolkit this guide is checked with.
|