@orkestrel/scaffold 0.0.5 → 0.0.7

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.
@@ -29,7 +29,7 @@ that projection, expressed as data. Rendered defaults ship as versioned package
29
29
  here rather than a hand edit in every workspace.
30
30
 
31
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 template override —
32
+ keywords, which src and app environments, which dependencies, any artifact override —
33
33
  belong to the caller. What this module supplies is the closed vocabularies, the variant matrix as
34
34
  data, exact-record validation, a fail-closed gate, a deterministic pin, and lossless projections.
35
35
 
@@ -127,49 +127,50 @@ plan.artifacts.some((artifact) => artifact.path === 'app/server/main.ts') // tru
127
127
 
128
128
  From [`types.ts`](../../src/core/types.ts).
129
129
 
130
- | Name | Kind |
131
- | ---------------------- | --------- |
132
- | `Environment` | type |
133
- | `BuildFormat` | type |
134
- | `SrcDefinition` | interface |
135
- | `AppDefinition` | interface |
136
- | `ViteMachinery` | interface |
137
- | `Origin` | type |
138
- | `Group` | type |
139
- | `Category` | type |
140
- | `CatalogEntry` | interface |
141
- | `Drift` | type |
142
- | `Freshness` | type |
143
- | `CompileStage` | type |
144
- | `ScaffoldErrorCode` | type |
145
- | `Dependency` | interface |
146
- | `Override` | interface |
147
- | `Blueprint` | interface |
148
- | `Member` | interface |
149
- | `ArtifactBase` | interface |
150
- | `HostArtifact` | interface |
151
- | `ContentArtifact` | interface |
152
- | `Artifact` | type |
153
- | `Snapshot` | type |
154
- | `Plan` | interface |
155
- | `Finding` | interface |
156
- | `Audit` | interface |
157
- | `Question` | interface |
158
- | `Validation` | interface |
159
- | `GuideSync` | interface |
160
- | `VersionSync` | interface |
161
- | `SyncReport` | interface |
162
- | `PlanSummary` | interface |
163
- | `CompileRecord` | interface |
164
- | `CompileFailure` | interface |
165
- | `Scaffolding` | interface |
166
- | `PlanRecord` | interface |
167
- | `CompilerEventMap` | type |
168
- | `CompilerOptions` | interface |
169
- | `CompilerInterface` | interface |
170
- | `PlanManagerEventMap` | type |
171
- | `PlanManagerOptions` | interface |
172
- | `PlanManagerInterface` | interface |
130
+ | Name | Kind |
131
+ | ------------------------- | --------- |
132
+ | `Environment` | type |
133
+ | `BuildFormat` | type |
134
+ | `SrcDefinition` | interface |
135
+ | `AppDefinition` | interface |
136
+ | `ViteMachinery` | interface |
137
+ | `ViteProjectRegistration` | interface |
138
+ | `Origin` | type |
139
+ | `Group` | type |
140
+ | `Category` | type |
141
+ | `CatalogEntry` | interface |
142
+ | `Drift` | type |
143
+ | `Freshness` | type |
144
+ | `CompileStage` | type |
145
+ | `ScaffoldErrorCode` | type |
146
+ | `Dependency` | interface |
147
+ | `Override` | interface |
148
+ | `Blueprint` | interface |
149
+ | `Member` | interface |
150
+ | `ArtifactBase` | interface |
151
+ | `HostArtifact` | interface |
152
+ | `ContentArtifact` | interface |
153
+ | `Artifact` | type |
154
+ | `Snapshot` | type |
155
+ | `Plan` | interface |
156
+ | `Finding` | interface |
157
+ | `Audit` | interface |
158
+ | `Question` | interface |
159
+ | `Validation` | interface |
160
+ | `GuideSync` | interface |
161
+ | `VersionSync` | interface |
162
+ | `SyncReport` | interface |
163
+ | `PlanSummary` | interface |
164
+ | `CompileRecord` | interface |
165
+ | `CompileFailure` | interface |
166
+ | `Scaffolding` | interface |
167
+ | `PlanRecord` | interface |
168
+ | `CompilerEventMap` | type |
169
+ | `CompilerOptions` | interface |
170
+ | `CompilerInterface` | interface |
171
+ | `PlanManagerEventMap` | type |
172
+ | `PlanManagerOptions` | interface |
173
+ | `PlanManagerInterface` | interface |
173
174
 
174
175
  The closed vocabularies are small and total. `Environment` is `'core' | 'browser' | 'server'`.
175
176
  `BuildFormat` is `'es' | 'cjs'`. `Origin` is `'host' | 'template' | 'computed'`. `Group` is
