@orkestrel/scaffold 0.0.4 → 0.0.6
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/dist/bin/scaffold.js +57 -22
- package/dist/bin/scaffold.js.map +1 -1
- package/dist/host/CLAUDE.md +18 -4
- package/dist/host/claude/agents/codex.md +16 -0
- package/dist/host/claude/rules/workspace.md +8 -8
- package/dist/host/guides/src/scaffold.md +144 -42
- package/dist/host/scripts/codex.sh +5 -0
- package/dist/host/tests/setupPolicy.ts +178 -4
- package/dist/src/core/index.cjs +285 -129
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +109 -25
- package/dist/src/core/index.d.ts +109 -25
- package/dist/src/core/index.js +283 -130
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +11 -5
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +4 -2
- package/dist/src/server/index.d.ts +4 -2
- package/dist/src/server/index.js +12 -6
- package/dist/src/server/index.js.map +1 -1
- package/package.json +5 -5
package/dist/host/CLAUDE.md
CHANGED
|
@@ -276,7 +276,13 @@ journal head as the recovery handle.
|
|
|
276
276
|
`tmp/codex/<unit>-brief.md`, one `codex exec --json` streaming to `tmp/codex/<unit>.jsonl`
|
|
277
277
|
with `--output-last-message`, foreground when it fits the shell cap, backgrounded with the
|
|
278
278
|
turn ended when it may not — the harness re-invocation is the wait; placeholder loops and
|
|
279
|
-
wait-promise reports are deviations.
|
|
279
|
+
wait-promise reports are deviations. Every exec names its working directory with `-C`, and an
|
|
280
|
+
exec rooted outside a trusted git repository dies at launch unless `--skip-git-repo-check` is
|
|
281
|
+
passed, so cross-repo and fleet-container work rooted outside a checkout always passes it. A
|
|
282
|
+
launch is not a launch until the journal grows past its header: the bridge confirms the event
|
|
283
|
+
stream advanced beyond the session-configured head
|
|
284
|
+
before it reports the exec started, and treats an instantly-dead journal as a failed launch
|
|
285
|
+
whose tail is the evidence. Recovery ladder on interruption: persisted-id
|
|
280
286
|
`codex-reply` re-emission → fresh CLI session with the same brief file → for an interrupted
|
|
281
287
|
CLI exec, the journal survives and the Orchestrator chooses resume or fresh.
|
|
282
288
|
- **The inverse bridge exists too:** Claude Code exposes `claude mcp serve`, registered in
|
|
@@ -297,9 +303,17 @@ journal head as the recovery handle.
|
|
|
297
303
|
- At the start of each live Cloud session the user runs `codex login --device-auth` and
|
|
298
304
|
completes ChatGPT approval in the browser. `scripts/codex.sh` only reports readiness; it
|
|
299
305
|
never installs, authenticates, logs out, reads the auth cache, or performs a model call.
|
|
300
|
-
-
|
|
301
|
-
|
|
302
|
-
|
|
306
|
+
- A probe that finds the binary present but authentication unavailable starts recovery in the
|
|
307
|
+
same turn instead of recording the bench dark and waiting: the Orchestrator backgrounds
|
|
308
|
+
`codex login --device-auth` with its output captured to `tmp/codex/login.log`, surfaces the
|
|
309
|
+
verification URL and one-time code to the user the moment they appear there, arms a watcher
|
|
310
|
+
on completion, and re-probes `codex login status` when it fires. The bench comes live
|
|
311
|
+
mid-session with no restart; a session that sits dark until the user asks for the login has
|
|
312
|
+
failed the probe, not the bench.
|
|
313
|
+
- If that recovery cannot complete — device login unavailable, declined, or expired — the
|
|
314
|
+
Codex bench is dark. Fall back to `planner`/`reviewer` (Opus 5) and `builder`, and say so.
|
|
315
|
+
Never substitute an API key, access token, copied `auth.json`, or another login flow unless
|
|
316
|
+
the user changes this policy.
|
|
303
317
|
|
|
304
318
|
Codex environment defaults:
|
|
305
319
|
|
|
@@ -81,6 +81,22 @@ shared-file edits, and tree-wide mutating gates. Return the touched files,
|
|
|
81
81
|
diffstat, scoped validation, and deviation state for independent integration and
|
|
82
82
|
review.
|
|
83
83
|
|
|
84
|
+
## Routing exclusion — defensive negative-test units
|
|
85
|
+
|
|
86
|
+
The provider applies a content-safety filter that terminates a turn mid-run when
|
|
87
|
+
the work requires authoring or reproducing a violation construct, even when the
|
|
88
|
+
purpose is to prove a guard REJECTS it: sandbox escapes, resolution-bypassing
|
|
89
|
+
imports, boundary evasion, injection payloads, credential-handling probes. The
|
|
90
|
+
filter reads the construct, not the intent, so a legitimate negative test trips
|
|
91
|
+
it exactly like an attack would. Observed twice on one unit, at the same point in
|
|
92
|
+
the work, with nothing written to disk either time.
|
|
93
|
+
|
|
94
|
+
Route such a unit to the native Opus `implementer` from the start, and record the
|
|
95
|
+
Codex bench dark for that unit with this reason. Do not soften or obscure a brief
|
|
96
|
+
to slip past the filter — a bench that declines work is a routing fact, not an
|
|
97
|
+
obstacle. The exclusion is per unit: everything else still routes to Sol
|
|
98
|
+
normally, and an audit that merely READS existing negative tests is unaffected.
|
|
99
|
+
|
|
84
100
|
## Journals and Windows
|
|
85
101
|
|
|
86
102
|
- Journals, briefs, session files, and last-message files under `tmp/codex/` are
|
|
@@ -123,14 +123,14 @@ then runs the configured scoped checks that prove environment isolation.
|
|
|
123
123
|
- Lint is a separate complementary gate; neither lint nor root checking replaces
|
|
124
124
|
environment-isolation checks.
|
|
125
125
|
|
|
126
|
-
| Scope | `lib` | `types` | Permitted host globals
|
|
127
|
-
| ---------------------------- | --------------------------------- | ----------------- |
|
|
128
|
-
| `src:core`, `app:core` | `["ESNext"]`
|
|
129
|
-
| `src:browser`, `app:browser` | `["ESNext","DOM","DOM.Iterable"]` | default | DOM; no Node
|
|
130
|
-
| `src:server`, `app:server` | `["ESNext"]` | `["node"]` | Node; no DOM
|
|
131
|
-
| `src:styles` | `["ESNext"]` | `["vite/client"]` | Vite SCSS module declaration only
|
|
132
|
-
|
|
133
|
-
Strict core is load-bearing. A host-dependent helper belongs in its host environment; for example, a `generateId`
|
|
126
|
+
| Scope | `lib` | `types` | Permitted host globals |
|
|
127
|
+
| ---------------------------- | --------------------------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
128
|
+
| `src:core`, `app:core` | `["ESNext","WebWorker"]` | `[]` | WHATWG web interop: fetch family, streams, URL, Abort, encoders, crypto, timers, console, DOMException, structuredClone; no DOM, no Node |
|
|
129
|
+
| `src:browser`, `app:browser` | `["ESNext","DOM","DOM.Iterable"]` | default | DOM; no Node |
|
|
130
|
+
| `src:server`, `app:server` | `["ESNext"]` | `["node"]` | Node; no DOM |
|
|
131
|
+
| `src:styles` | `["ESNext"]` | `["vite/client"]` | Vite SCSS module declaration only |
|
|
132
|
+
|
|
133
|
+
Strict core is load-bearing. A host-dependent helper belongs in its host environment; for example, a `generateId` reading `node:crypto` belongs in server, not core. The worker-only globals — `name`, `onrtctransform`, `close`, `postMessage`, `dispatchEvent`, `location`, `onerror`, `onlanguagechange`, `onoffline`, `ononline`, `onrejectionhandled`, `onunhandledrejection`, `self`, `importScripts`, `fonts`, `caches`, `crossOriginIsolated`, `indexedDB`, `isSecureContext`, `origin`, `scheduler`, `createImageBitmap`, `reportError`, `cancelAnimationFrame`, `requestAnimationFrame`, `onmessage`, `onmessageerror`, `addEventListener`, and `removeEventListener` — are policy-fenced out of core sources, so the `WebWorker` declarations widen the interop surface without admitting a worker host.
|
|
134
134
|
|
|
135
135
|
Build/check config alignment:
|
|
136
136
|
|
|
@@ -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
|
|
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
|
|
|
@@ -133,6 +133,7 @@ From [`types.ts`](../../src/core/types.ts).
|
|
|
133
133
|
| `BuildFormat` | type |
|
|
134
134
|
| `SrcDefinition` | interface |
|
|
135
135
|
| `AppDefinition` | interface |
|
|
136
|
+
| `ViteMachinery` | interface |
|
|
136
137
|
| `Origin` | type |
|
|
137
138
|
| `Group` | type |
|
|
138
139
|
| `Category` | type |
|
|
@@ -183,6 +184,12 @@ is `'INVALID' | 'BLOCKED' | 'DESTROYED' | 'TARGET' | 'WRITE' | 'FETCH'`.
|
|
|
183
184
|
environment contributes, its test-project label, and — on the `src` axis — its `exports` subpath
|
|
184
185
|
and build formats, or — on the `app` axis — its optional runtime entry.
|
|
185
186
|
|
|
187
|
+
`ViteMachinery` names the three host-specific pipelines a workspace's generated `vite.config.ts` may
|
|
188
|
+
carry: `browser` for the CSS pipeline and the Playwright-backed browser test project, `vue` for the
|
|
189
|
+
single-file-component, HTML, and development-server machinery an application browser environment
|
|
190
|
+
needs, and `output` for build-output containment. It never selects a boundary guarantee — those ship
|
|
191
|
+
in every shape, as the compilers section sets out.
|
|
192
|
+
|
|
186
193
|
`Blueprint` is the closed input spec:
|
|
187
194
|
|
|
188
195
|
```ts
|
|
@@ -363,7 +370,9 @@ value lists behind their literal unions. `SRC_MATRIX` is the `src` environment m
|
|
|
363
370
|
data — each environment's `configs/src` files, test-project label, `exports` subpath, and build
|
|
364
371
|
formats. `APP_MATRIX` is its application sibling, adding the runtime entry where an environment produces
|
|
365
372
|
one (`app/browser/index.html`, `app/server/main.ts`). `HOST_PATHS` is the ordered list of
|
|
366
|
-
byte-copied host artifacts
|
|
373
|
+
byte-copied host artifacts, and it is the staging manifest rather than the per-plan carried set:
|
|
374
|
+
`stageHost` vendors every path on it, while each plan carries the subset `selectHostPaths` selects
|
|
375
|
+
for that one workspace.
|
|
367
376
|
|
|
368
377
|
The bounds are public because they are part of the contract, not implementation trivia.
|
|
369
378
|
`MAX_ARTIFACT_BYTES` caps one artifact at 5 MiB and `MAX_TOTAL_ARTIFACT_BYTES` caps one blueprint,
|
|
@@ -537,7 +546,8 @@ entity allocation.
|
|
|
537
546
|
then checks every override against the exact artifact set the plan would write. An override whose
|
|
538
547
|
`path` matches no planned artifact, targets a `host`-origin artifact, or targets the
|
|
539
548
|
blueprint-owned `package.json` publication boundary is a blocking question rather than a silent
|
|
540
|
-
no-op.
|
|
549
|
+
no-op. An override that clears all three lands a `warnings` entry naming the path it replaces — the
|
|
550
|
+
declaration is accepted, and it is never accepted silently.
|
|
541
551
|
|
|
542
552
|
### Validators — server
|
|
543
553
|
|
|
@@ -729,11 +739,13 @@ From [`helpers.ts`](../../src/core/helpers.ts).
|
|
|
729
739
|
| `contentByteLength` | function |
|
|
730
740
|
| `contentToHex` | function |
|
|
731
741
|
| `snapshotOf` | function |
|
|
742
|
+
| `selectHostPaths` | function |
|
|
732
743
|
| `findPathConflict` | function |
|
|
733
744
|
| `findFileConflict` | function |
|
|
734
745
|
| `validateDependencyArray` | function |
|
|
735
746
|
| `validateBlueprint` | function |
|
|
736
747
|
| `manifestToDependencies` | function |
|
|
748
|
+
| `manifestToName` | function |
|
|
737
749
|
| `rangeToFreshness` | function |
|
|
738
750
|
| `computeHash` | function |
|
|
739
751
|
| `stableStringify` | function |
|
|
@@ -779,14 +791,20 @@ report projections count with.
|
|
|
779
791
|
`snapshotOf`, `contentToHex`, `contentToBytes`, `contentByteLength`, `contentCodePoint`, and
|
|
780
792
|
`bytesToHex` are the host-independent byte leaves that make exact comparison possible without a
|
|
781
793
|
host encoder or buffer; an unpaired surrogate encodes as `U+FFFD` rather than throwing.
|
|
782
|
-
`
|
|
783
|
-
|
|
794
|
+
`selectHostPaths` is the one-owner filter plan assembly applies before it carries anything: it
|
|
795
|
+
returns the host paths in input order minus `guides/src/<name>.md`, so a workspace never plans a
|
|
796
|
+
vendored mirror of the guide it writes itself. `findPathConflict` finds the first exact or
|
|
797
|
+
case-insensitive collision in a path list, and `findFileConflict` additionally rejects a file that
|
|
798
|
+
would sit inside another planned path — the loud backstop behind that selection.
|
|
784
799
|
|
|
785
800
|
`validateBlueprint` and `validateDependencyArray` are the semantic pass. The array validator is
|
|
786
801
|
pure — it returns its questions and the set of names it saw, so the caller can apply the
|
|
787
802
|
cross-array overlap rules on top. `manifestToDependencies` reads a manifest's `dependencies`,
|
|
788
803
|
`devDependencies`, and `peerDependencies` in that order, keeps only own data sections and scoped
|
|
789
|
-
names, deduplicates, and never throws. `
|
|
804
|
+
names, deduplicates, and never throws. `manifestToName` is its self-reading sibling over the same
|
|
805
|
+
text: the manifest's own string `name`, or `undefined` when the text is oversized, malformed,
|
|
806
|
+
rootless, or nameless — the projection that lets a target recognize itself in its own declared
|
|
807
|
+
dependencies. `rangeToFreshness` applies the exact-pin comparison; the `missing` and `failed`
|
|
790
808
|
verdicts come from the fetch layer, never from this pure comparison.
|
|
791
809
|
|
|
792
810
|
`computeHash` is a deterministic FNV-1a digest and `stableStringify` a key-order-independent
|
|
@@ -897,7 +915,9 @@ dependency-less stand-in. Environments come from `src/<environment>/` and `app/<
|
|
|
897
915
|
scoped entries, with an optional peer recovered from `peerDependenciesMeta`; and `extras` is every
|
|
898
916
|
development dependency minus the generated baseline and minus anything already declared as a
|
|
899
917
|
dependency or peer, so a hand-added development dependency round-trips and stays audit-clean.
|
|
900
|
-
`overrides
|
|
918
|
+
Derivation yields no `overrides`: they are caller-time inputs, not repository state. A computed
|
|
919
|
+
artifact that must differ reveals a gap in the canon; the blueprint grows an axis for that
|
|
920
|
+
distinction rather than the repository forking the file.
|
|
901
921
|
|
|
902
922
|
`storagePath`, `stageHost`, `readHostManifest`, `locateHostSource`, `remapArtifactPath`, and
|
|
903
923
|
`hydratePlan` are the vendored-host path. `storagePath` maps a repo-relative path to its un-dotted
|
|
@@ -919,7 +939,9 @@ anything.
|
|
|
919
939
|
`consumeCatalogAllowance` decrements the single shared entry allowance and throws `TARGET` before an
|
|
920
940
|
over-budget traversal continues. `discoverPackages` requires a real, unlinked root and lists its
|
|
921
941
|
immediate child directories whose bounded manifest names a scoped package, skipping anything else
|
|
922
|
-
silently.
|
|
942
|
+
silently. A control-bearing child directory fails closed before its manifest is read and the
|
|
943
|
+
untrusted name is never reflected in the diagnostic. `catalogPackages` applies one allowance across
|
|
944
|
+
every root and directory rather than
|
|
923
945
|
resetting a per-root budget, then draws each description from the first paragraph of the first
|
|
924
946
|
blockquote of that package's own bounded guide via `guideToDescription`; a missing guide, an
|
|
925
947
|
unreadable or oversized one, or one with no blockquote yields an empty description rather than an
|
|
@@ -946,6 +968,7 @@ From [`compilers.ts`](../../src/core/compilers.ts).
|
|
|
946
968
|
| `devDependenciesFor` | function |
|
|
947
969
|
| `packageManifest` | function |
|
|
948
970
|
| `rootTsconfig` | function |
|
|
971
|
+
| `viteMachinery` | function |
|
|
949
972
|
| `viteHeader` | function |
|
|
950
973
|
| `policyViteProject` | function |
|
|
951
974
|
| `singleSrcViteConfig` | function |
|
|
@@ -986,11 +1009,40 @@ optional metadata, and engines.
|
|
|
986
1009
|
|
|
987
1010
|
`rootTsconfig` emits the root compiler options and one path alias per declared environment;
|
|
988
1011
|
`coreTsconfig`, `srcTsconfig`, and `appTsconfig` emit the scoped configurations that remove the
|
|
989
|
-
wrong host's globals from each environment.
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
`
|
|
993
|
-
|
|
1012
|
+
wrong host's globals from each environment. A core scope is the interesting one: `lib` is
|
|
1013
|
+
`["ESNext", "WebWorker"]` and `types` stays `[]`, which declares the WHATWG surface that is
|
|
1014
|
+
identical across Node, browsers, and workers — `fetch` and its request/response/header types,
|
|
1015
|
+
streams, `URL`, `AbortController`, the text encoders, `crypto`, timers, `console`, `DOMException`,
|
|
1016
|
+
`structuredClone` — while leaving `document`, `window`, and every `node:*` type unresolvable. That
|
|
1017
|
+
is one declaration set for a host-independent module, not a host. `viteHeader` renders the shared
|
|
1018
|
+
header — the alias block
|
|
1019
|
+
derived from the tsconfig paths, plus the environment-boundary plugin — and `viteMachinery` is the
|
|
1020
|
+
one place the header's axes are derived, read by `rootViteConfig`, `singleSrcViteConfig`,
|
|
1021
|
+
`applicationViteConfig`, and `configArtifacts` alike so no caller can invent a fourth answer.
|
|
1022
|
+
|
|
1023
|
+
**The boundary guarantees do not vary by blueprint.** Every generated `vite.config.ts` — a
|
|
1024
|
+
`core`-only library, an application of `app/core` alone, or the full six-environment workspace —
|
|
1025
|
+
emits `environmentBoundary`, its `resolveId` / `load` / `buildEnd` walks, the module-graph AST audit
|
|
1026
|
+
(`environmentAssetSources`, `parseSync`, `Visitor`), and stylesheet rejection (`isStylesheetPath`
|
|
1027
|
+
plus its `environmentPathError` / `environmentSourceError` clauses). Those enforce owner-independent
|
|
1028
|
+
laws: core stays host-independent whatever else the workspace declares, a server module never
|
|
1029
|
+
imports a stylesheet, and a `@vite-ignore` dynamic import — which `resolveId` never sees and the
|
|
1030
|
+
module graph never records — has no other enforcement point. Only host-specific pipelines vary,
|
|
1031
|
+
along the three `ViteMachinery` axes:
|
|
1032
|
+
|
|
1033
|
+
| Machinery | Emitted when |
|
|
1034
|
+
| ----------------------------------------------------------------- | ------------------------------------ |
|
|
1035
|
+
| CSS pipeline (`ENVIRONMENT_CSS`, `preprocessCSS`, `isCSSRequest`) | a `src` or `app` browser environment |
|
|
1036
|
+
| Playwright provider and `hasChromium` | a `src` or `app` browser environment |
|
|
1037
|
+
| Vue plugin, HTML boundary, browser development server | an `app` browser environment |
|
|
1038
|
+
| Output containment (`outputBoundary`, `enforceOutputPath`) | anything the workspace builds |
|
|
1039
|
+
|
|
1040
|
+
An application of `app/core` alone is the sole shape that builds nothing, so it is the sole shape
|
|
1041
|
+
without output containment — and it still carries every boundary guarantee above.
|
|
1042
|
+
|
|
1043
|
+
`coreViteConfig`, `srcViteConfig`, and `appViteConfig` emit the thin per-target wrappers;
|
|
1044
|
+
`rootViteConfig`, `singleSrcViteConfig`, and `applicationViteConfig` emit the root configuration for
|
|
1045
|
+
a library-only, single non-core `src` environment, and application-bearing workspace respectively;
|
|
994
1046
|
`policyViteProject` emits the dedicated Node-only repository-policy test project.
|
|
995
1047
|
|
|
996
1048
|
`configArtifacts`, `sourceArtifacts`, `applicationArtifacts`, `testArtifacts`, and `guideArtifacts`
|
|
@@ -1094,8 +1146,9 @@ The public methods of each behavioral interface, one table per type.
|
|
|
1094
1146
|
`Scaffolding`; the optional group selection scopes the plan to those artifact groups.
|
|
1095
1147
|
`audit(blueprint, current, groups?)` compiles and then diffs the resulting plan against the
|
|
1096
1148
|
caller-supplied current content; a gated blueprint returns `complete: false` with the gate's
|
|
1097
|
-
blocking questions and zero findings
|
|
1098
|
-
exposes the readonly
|
|
1149
|
+
blocking questions and zero findings, and a complete one carries the gate's advisories on that same
|
|
1150
|
+
`questions` field. `destroy()` is idempotent teardown. The interface also exposes the readonly
|
|
1151
|
+
`emitter`.
|
|
1099
1152
|
|
|
1100
1153
|
#### `PlanManagerInterface`
|
|
1101
1154
|
|
|
@@ -1159,12 +1212,14 @@ in-flight request. The interface also exposes the readonly `emitter`.
|
|
|
1159
1212
|
`compile` runs three stages in fixed order and records each as a `CompileRecord` carrying its input,
|
|
1160
1213
|
its output, whether it failed, and any error text.
|
|
1161
1214
|
|
|
1162
|
-
1. **draft** — `blueprintToPlan` selects the covered groups, drafts each group's artifacts,
|
|
1163
|
-
the host set
|
|
1215
|
+
1. **draft** — `blueprintToPlan` selects the covered groups, drafts each group's artifacts, carries
|
|
1216
|
+
the selected host set — every vendored host path except the workspace's own guide — applies
|
|
1217
|
+
overrides, and pins the draft. A throw here records a `draft` failure coded
|
|
1164
1218
|
`INVALID`, emits `error`, marks the remaining two stages skipped, and returns incomplete.
|
|
1165
1219
|
2. **gate** — `validatePlan` runs the semantic pass over the blueprint and checks every override
|
|
1166
|
-
against the drafted artifact set. Blocking questions fail the stage;
|
|
1167
|
-
vendored guide set
|
|
1220
|
+
against the drafted artifact set. Blocking questions fail the stage; an accepted override and a
|
|
1221
|
+
dependency outside the vendored guide set each contribute a non-blocking advisory question
|
|
1222
|
+
instead.
|
|
1168
1223
|
3. **pin** — a host-origin pointer artifact is appended for each non-vendored dependency, and
|
|
1169
1224
|
`pinPlan` fills `trace` and `hash` from the plan's own content.
|
|
1170
1225
|
|
|
@@ -1196,7 +1251,8 @@ how it is audited, and whether it may ever be overwritten.
|
|
|
1196
1251
|
- **`host`** — byte-copied from the vendored data root. These are the shared files a whole fleet
|
|
1197
1252
|
keeps identical: the root instruction documents and licence, the agent, rule, and skill
|
|
1198
1253
|
directories, the session scripts, the repository coding-law policy module, the byte-identical root
|
|
1199
|
-
dotfiles, and the two guide mirrors
|
|
1254
|
+
dotfiles, and the two line guide mirrors a workspace carries for contracts other than its own.
|
|
1255
|
+
`HOST_PATHS` is the exact vendored list; what a given plan carries is `selectHostPaths` of it.
|
|
1200
1256
|
- **`template`** — filled from a frozen template definition by a pure fill engine. These are
|
|
1201
1257
|
starter files: source stubs, test stubs, the starter guide, the README.
|
|
1202
1258
|
- **`computed`** — derived by this package's own combination logic. These are the structural files:
|
|
@@ -1225,18 +1281,31 @@ anything, so a mature workspace's hand-written source, tests, guides, and manife
|
|
|
1225
1281
|
overwritten with a stub. A consequence worth stating plainly: the generated
|
|
1226
1282
|
`.github/workflows/ci.yml` is a **computed** artifact, so **user-owned CI is never repaired**. Once
|
|
1227
1283
|
a workspace has its own workflow, that copy stands, and any change to it is an ordinary edit in that
|
|
1228
|
-
workspace.
|
|
1284
|
+
workspace. Audit still compares it because computed artifacts are content-aware canon. A legitimate
|
|
1285
|
+
difference that the blueprint cannot express is a canon gap: add the missing blueprint axis rather
|
|
1286
|
+
than forking the computed file in one repository.
|
|
1229
1287
|
|
|
1230
1288
|
Overrides respect the same boundary from the other direction. `applyOverrides` never replaces a
|
|
1231
1289
|
host-origin artifact and never replaces `package.json`; the gate turns either attempt — and an
|
|
1232
1290
|
override matching no planned artifact at all — into a blocking question rather than a silent no-op.
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1291
|
+
What survives those three refusals is applied and announced: the gate carries a non-blocking
|
|
1292
|
+
advisory naming each replaced path, and that advisory rides the `Scaffolding` and the `Audit` all
|
|
1293
|
+
the way through the library result.
|
|
1294
|
+
|
|
1295
|
+
Guide mirrors are the one place ownership is conditional, and the law is one owner per guide path.
|
|
1296
|
+
**A workspace mirrors every line guide except its own.** When the name matches — the guide package
|
|
1297
|
+
on `guides/src/guide.md`, this package on `guides/src/scaffold.md` — the workspace itself is the
|
|
1298
|
+
owner, keeping that path as its **template**-origin starter guide, and `selectHostPaths` drops the
|
|
1299
|
+
vendored mirror so the path is contributed exactly once. For every other contract the mirror is the
|
|
1300
|
+
owner: a dependency this package vendors a byte-identical mirror for gets a real host-origin copy of
|
|
1301
|
+
`guides/src/<short>.md`, contributed once whether it arrives through the host set or through the
|
|
1302
|
+
dependency, so a package depending on `@orkestrel/guide` plans one `guides/src/guide.md` rather than
|
|
1303
|
+
two. Any other dependency gets a host-origin _pointer_ artifact plus a non-blocking question, never
|
|
1304
|
+
a fabricated mirror; on materialization that pointer degrades to a short stub, and `scaffold pull`
|
|
1305
|
+
fetches the real thing. That degrade is scoped exactly to guide pointers: any other missing manifest
|
|
1306
|
+
entry means a corrupt or truncated vendored manifest, and fails closed. Selection is the law and
|
|
1307
|
+
`findFileConflict` is its backstop: two artifacts at one path refuse the plan rather than racing to
|
|
1308
|
+
be the last writer.
|
|
1240
1309
|
|
|
1241
1310
|
## Audit, repair, and prune
|
|
1242
1311
|
|
|
@@ -1286,10 +1355,15 @@ because there is nothing to explain.
|
|
|
1286
1355
|
`pull` is the target-aware composition. It reads the target's declared scoped dependencies from its
|
|
1287
1356
|
manifest, rejects any explicit selection the target does not declare, builds the reference map from
|
|
1288
1357
|
the target's own `guides/src/<short>.md` mirrors, fetches guides and versions under one shared
|
|
1289
|
-
allowance, and assembles a report whose `clean` flag requires both no drift and no failures.
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1358
|
+
allowance, and assembles a report whose `clean` flag requires both no drift and no failures. A
|
|
1359
|
+
target that declares itself is the one asymmetry, and it follows the same single-owner law: the
|
|
1360
|
+
guide pass drops the self dependency, so `pull` never fetches or writes a workspace's own contract
|
|
1361
|
+
guide over the copy that workspace owns, while the version pass keeps it and still reports its
|
|
1362
|
+
freshness. A `--live` audit reads upstream through the same two passes and applies the same
|
|
1363
|
+
self-exclusion, so the freshness a workspace reports about itself never depends on which verb asked.
|
|
1364
|
+
`write` then commits only the `behind` guides — never `current`, `missing`, or `failed`,
|
|
1365
|
+
none of which carry trustworthy content — under the same containment and precondition law
|
|
1366
|
+
`Materializer` enforces, including a baseline digest check against what is actually on disk.
|
|
1293
1367
|
|
|
1294
1368
|
`catalog` builds the fleet package catalog from three reads per entry. The registry's exact
|
|
1295
1369
|
organization package list is authoritative and unconditionally required. Each package's own registry
|
|
@@ -1331,10 +1405,20 @@ a fixed, interleaved order so aggregates sit immediately before their per-enviro
|
|
|
1331
1405
|
live generated-consumer integration gate for the scaffold engine itself
|
|
1332
1406
|
|
|
1333
1407
|
**Environment isolation.** Scoped TypeScript projects remove the wrong host's globals from each
|
|
1334
|
-
environment: core scopes carry
|
|
1335
|
-
and no Node; server scopes carry Node and no DOM.
|
|
1336
|
-
|
|
1337
|
-
|
|
1408
|
+
environment: core scopes carry the WHATWG web-interop surface and no host at all — no DOM, no Node,
|
|
1409
|
+
no `vite/client`; browser scopes carry DOM and no Node; server scopes carry Node and no DOM. The
|
|
1410
|
+
worker-only globals the `WebWorker` declarations would otherwise admit — `name`, `onrtctransform`,
|
|
1411
|
+
`close`, `postMessage`, `dispatchEvent`, `location`, `onerror`, `onlanguagechange`, `onoffline`,
|
|
1412
|
+
`ononline`, `onrejectionhandled`, `onunhandledrejection`, `self`, `importScripts`, `fonts`, `caches`,
|
|
1413
|
+
`crossOriginIsolated`, `indexedDB`, `isSecureContext`, `origin`, `scheduler`, `createImageBitmap`,
|
|
1414
|
+
`reportError`, `cancelAnimationFrame`, `requestAnimationFrame`, `onmessage`, `onmessageerror`,
|
|
1415
|
+
`addEventListener`, and `removeEventListener` — are fenced out of `src/core` and `app/core` sources
|
|
1416
|
+
by the policy suite, so the declarations widen what a host-independent module may call without
|
|
1417
|
+
widening where it may run. On every TypeScript bump, derive this list from the module-scope
|
|
1418
|
+
global-object `declare var` and `declare function` declarations in the installed
|
|
1419
|
+
`lib.webworker.d.ts`, then subtract values supplied by `lib.esnext*` or current Node globals. Lint
|
|
1420
|
+
restricts declared package, alias, and conventional relative imports in the same directions.
|
|
1421
|
+
Neither replaces the other, and neither replaces the build.
|
|
1338
1422
|
|
|
1339
1423
|
**The generated build boundary.** The emitted configuration carries an environment-boundary plugin
|
|
1340
1424
|
that resolves the real module graph rather than re-implementing a parser. **TypeScript and
|
|
@@ -1454,7 +1538,8 @@ cannot express — that a centralized module exports every top-level declaration
|
|
|
1454
1538
|
implementation files hold one class and no stray module-scope declaration, that no function is
|
|
1455
1539
|
declared inside another function outside a directly-passed callback, that interface properties are
|
|
1456
1540
|
readonly, that privacy is a runtime `#` field rather than a TypeScript modifier, that a barrel
|
|
1457
|
-
re-exports only through `export *`,
|
|
1541
|
+
re-exports only through `export *`, that a core source never names a worker-only global the
|
|
1542
|
+
`WebWorker` declarations expose, and that a computed dynamic import cannot smuggle a
|
|
1458
1543
|
cross-environment dependency past the declared import rules. Vue components are inspected for the
|
|
1459
1544
|
same evasions. It is a complement to lint and typecheck, never a second type system, and it is not a
|
|
1460
1545
|
general-purpose source analyzer. Generated workspaces receive the same exported policy module as a
|
|
@@ -1464,7 +1549,11 @@ host-origin file and run it as a dedicated Node-only `policy` test project over
|
|
|
1464
1549
|
**Real browser capability.** Browser test projects are gated on the real executable: the generated
|
|
1465
1550
|
configuration and the generated policy test both probe `existsSync(chromium.executablePath())`. A
|
|
1466
1551
|
browser suite runs when a real Chromium is installed and is skipped honestly when it is not, rather
|
|
1467
|
-
than being faked.
|
|
1552
|
+
than being faked. The gate is applied at registration, not only inside the project: without a
|
|
1553
|
+
Chromium the browser project is left out of the emitted `projects` list entirely, so the runner
|
|
1554
|
+
never has to reconcile a registered project whose include set resolves to nothing, and one printed
|
|
1555
|
+
warning names every omitted project label. A machine with a browser runs the browser suite; a
|
|
1556
|
+
machine without one runs the remaining projects and says so.
|
|
1468
1557
|
|
|
1469
1558
|
**Continuous integration.** The generated workflow runs on push and pull request, on
|
|
1470
1559
|
`ubuntu-latest`, with read-only contents permission, a 60-minute timeout, and a matrix that **tests
|
|
@@ -1872,6 +1961,7 @@ import {
|
|
|
1872
1961
|
hostGroup,
|
|
1873
1962
|
packageManifest,
|
|
1874
1963
|
paritySpecifiers,
|
|
1964
|
+
selectHostPaths,
|
|
1875
1965
|
sourceArtifacts,
|
|
1876
1966
|
srcVariant,
|
|
1877
1967
|
testArtifacts,
|
|
@@ -1881,6 +1971,7 @@ const spec = blueprint('router', { src: ['core'], app: ['core', 'server'] })
|
|
|
1881
1971
|
const members = blueprintToMembers(spec)
|
|
1882
1972
|
|
|
1883
1973
|
hostGroup('AGENTS.md') // 'docs'
|
|
1974
|
+
selectHostPaths(['guides/src/router.md', 'LICENSE'], spec.name) // ['LICENSE'] — never its own guide
|
|
1884
1975
|
srcVariant(['core', 'server']) // 'multi'
|
|
1885
1976
|
entryFields(['browser']).main // './dist/src/browser/index.js'
|
|
1886
1977
|
dualCondition('./dist/src/core/index')
|
|
@@ -1924,6 +2015,7 @@ import {
|
|
|
1924
2015
|
srcTsconfig,
|
|
1925
2016
|
srcViteConfig,
|
|
1926
2017
|
viteHeader,
|
|
2018
|
+
viteMachinery,
|
|
1927
2019
|
} from '@orkestrel/scaffold'
|
|
1928
2020
|
|
|
1929
2021
|
rootTsconfig(['core'], ['core', 'server'])
|
|
@@ -1931,7 +2023,9 @@ coreTsconfig()
|
|
|
1931
2023
|
srcTsconfig('server')
|
|
1932
2024
|
appTsconfig('browser', true)
|
|
1933
2025
|
|
|
1934
|
-
|
|
2026
|
+
viteMachinery(['core']) // { browser: false, vue: false, output: true }
|
|
2027
|
+
viteMachinery([], ['core', 'browser']) // { browser: true, vue: true, output: true }
|
|
2028
|
+
viteHeader(viteMachinery([], ['core', 'browser'])) // the shared header, with browser and Vue support
|
|
1935
2029
|
coreViteConfig()
|
|
1936
2030
|
srcViteConfig('browser')
|
|
1937
2031
|
appViteConfig('server')
|
|
@@ -1945,7 +2039,12 @@ applicationViteConfig([], ['core', 'server']).includes('appServer') // true
|
|
|
1945
2039
|
### Reading declared dependencies and comparing freshness
|
|
1946
2040
|
|
|
1947
2041
|
```ts
|
|
1948
|
-
import {
|
|
2042
|
+
import {
|
|
2043
|
+
isBehind,
|
|
2044
|
+
manifestToDependencies,
|
|
2045
|
+
manifestToName,
|
|
2046
|
+
rangeToFreshness,
|
|
2047
|
+
} from '@orkestrel/scaffold'
|
|
1949
2048
|
import {
|
|
1950
2049
|
guideStub,
|
|
1951
2050
|
packageShortName,
|
|
@@ -1954,6 +2053,7 @@ import {
|
|
|
1954
2053
|
} from '@orkestrel/scaffold/server'
|
|
1955
2054
|
|
|
1956
2055
|
manifestToDependencies('{"dependencies":{"@orkestrel/contract":"^0.0.7"}}')
|
|
2056
|
+
manifestToName('{"name":"@orkestrel/router"}') // '@orkestrel/router' — the target's own name
|
|
1957
2057
|
rangeToFreshness('^0.0.7', '0.0.7') // 'current'
|
|
1958
2058
|
isBehind(rangeToFreshness('^0.0.7', '0.0.9')) // true
|
|
1959
2059
|
|
|
@@ -2186,7 +2286,8 @@ isMissingPathError(caught) // true only for an ENOENT error
|
|
|
2186
2286
|
## Tests
|
|
2187
2287
|
|
|
2188
2288
|
- [`tests/src/core/helpers.test.ts`](../../tests/src/core/helpers.test.ts) — the pure leaves: table
|
|
2189
|
-
alignment, byte encoding, snapshots, conflicts, projections, hashing, and
|
|
2289
|
+
alignment, byte encoding, snapshots, host selection, conflicts, projections, hashing, and
|
|
2290
|
+
format-stable JSON.
|
|
2190
2291
|
- [`tests/src/core/builders.test.ts`](../../tests/src/core/builders.test.ts) — the blueprint,
|
|
2191
2292
|
dependency, override, and member builders, including optional-field omission.
|
|
2192
2293
|
- [`tests/src/core/validators.test.ts`](../../tests/src/core/validators.test.ts) — every guard and
|
|
@@ -2194,7 +2295,8 @@ isMissingPathError(caught) // true only for an ENOENT error
|
|
|
2194
2295
|
- [`tests/src/core/shapers.test.ts`](../../tests/src/core/shapers.test.ts) — per-shape guard
|
|
2195
2296
|
exactness, schema essentials, seeded generation, and parse round-trips.
|
|
2196
2297
|
- [`tests/src/core/compilers.test.ts`](../../tests/src/core/compilers.test.ts) — every drafted
|
|
2197
|
-
group, the manifest and exports combination rules,
|
|
2298
|
+
group, the manifest and exports combination rules, the one-owner guide law for a workspace that
|
|
2299
|
+
names a line guide, and the emitted configuration text.
|
|
2198
2300
|
- [`tests/src/core/Compiler.test.ts`](../../tests/src/core/Compiler.test.ts) — the three-stage
|
|
2199
2301
|
pipeline, the fail-closed gate, the emission sequences, and post-destroy behavior.
|
|
2200
2302
|
- [`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
|