@@ -190,6 +191,10 @@ single-file-component, HTML, and development-server machinery an application bro
190
191
  needs, and `output` for build-output containment. It never selects a boundary guarantee — those ship
191
192
  in every shape, as the compilers section sets out.
192
193
 
194
+ `ViteProjectRegistration` carries one generated project factory identifier and its optional browser
195
+ label. Root configuration renderers preserve that browser ownership as data through registration
196
+ instead of inferring it from a project identifier.
197
+
193
198
  `Blueprint` is the closed input spec:
194
199
 
195
200
  ```ts
@@ -370,7 +375,9 @@ value lists behind their literal unions. `SRC_MATRIX` is the `src` environment m
370
375
  data — each environment's `configs/src` files, test-project label, `exports` subpath, and build
371
376
  formats. `APP_MATRIX` is its application sibling, adding the runtime entry where an environment produces
372
377
  one (`app/browser/index.html`, `app/server/main.ts`). `HOST_PATHS` is the ordered list of
373
- byte-copied host artifacts.
378
+ byte-copied host artifacts, and it is the staging manifest rather than the per-plan carried set:
379
+ `stageHost` vendors every path on it, while each plan carries the subset `selectHostPaths` selects
380
+ for that one workspace.
374
381
 
375
382
  The bounds are public because they are part of the contract, not implementation trivia.
376
383
  `MAX_ARTIFACT_BYTES` caps one artifact at 5 MiB and `MAX_TOTAL_ARTIFACT_BYTES` caps one blueprint,
@@ -544,7 +551,8 @@ entity allocation.
544
551
  then checks every override against the exact artifact set the plan would write. An override whose
545
552
  `path` matches no planned artifact, targets a `host`-origin artifact, or targets the
546
553
  blueprint-owned `package.json` publication boundary is a blocking question rather than a silent
547
- no-op.
554
+ no-op. An override that clears all three lands a `warnings` entry naming the path it replaces — the
555
+ declaration is accepted, and it is never accepted silently.
548
556
 
549
557
  ### Validators — server
550
558
 
@@ -736,11 +744,13 @@ From [`helpers.ts`](../../src/core/helpers.ts).
736
744
  | `contentByteLength` | function |
737
745
  | `contentToHex` | function |
738
746
  | `snapshotOf` | function |
747
+ | `selectHostPaths` | function |
739
748
  | `findPathConflict` | function |
740
749
  | `findFileConflict` | function |
741
750
  | `validateDependencyArray` | function |
742
751
  | `validateBlueprint` | function |
743
752
  | `manifestToDependencies` | function |
753
+ | `manifestToName` | function |
744
754
  | `rangeToFreshness` | function |
745
755
  | `computeHash` | function |
746
756
  | `stableStringify` | function |
@@ -786,14 +796,20 @@ report projections count with.
786
796
  `snapshotOf`, `contentToHex`, `contentToBytes`, `contentByteLength`, `contentCodePoint`, and
787
797
  `bytesToHex` are the host-independent byte leaves that make exact comparison possible without a
788
798
  host encoder or buffer; an unpaired surrogate encodes as `U+FFFD` rather than throwing.
789
- `findPathConflict` finds the first exact or case-insensitive collision in a path list, and
790
- `findFileConflict` additionally rejects a file that would sit inside another planned path.
799
+ `selectHostPaths` is the one-owner filter plan assembly applies before it carries anything: it
800
+ returns the host paths in input order minus `guides/src/<name>.md`, so a workspace never plans a
801
+ vendored mirror of the guide it writes itself. `findPathConflict` finds the first exact or
802
+ case-insensitive collision in a path list, and `findFileConflict` additionally rejects a file that
803
+ would sit inside another planned path — the loud backstop behind that selection.
791
804
 
792
805
  `validateBlueprint` and `validateDependencyArray` are the semantic pass. The array validator is
793
806
  pure — it returns its questions and the set of names it saw, so the caller can apply the
794
807
  cross-array overlap rules on top. `manifestToDependencies` reads a manifest's `dependencies`,
795
808
  `devDependencies`, and `peerDependencies` in that order, keeps only own data sections and scoped
796
- names, deduplicates, and never throws. `rangeToFreshness` applies the exact-pin comparison; the `missing` and `failed`
809
+ names, deduplicates, and never throws. `manifestToName` is its self-reading sibling over the same
810
+ text: the manifest's own string `name`, or `undefined` when the text is oversized, malformed,
811
+ rootless, or nameless — the projection that lets a target recognize itself in its own declared
812
+ dependencies. `rangeToFreshness` applies the exact-pin comparison; the `missing` and `failed`
797
813
  verdicts come from the fetch layer, never from this pure comparison.
798
814
 
799
815
  `computeHash` is a deterministic FNV-1a digest and `stableStringify` a key-order-independent
@@ -904,7 +920,9 @@ dependency-less stand-in. Environments come from `src/<environment>/` and `app/<
904
920
  scoped entries, with an optional peer recovered from `peerDependenciesMeta`; and `extras` is every
905
921
  development dependency minus the generated baseline and minus anything already declared as a
906
922
  dependency or peer, so a hand-added development dependency round-trips and stays audit-clean.
907
- `overrides` is always empty, because derivation cannot know a caller's override intent.
923
+ Derivation yields no `overrides`: they are caller-time inputs, not repository state. A computed
924
+ artifact that must differ reveals a gap in the canon; the blueprint grows an axis for that
925
+ distinction rather than the repository forking the file.
908
926
 
909
927
  `storagePath`, `stageHost`, `readHostManifest`, `locateHostSource`, `remapArtifactPath`, and
910
928
  `hydratePlan` are the vendored-host path. `storagePath` maps a repo-relative path to its un-dotted
@@ -926,7 +944,9 @@ anything.
926
944
  `consumeCatalogAllowance` decrements the single shared entry allowance and throws `TARGET` before an
927
945
  over-budget traversal continues. `discoverPackages` requires a real, unlinked root and lists its
928
946
  immediate child directories whose bounded manifest names a scoped package, skipping anything else
929
- silently. `catalogPackages` applies one allowance across every root and directory rather than
947
+ silently. A control-bearing child directory fails closed before its manifest is read and the
948
+ untrusted name is never reflected in the diagnostic. `catalogPackages` applies one allowance across
949
+ every root and directory rather than
930
950
  resetting a per-root budget, then draws each description from the first paragraph of the first
931
951
  blockquote of that package's own bounded guide via `guideToDescription`; a missing guide, an
932
952
  unreadable or oversized one, or one with no blockquote yields an empty description rather than an
@@ -954,6 +974,7 @@ From [`compilers.ts`](../../src/core/compilers.ts).
954
974
  | `packageManifest` | function |
955
975
  | `rootTsconfig` | function |
956
976
  | `viteMachinery` | function |
977
+ | `renderViteTest` | function |
957
978
  | `viteHeader` | function |
958
979
  | `policyViteProject` | function |
959
980
  | `singleSrcViteConfig` | function |
@@ -994,7 +1015,13 @@ optional metadata, and engines.
994
1015
 
995
1016
  `rootTsconfig` emits the root compiler options and one path alias per declared environment;
996
1017
  `coreTsconfig`, `srcTsconfig`, and `appTsconfig` emit the scoped configurations that remove the
997
- wrong host's globals from each environment. `viteHeader` renders the shared header the alias block
1018
+ wrong host's globals from each environment. A core scope is the interesting one: `lib` is
1019
+ `["ESNext", "WebWorker"]` and `types` stays `[]`, which declares the WHATWG surface that is
1020
+ identical across Node, browsers, and workers — `fetch` and its request/response/header types,
1021
+ streams, `URL`, `AbortController`, the text encoders, `crypto`, timers, `console`, `DOMException`,
1022
+ `structuredClone` — while leaving `document`, `window`, and every `node:*` type unresolvable. That
1023
+ is one declaration set for a host-independent module, not a host. `viteHeader` renders the shared
1024
+ header — the alias block
998
1025
  derived from the tsconfig paths, plus the environment-boundary plugin — and `viteMachinery` is the
999
1026
  one place the header's axes are derived, read by `rootViteConfig`, `singleSrcViteConfig`,
1000
1027
  `applicationViteConfig`, and `configArtifacts` alike so no caller can invent a fourth answer.
@@ -1006,7 +1033,8 @@ emits `environmentBoundary`, its `resolveId` / `load` / `buildEnd` walks, the mo
1006
1033
  plus its `environmentPathError` / `environmentSourceError` clauses). Those enforce owner-independent
1007
1034
  laws: core stays host-independent whatever else the workspace declares, a server module never
1008
1035
  imports a stylesheet, and a `@vite-ignore` dynamic import — which `resolveId` never sees and the
1009
- module graph never records — has no other enforcement point. Only host-specific pipelines vary,
1036
+ module graph never records — has no other enforcement point in workspace-owned source. Dependency
1037
+ and toolchain modules are outside that ownership boundary. Only host-specific pipelines vary,
1010
1038
  along the three `ViteMachinery` axes:
1011
1039
 
1012
1040
  | Machinery | Emitted when |
@@ -1019,6 +1047,10 @@ along the three `ViteMachinery` axes:
1019
1047
  An application of `app/core` alone is the sole shape that builds nothing, so it is the sole shape
1020
1048
  without output containment — and it still carries every boundary guarantee above.
1021
1049
 
1050
+ `renderViteTest` is the single root-project renderer. It consumes ordered
1051
+ `ViteProjectRegistration` data and emits either the plain project list or the browser gate, keeping
1052
+ source and application root configurations byte-consistent without reconstructing browser ownership.
1053
+
1022
1054
  `coreViteConfig`, `srcViteConfig`, and `appViteConfig` emit the thin per-target wrappers;
1023
1055
  `rootViteConfig`, `singleSrcViteConfig`, and `applicationViteConfig` emit the root configuration for
1024
1056
  a library-only, single non-core `src` environment, and application-bearing workspace respectively;
@@ -1125,8 +1157,9 @@ The public methods of each behavioral interface, one table per type.
1125
1157
  `Scaffolding`; the optional group selection scopes the plan to those artifact groups.
1126
1158
  `audit(blueprint, current, groups?)` compiles and then diffs the resulting plan against the
1127
1159
  caller-supplied current content; a gated blueprint returns `complete: false` with the gate's
1128
- blocking questions and zero findings. `destroy()` is idempotent teardown. The interface also
1129
- exposes the readonly `emitter`.
1160
+ blocking questions and zero findings, and a complete one carries the gate's advisories on that same
1161
+ `questions` field. `destroy()` is idempotent teardown. The interface also exposes the readonly
1162
+ `emitter`.
1130
1163
 
1131
1164
  #### `PlanManagerInterface`
1132
1165
 
@@ -1190,12 +1223,14 @@ in-flight request. The interface also exposes the readonly `emitter`.
1190
1223
  `compile` runs three stages in fixed order and records each as a `CompileRecord` carrying its input,
1191
1224
  its output, whether it failed, and any error text.
1192
1225
 
1193
- 1. **draft** — `blueprintToPlan` selects the covered groups, drafts each group's artifacts, appends
1194
- the host set, applies overrides, and pins the draft. A throw here records a `draft` failure coded
1226
+ 1. **draft** — `blueprintToPlan` selects the covered groups, drafts each group's artifacts, carries
1227
+ the selected host set every vendored host path except the workspace's own guide applies
1228
+ overrides, and pins the draft. A throw here records a `draft` failure coded
1195
1229
  `INVALID`, emits `error`, marks the remaining two stages skipped, and returns incomplete.
1196
1230
  2. **gate** — `validatePlan` runs the semantic pass over the blueprint and checks every override
1197
- against the drafted artifact set. Blocking questions fail the stage; a dependency outside the
1198
- vendored guide set contributes a non-blocking advisory question instead.
1231
+ against the drafted artifact set. Blocking questions fail the stage; an accepted override and a
1232
+ dependency outside the vendored guide set each contribute a non-blocking advisory question
1233
+ instead.
1199
1234
  3. **pin** — a host-origin pointer artifact is appended for each non-vendored dependency, and
1200
1235
  `pinPlan` fills `trace` and `hash` from the plan's own content.
1201
1236
 
@@ -1227,7 +1262,8 @@ how it is audited, and whether it may ever be overwritten.
1227
1262
  - **`host`** — byte-copied from the vendored data root. These are the shared files a whole fleet
1228
1263
  keeps identical: the root instruction documents and licence, the agent, rule, and skill
1229
1264
  directories, the session scripts, the repository coding-law policy module, the byte-identical root
1230
- dotfiles, and the two guide mirrors every workspace carries. `HOST_PATHS` is the exact list.
1265
+ dotfiles, and the two line guide mirrors a workspace carries for contracts other than its own.
1266
+ `HOST_PATHS` is the exact vendored list; what a given plan carries is `selectHostPaths` of it.
1231
1267
  - **`template`** — filled from a frozen template definition by a pure fill engine. These are
1232
1268
  starter files: source stubs, test stubs, the starter guide, the README.
1233
1269
  - **`computed`** — derived by this package's own combination logic. These are the structural files:
@@ -1256,18 +1292,31 @@ anything, so a mature workspace's hand-written source, tests, guides, and manife
1256
1292
  overwritten with a stub. A consequence worth stating plainly: the generated
1257
1293
  `.github/workflows/ci.yml` is a **computed** artifact, so **user-owned CI is never repaired**. Once
1258
1294
  a workspace has its own workflow, that copy stands, and any change to it is an ordinary edit in that
1259
- workspace.
1295
+ workspace. Audit still compares it because computed artifacts are content-aware canon. A legitimate
1296
+ difference that the blueprint cannot express is a canon gap: add the missing blueprint axis rather
1297
+ than forking the computed file in one repository.
1260
1298
 
1261
1299
  Overrides respect the same boundary from the other direction. `applyOverrides` never replaces a
1262
1300
  host-origin artifact and never replaces `package.json`; the gate turns either attempt — and an
1263
1301
  override matching no planned artifact at all — into a blocking question rather than a silent no-op.
1264
-
1265
- Guide mirrors are the one place ownership is conditional. A dependency this package vendors a
1266
- byte-identical mirror for gets a real host-origin copy of `guides/src/<short>.md`. Any other
1267
- dependency gets a host-origin _pointer_ artifact plus a non-blocking question, never a fabricated
1268
- mirror; on materialization that pointer degrades to a short stub, and `scaffold pull` fetches the
1269
- real thing. That degrade is scoped exactly to guide pointers: any other missing manifest entry
1270
- means a corrupt or truncated vendored manifest, and fails closed.
1302
+ What survives those three refusals is applied and announced: the gate carries a non-blocking
1303
+ advisory naming each replaced path, and that advisory rides the `Scaffolding` and the `Audit` all
1304
+ the way through the library result.
1305
+
1306
+ Guide mirrors are the one place ownership is conditional, and the law is one owner per guide path.
1307
+ **A workspace mirrors every line guide except its own.** When the name matches the guide package
1308
+ on `guides/src/guide.md`, this package on `guides/src/scaffold.md` the workspace itself is the
1309
+ owner, keeping that path as its **template**-origin starter guide, and `selectHostPaths` drops the
1310
+ vendored mirror so the path is contributed exactly once. For every other contract the mirror is the
1311
+ owner: a dependency this package vendors a byte-identical mirror for gets a real host-origin copy of
1312
+ `guides/src/<short>.md`, contributed once whether it arrives through the host set or through the
1313
+ dependency, so a package depending on `@orkestrel/guide` plans one `guides/src/guide.md` rather than
1314
+ two. Any other dependency gets a host-origin _pointer_ artifact plus a non-blocking question, never
1315
+ a fabricated mirror; on materialization that pointer degrades to a short stub, and `scaffold pull`
1316
+ fetches the real thing. That degrade is scoped exactly to guide pointers: any other missing manifest
1317
+ entry means a corrupt or truncated vendored manifest, and fails closed. Selection is the law and
1318
+ `findFileConflict` is its backstop: two artifacts at one path refuse the plan rather than racing to
1319
+ be the last writer.
1271
1320
 
1272
1321
  ## Audit, repair, and prune
1273
1322
 
@@ -1317,10 +1366,15 @@ because there is nothing to explain.
1317
1366
  `pull` is the target-aware composition. It reads the target's declared scoped dependencies from its
1318
1367
  manifest, rejects any explicit selection the target does not declare, builds the reference map from
1319
1368
  the target's own `guides/src/<short>.md` mirrors, fetches guides and versions under one shared
1320
- allowance, and assembles a report whose `clean` flag requires both no drift and no failures. `write`
1321
- then commits only the `behind` guides never `current`, `missing`, or `failed`, none of which carry
1322
- trustworthy content under the same containment and precondition law `Materializer` enforces,
1323
- including a baseline digest check against what is actually on disk.
1369
+ allowance, and assembles a report whose `clean` flag requires both no drift and no failures. A
1370
+ target that declares itself is the one asymmetry, and it follows the same single-owner law: the
1371
+ guide pass drops the self dependency, so `pull` never fetches or writes a workspace's own contract
1372
+ guide over the copy that workspace owns, while the version pass keeps it and still reports its
1373
+ freshness. A `--live` audit reads upstream through the same two passes and applies the same
1374
+ self-exclusion, so the freshness a workspace reports about itself never depends on which verb asked.
1375
+ `write` then commits only the `behind` guides — never `current`, `missing`, or `failed`,
1376
+ none of which carry trustworthy content — under the same containment and precondition law
1377
+ `Materializer` enforces, including a baseline digest check against what is actually on disk.
1324
1378
 
1325
1379
  `catalog` builds the fleet package catalog from three reads per entry. The registry's exact
1326
1380
  organization package list is authoritative and unconditionally required. Each package's own registry
@@ -1353,7 +1407,7 @@ a fixed, interleaved order so aggregates sit immediately before their per-enviro
1353
1407
  Vue typechecker, every other scope uses plain `tsc`
1354
1408
  - `test`, then `test:src` and its per-environment scopes, `test:app` and its per-environment scopes,
1355
1409
  `test:policy`, and `test:guides`; an engine also receives the deliberately non-default
1356
- `test:integration` live installed-consumer gate
1410
+ `test:integration` live installed-consumer gate and `test:equivalence` driver-reference proof
1357
1411
  - `build`, then `build:src` and its per-environment targets, `build:app` and its runtime targets, and
1358
1412
  `build:host` for an engine workspace
1359
1413
  - `dev` when a browser application is selected; `serve` and `serve:build` when a server application
@@ -1361,11 +1415,25 @@ a fixed, interleaved order so aggregates sit immediately before their per-enviro
1361
1415
  - `prepublishOnly` chaining `format:check → lint:check → check → build → test`, followed by the
1362
1416
  live generated-consumer integration gate for the scaffold engine itself
1363
1417
 
1418
+ Run `npm run test:equivalence` after changing the persistent boundary build driver. It reruns the
1419
+ integration project in dual-path mode and proves each programmatic driver verdict against the
1420
+ spawned npm-script reference; ordinary integration runs keep the faster driver-only path.
1421
+
1364
1422
  **Environment isolation.** Scoped TypeScript projects remove the wrong host's globals from each
1365
- environment: core scopes carry no DOM, no Node, and no host globals at all; browser scopes carry DOM
1366
- and no Node; server scopes carry Node and no DOM. Lint restricts declared package, alias, and
1367
- conventional relative imports in the same directions. Neither replaces the other, and neither
1368
- replaces the build.
1423
+ environment: core scopes carry the WHATWG web-interop surface and no host at all no DOM, no Node,
1424
+ no `vite/client`; browser scopes carry DOM and no Node; server scopes carry Node and no DOM. The
1425
+ worker-only globals the `WebWorker` declarations would otherwise admit `name`, `onrtctransform`,
1426
+ `close`, `postMessage`, `dispatchEvent`, `location`, `onerror`, `onlanguagechange`, `onoffline`,
1427
+ `ononline`, `onrejectionhandled`, `onunhandledrejection`, `self`, `importScripts`, `fonts`, `caches`,
1428
+ `crossOriginIsolated`, `indexedDB`, `isSecureContext`, `origin`, `scheduler`, `createImageBitmap`,
1429
+ `reportError`, `cancelAnimationFrame`, `requestAnimationFrame`, `onmessage`, `onmessageerror`,
1430
+ `addEventListener`, and `removeEventListener` — are fenced out of `src/core` and `app/core` sources
1431
+ by the policy suite, so the declarations widen what a host-independent module may call without
1432
+ widening where it may run. On every TypeScript bump, derive this list from the module-scope
1433
+ global-object `declare var` and `declare function` declarations in the installed
1434
+ `lib.webworker.d.ts`, then subtract values supplied by `lib.esnext*` or current Node globals. Lint
1435
+ restricts declared package, alias, and conventional relative imports in the same directions.
1436
+ Neither replaces the other, and neither replaces the build.
1369
1437
 
1370
1438
  **The generated build boundary.** The emitted configuration carries an environment-boundary plugin
1371
1439
  that resolves the real module graph rather than re-implementing a parser. **TypeScript and
@@ -1398,10 +1466,10 @@ application's module graph through `import.meta.env` instead.
1398
1466
  Asset URLs that force `?inline` are rejected before Vite can read them outside that auditable output
1399
1467
  graph. Dynamic imports must use a static quoted string or expression-free template string; even
1400
1468
  `/* @vite-ignore */` static values repeat the same environment and containment checks inside the
1401
- transform boundary, including inline HTML proxy modules and trusted dependency modules. Trusted
1402
- dependency modules also pass through a bounded, no-follow, identity-checked load inspection before
1403
- Vite transforms or tree-shakes their raw source, so dependency-side asset references cannot escape
1404
- the physical package root by disappearing from the later graph.
1469
+ transform boundary, including inline HTML proxy modules. The transform, load, resolution, emitted
1470
+ asset, and finished-module-graph passes apply that law only to workspace-owned `src/*` and `app/*`
1471
+ modules. Resolved ids under any `node_modules` segment, Vite/Vitest virtual ids, and tooling client
1472
+ injections remain owned by their toolchain and are exempt.
1405
1473
 
1406
1474
  Browser application scripts are modules. Vite's parsed HTML asset callback rejects a classic
1407
1475
  external `<script src>` before resolution and directs the author to `type="module"`. A module
@@ -1416,9 +1484,9 @@ path remains Vite-owned and passes through the environment resolver, which rejec
1416
1484
  ASCII control range and every non-Node URL scheme before loading or output. No second HTML parser or global
1417
1485
  reference rewrite is involved, so comments, text, non-script attributes, and entity-spelled asset
1418
1486
  filenames retain Vite's native parsing and resolution behavior.
1419
- The resolver leaves NUL-prefixed Rolldown/Vite virtual module IDs to the tool that owns that
1420
- namespace; author module and asset URLs are extracted and validated before they reach that resolver
1421
- exception.
1487
+ The resolver leaves NUL-prefixed and `virtual:` Rolldown/Vite module IDs, tooling client injections,
1488
+ and every resolved `node_modules` module to the tool that owns that namespace; author module and
1489
+ asset URLs are extracted and validated before they reach those resolver exceptions.
1422
1490
  SVG script `href` and `xlink:href` attributes are parsed too and rejected as classic script loads.
1423
1491
  Inline module scripts enter Vite's HTML proxy graph and receive the same Oxc boundary analysis as
1424
1492
  module files. Classic inline scripts cannot enter that graph, so the required security prologue places
@@ -1458,11 +1526,7 @@ What it rejects is equally deliberate:
1458
1526
  browser or server package subpath;
1459
1527
  - a browser module reaching a Node builtin or a server subpath;
1460
1528
  - a server module reaching a stylesheet, Vue, or a browser subpath;
1461
- - a workspace-relative import that resolves outside the workspace, or a dependency import that
1462
- escapes the exact physical package root established by the nearest bounded, unlinked
1463
- `package.json` whose own `name` exactly matches the resolved dependency;
1464
- - a package `#imports` mapping that resolves outside both the declaring package and another exact
1465
- physical package root;
1529
+ - a workspace-relative import that resolves outside the workspace;
1466
1530
  - an HTML reference carrying `vite-ignore` that violates the same environment or containment law
1467
1531
  as an ordinary reference, a Vite `%ENV%` HTML substitution, a classic external script, or a
1468
1532
  computed dynamic import in the module graph that would bypass graph resolution;
@@ -1485,7 +1549,8 @@ cannot express — that a centralized module exports every top-level declaration
1485
1549
  implementation files hold one class and no stray module-scope declaration, that no function is
1486
1550
  declared inside another function outside a directly-passed callback, that interface properties are
1487
1551
  readonly, that privacy is a runtime `#` field rather than a TypeScript modifier, that a barrel
1488
- re-exports only through `export *`, and that a computed dynamic import cannot smuggle a
1552
+ re-exports only through `export *`, that a core source never names a worker-only global the
1553
+ `WebWorker` declarations expose, and that a computed dynamic import cannot smuggle a
1489
1554
  cross-environment dependency past the declared import rules. Vue components are inspected for the
1490
1555
  same evasions. It is a complement to lint and typecheck, never a second type system, and it is not a
1491
1556
  general-purpose source analyzer. Generated workspaces receive the same exported policy module as a
@@ -1495,7 +1560,14 @@ host-origin file and run it as a dedicated Node-only `policy` test project over
1495
1560
  **Real browser capability.** Browser test projects are gated on the real executable: the generated
1496
1561
  configuration and the generated policy test both probe `existsSync(chromium.executablePath())`. A
1497
1562
  browser suite runs when a real Chromium is installed and is skipped honestly when it is not, rather
1498
- than being faked.
1563
+ than being faked. The gate is applied at registration, not inside the real browser project: without
1564
+ Chromium, each browser factory is replaced by a same-label Node/no-test placeholder, so generated
1565
+ `--project <label>` and `--project=<label>` filters still resolve while no browser code runs. The
1566
+ root permits an empty run only when every recognized exact project filter names one of those gated
1567
+ placeholders; an unreadable or mixed filter keeps the ordinary no-test failure semantics for its
1568
+ Node projects. One printed warning names every gated project label. A machine with a browser
1569
+ registers and runs the real browser suites unchanged; a machine without one runs the remaining
1570
+ projects and says so.
1499
1571
 
1500
1572
  **Continuous integration.** The generated workflow runs on push and pull request, on
1501
1573
  `ubuntu-latest`, with read-only contents permission, a 60-minute timeout, and a matrix that **tests
@@ -1903,6 +1975,7 @@ import {
1903
1975
  hostGroup,
1904
1976
  packageManifest,
1905
1977
  paritySpecifiers,
1978
+ selectHostPaths,
1906
1979
  sourceArtifacts,
1907
1980
  srcVariant,
1908
1981
  testArtifacts,
@@ -1912,6 +1985,7 @@ const spec = blueprint('router', { src: ['core'], app: ['core', 'server'] })
1912
1985
  const members = blueprintToMembers(spec)
1913
1986
 
1914
1987
  hostGroup('AGENTS.md') // 'docs'
1988
+ selectHostPaths(['guides/src/router.md', 'LICENSE'], spec.name) // ['LICENSE'] — never its own guide
1915
1989
  srcVariant(['core', 'server']) // 'multi'
1916
1990
  entryFields(['browser']).main // './dist/src/browser/index.js'
1917
1991
  dualCondition('./dist/src/core/index')
@@ -1949,6 +2023,7 @@ import {
1949
2023
  coreTsconfig,
1950
2024
  coreViteConfig,
1951
2025
  policyViteProject,
2026
+ renderViteTest,
1952
2027
  rootTsconfig,
1953
2028
  rootViteConfig,
1954
2029
  singleSrcViteConfig,
@@ -1965,6 +2040,7 @@ appTsconfig('browser', true)
1965
2040
 
1966
2041
  viteMachinery(['core']) // { browser: false, vue: false, output: true }
1967
2042
  viteMachinery([], ['core', 'browser']) // { browser: true, vue: true, output: true }
2043
+ renderViteTest([{ project: 'srcCore' }], false).includes('projects: [srcCore]') // true
1968
2044
  viteHeader(viteMachinery([], ['core', 'browser'])) // the shared header, with browser and Vue support
1969
2045
  coreViteConfig()
1970
2046
  srcViteConfig('browser')
@@ -1979,7 +2055,12 @@ applicationViteConfig([], ['core', 'server']).includes('appServer') // true
1979
2055
  ### Reading declared dependencies and comparing freshness
1980
2056
 
1981
2057
  ```ts
1982
- import { isBehind, manifestToDependencies, rangeToFreshness } from '@orkestrel/scaffold'
2058
+ import {
2059
+ isBehind,
2060
+ manifestToDependencies,
2061
+ manifestToName,
2062
+ rangeToFreshness,
2063
+ } from '@orkestrel/scaffold'
1983
2064
  import {
1984
2065
  guideStub,
1985
2066
  packageShortName,
@@ -1988,6 +2069,7 @@ import {
1988
2069
  } from '@orkestrel/scaffold/server'
1989
2070
 
1990
2071
  manifestToDependencies('{"dependencies":{"@orkestrel/contract":"^0.0.7"}}')
2072
+ manifestToName('{"name":"@orkestrel/router"}') // '@orkestrel/router' — the target's own name
1991
2073
  rangeToFreshness('^0.0.7', '0.0.7') // 'current'
1992
2074
  isBehind(rangeToFreshness('^0.0.7', '0.0.9')) // true
1993
2075
 
@@ -2220,7 +2302,8 @@ isMissingPathError(caught) // true only for an ENOENT error
2220
2302
  ## Tests
2221
2303
 
2222
2304
  - [`tests/src/core/helpers.test.ts`](../../tests/src/core/helpers.test.ts) — the pure leaves: table
2223
- alignment, byte encoding, snapshots, conflicts, projections, hashing, and format-stable JSON.
2305
+ alignment, byte encoding, snapshots, host selection, conflicts, projections, hashing, and
2306
+ format-stable JSON.
2224
2307
  - [`tests/src/core/builders.test.ts`](../../tests/src/core/builders.test.ts) — the blueprint,
2225
2308
  dependency, override, and member builders, including optional-field omission.
2226
2309
  - [`tests/src/core/validators.test.ts`](../../tests/src/core/validators.test.ts) — every guard and
@@ -2228,7 +2311,8 @@ isMissingPathError(caught) // true only for an ENOENT error
2228
2311
  - [`tests/src/core/shapers.test.ts`](../../tests/src/core/shapers.test.ts) — per-shape guard
2229
2312
  exactness, schema essentials, seeded generation, and parse round-trips.
2230
2313
  - [`tests/src/core/compilers.test.ts`](../../tests/src/core/compilers.test.ts) — every drafted
2231
- group, the manifest and exports combination rules, and the emitted configuration text.
2314
+ group, the manifest and exports combination rules, the one-owner guide law for a workspace that
2315
+ names a line guide, and the emitted configuration text.
2232
2316
  - [`tests/src/core/Compiler.test.ts`](../../tests/src/core/Compiler.test.ts) — the three-stage
2233
2317
  pipeline, the fail-closed gate, the emission sequences, and post-destroy behavior.
2234
2318
  - [`tests/src/core/PlanManager.test.ts`](../../tests/src/core/PlanManager.test.ts) — content-hash
@@ -32,8 +32,10 @@ valid_effort() {
32
32
 
33
33
  if codex login status >/dev/null 2>&1; then
34
34
  auth="authentication ready"
35
+ recovery=""
35
36
  else
36
37
  auth="authentication unavailable"
38
+ recovery='codex login --device-auth'
37
39
  fi
38
40
 
39
41
  if valid_model "$analyst_model" &&
@@ -46,4 +48,7 @@ else
46
48
  fi
47
49
 
48
50
  echo "codex.sh: bench detected; ${auth}; ${routes}."
51
+ if [ -n "$recovery" ]; then
52
+ echo "codex.sh: to recover, run \`${recovery}\`."
53
+ fi
49
54
  exit 0