@orkestrel/scaffold 0.0.31 → 0.0.32
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/main.js +144 -23
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/orchestration.md +20 -1
- package/dist/host/claude/agents/grok.md +17 -6
- package/dist/host/claude/rules/architecture.md +12 -0
- package/dist/host/claude/rules/documentation.md +2 -1
- package/dist/host/claude/rules/tests.md +57 -21
- package/dist/host/claude/rules/workspace.md +25 -20
- package/dist/host/dotfiles/oxlintrc.json +1 -0
- package/dist/host/guides/scaffold.md +142 -67
- package/dist/host/tests/config.test.ts +45 -17
- package/dist/host/tests/policy.test.ts +254 -2
- package/dist/host/tests/setupPolicy.ts +297 -34
- package/dist/src/core/index.cjs +176 -179
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +90 -33
- package/dist/src/core/index.d.ts +90 -33
- package/dist/src/core/index.js +171 -180
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +13 -1
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.js +14 -2
- package/dist/src/server/index.js.map +1 -1
- package/package.json +4 -4
|
@@ -10,8 +10,15 @@ paths:
|
|
|
10
10
|
|
|
11
11
|
## Test contract
|
|
12
12
|
|
|
13
|
-
- Mirror
|
|
14
|
-
`tests/{src,app}/[environment]/[domain]/[
|
|
13
|
+
- Mirror module/application structure:
|
|
14
|
+
`tests/{src,app}/[environment]/[domain]/[module].test.ts`.
|
|
15
|
+
- The mirrored population is `src` and `app` alone. `configs/` is a source directory and is
|
|
16
|
+
deliberately not a mirrored root: its leaves produce the workspace's configuration rather than ship
|
|
17
|
+
in it, and they are proved from `tests/config.test.ts` beside the configuration they produce. Do
|
|
18
|
+
not add `tests/configs/`.
|
|
19
|
+
- Resolve a mirrored module through `.ts`, `.tsx`, `.mts`, `.cts`, `.vue`, `.scss`, or `.css`.
|
|
20
|
+
- Resolve a Sass or CSS partial through the module's leading underscore.
|
|
21
|
+
- Resolve a `setup*` module test against its sibling `setup*.ts` module inside `tests/`.
|
|
15
22
|
- Prefer test filenames matching entrypoints: `index.test.ts` for `index.ts`, `main.test.ts` for `main.ts`.
|
|
16
23
|
- Tests are deterministic: identical inputs produce identical results.
|
|
17
24
|
- Keep default suites fast: timers normally use 10–50 ms and tests make no network calls.
|
|
@@ -37,16 +44,20 @@ paths:
|
|
|
37
44
|
A proof that covers the workspace instead of one module has a fixed location, so no package invents
|
|
38
45
|
its own:
|
|
39
46
|
|
|
40
|
-
| Path | Proves
|
|
41
|
-
| ---------------------------- |
|
|
42
|
-
| `tests/policy.test.ts` | Every source file obeys the syntactic coding and placement law
|
|
43
|
-
| `tests/config.test.ts` | Root configuration resolves its aliases, projects, and outputs
|
|
44
|
-
| `tests/guides.test.ts` | Every documented API exists and every public API is documented
|
|
45
|
-
| `tests/conformance.test.ts` | Where this package drifts from the official tooling it tracks
|
|
46
|
-
| `tests/
|
|
47
|
-
| `tests/
|
|
47
|
+
| Path | Proves |
|
|
48
|
+
| ---------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| `tests/policy.test.ts` | Every source file obeys the syntactic coding and placement law |
|
|
50
|
+
| `tests/config.test.ts` | Root configuration resolves its aliases, projects, and outputs, and the `configs/` leaves behind them |
|
|
51
|
+
| `tests/guides.test.ts` | Every documented API exists and every public API is documented |
|
|
52
|
+
| `tests/conformance.test.ts` | Where this package drifts from the official tooling it tracks |
|
|
53
|
+
| `tests/distribution.test.ts` | The packed package installs and resolves through its public exports |
|
|
54
|
+
| `tests/integration.test.ts` | The package's features work together end to end across environments |
|
|
55
|
+
| `tests/service/**/*.test.ts` | The live external services this package drives, driven for real |
|
|
48
56
|
|
|
49
57
|
- `.claude/rules/workspace.md` names the Vitest project each location belongs to.
|
|
58
|
+
- The `guides` project runs in Node with the browser disabled. Its subject is
|
|
59
|
+
documented-name-to-real-export. A proof that renders a component and compares it against a
|
|
60
|
+
definition is a composition and belongs in an `integration.test.ts` scoped to its directory.
|
|
50
61
|
- `integration.test.ts` is a reserved filename at any level. It names a scope rather than a module,
|
|
51
62
|
so the mirror rule does not reach it; its scope is the directory it sits in.
|
|
52
63
|
- An integration test is an end-to-end test: it composes the package's own features and drives them
|
|
@@ -58,9 +69,13 @@ its own:
|
|
|
58
69
|
- Do not put a packaging, install, or distribution check in an integration test. What the tarball
|
|
59
70
|
contains is a different question from whether the features compose.
|
|
60
71
|
- A test the mirror rule flags is a misplaced test until its placement is checked. Move it to the
|
|
61
|
-
location its scope names.
|
|
62
|
-
|
|
63
|
-
|
|
72
|
+
location its scope names. Widen the rule only when that check shows the test already sits at the
|
|
73
|
+
location its scope names and the rule's population omits a module this ruleset mandates, and then
|
|
74
|
+
widen the population rather than admitting the individual case.
|
|
75
|
+
- A nested `tests/{src,app}/<environment>/**/integration.test.ts` runs in that environment's project,
|
|
76
|
+
whose existing glob collects it exactly once. Give it a separate exact-path project entry only when
|
|
77
|
+
the proof needs different setup or a different runtime, and exclude that exact path from the
|
|
78
|
+
environment project when you do.
|
|
64
79
|
|
|
65
80
|
## Probes
|
|
66
81
|
|
|
@@ -96,7 +111,8 @@ Live external services/models are the deliberate exception to fast hermetic defa
|
|
|
96
111
|
- Put them in the `service` project, under `tests/service/`, with `tests/setupService.ts` for setup
|
|
97
112
|
and a longer timeout. That module's presence is what registers the project, so a live proof with
|
|
98
113
|
no readiness setup is a project nothing configures.
|
|
99
|
-
-
|
|
114
|
+
- `.claude/rules/workspace.md` fixes which gate runs the `service` project, and that gate is not the
|
|
115
|
+
same one in a publishing and a `private: true` workspace.
|
|
100
116
|
- Warm and verify service readiness in `tests/setupService.ts`.
|
|
101
117
|
- Hard-require readiness: throw loudly; never silently skip.
|
|
102
118
|
- Verify service-dependent logic through that service's project, not unrelated module tests or scattered conditional skips.
|
|
@@ -108,7 +124,13 @@ Live external services/models are the deliberate exception to fast hermetic defa
|
|
|
108
124
|
A test that spawns a process, packs, installs, or drives a real build is a proof, not a unit test.
|
|
109
125
|
|
|
110
126
|
- Give it its own Vitest project with its own setup and timeout.
|
|
111
|
-
- Keep it out of the default run
|
|
127
|
+
- Keep it out of the default run where the workspace has a gate that can hold it.
|
|
128
|
+
`.claude/rules/workspace.md` fixes which gate each isolated project runs from, and that placement
|
|
129
|
+
differs between a publishing and a `private: true` workspace.
|
|
130
|
+
- The fixed expensive-proof projects are `distribution` and `service`.
|
|
131
|
+
- A `distribution` proof reads `import.meta.env.MODE` and fails, rather than skips, on an unreachable
|
|
132
|
+
registry under `--mode release`. The publish gate invokes it that way, so a proof that skips there
|
|
133
|
+
passes the gate without ever proving the artifact installs. An ordinary local run may still skip.
|
|
112
134
|
- Slow and hermetic is reason enough to isolate a proof; it need not touch an external service.
|
|
113
135
|
- Where such a proof stays in a shared project, size its budget from a full contended run rather than from an isolated one. A budget that clears the isolated cost by a thin margin turns contention into a red gate reporting a timeout, which carries no diagnostic about the code and costs a full investigation to dismiss.
|
|
114
136
|
|
|
@@ -116,6 +138,8 @@ A test that spawns a process, packs, installs, or drives a real build is a proof
|
|
|
116
138
|
|
|
117
139
|
Test helpers are shared infrastructure, not local test-file clutter.
|
|
118
140
|
|
|
141
|
+
`@orkestrel/test` owns the helpers every workspace repeats: the call recorder, the real delay, the JSON and async collectors, and the owned scratch directory. Import them from `@orkestrel/test`, and its Node-only helpers from `@orkestrel/test/server`. Write a helper of your own only where the package exports none for the job. The shapes below are the contract a workspace codes against, not source to copy.
|
|
142
|
+
|
|
119
143
|
- Extract a fixture, recorder, event factory, async wait, renderer, scenario/data builder, protocol fixture, or DOM builder as soon as it could serve another test.
|
|
120
144
|
- Any duplicate or near-duplicate helper is a defect; consolidate it into one general form.
|
|
121
145
|
- Export every reusable helper, fixture type, factory, constant, and guard from setup files.
|
|
@@ -124,7 +148,7 @@ Test helpers are shared infrastructure, not local test-file clutter.
|
|
|
124
148
|
- Test files import shared infrastructure rather than declaring local fixture factories.
|
|
125
149
|
- Never reimplement a framework helper in tests or fixtures; import the real parser, signer, flattener, or other helper.
|
|
126
150
|
- Prefer small customizable factories/stubs that seed inert data for a real scenario over repeated inline setup.
|
|
127
|
-
- Helper names follow module-helper naming: `
|
|
151
|
+
- Helper names follow module-helper naming: `createFixtureServer`, `buildElement`, `appendItems`, `renderRows`, `waitForReady`, `extractDetail`.
|
|
128
152
|
|
|
129
153
|
Place helpers by environment:
|
|
130
154
|
|
|
@@ -135,10 +159,10 @@ Place helpers by environment:
|
|
|
135
159
|
|
|
136
160
|
### Recorder
|
|
137
161
|
|
|
138
|
-
|
|
162
|
+
Import `createRecorder` from `@orkestrel/test` instead of a framework spy when only calls and arguments matter. It returns:
|
|
139
163
|
|
|
140
164
|
```ts
|
|
141
|
-
interface
|
|
165
|
+
interface RecorderInterface<TArgs extends readonly unknown[]> {
|
|
142
166
|
readonly calls: readonly TArgs[]
|
|
143
167
|
readonly count: number
|
|
144
168
|
readonly handler: (...args: TArgs) => void
|
|
@@ -148,11 +172,23 @@ interface TestRecorderInterface<TArgs extends readonly unknown[]> {
|
|
|
148
172
|
|
|
149
173
|
### Delay
|
|
150
174
|
|
|
151
|
-
|
|
175
|
+
Import `waitForDelay` from `@orkestrel/test`; never repeat an inline timeout promise. It waits for one host timer and defaults to `0`:
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
function waitForDelay(ms?: number): Promise<void>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Scratch
|
|
182
|
+
|
|
183
|
+
Import `createScratch` from `@orkestrel/test/server` when a proof needs real files. It allocates a temporary directory it owns, contains every path against escape, and removes the directory on `destroy`:
|
|
152
184
|
|
|
153
185
|
```ts
|
|
154
|
-
|
|
155
|
-
|
|
186
|
+
interface ScratchInterface {
|
|
187
|
+
readonly path: string
|
|
188
|
+
write(target: string, text: string): void
|
|
189
|
+
read(target: string): string | undefined
|
|
190
|
+
exists(target: string): boolean
|
|
191
|
+
destroy(): void
|
|
156
192
|
}
|
|
157
193
|
```
|
|
158
194
|
|
|
@@ -114,21 +114,26 @@ environment:
|
|
|
114
114
|
The second axis is cross-cutting workspace proofs. Each one covers the whole workspace rather than
|
|
115
115
|
one environment, so each is its own project:
|
|
116
116
|
|
|
117
|
-
| Project
|
|
118
|
-
|
|
|
119
|
-
| `policy`
|
|
120
|
-
| `config`
|
|
121
|
-
| `guides`
|
|
122
|
-
| `conformance`
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
117
|
+
| Project | Files | Proves | Gate |
|
|
118
|
+
| -------------- | ---------------------------- | ------------------------------------------------------------------- | ------------------------------------- |
|
|
119
|
+
| `policy` | `tests/policy.test.ts` | Every source file obeys the syntactic coding and placement law | `test` |
|
|
120
|
+
| `config` | `tests/config.test.ts` | Root configuration resolves its aliases, projects, and outputs | `test` |
|
|
121
|
+
| `guides` | `tests/guides.test.ts` | Every documented API exists and every public API is documented | `test` |
|
|
122
|
+
| `conformance` | `tests/conformance.test.ts` | Where this package drifts from the official tooling it tracks | `test` |
|
|
123
|
+
| `distribution` | `tests/distribution.test.ts` | The packed package installs and resolves through its public exports | `prepublishOnly`; absent when private |
|
|
124
|
+
| `integration` | `tests/integration.test.ts` | The package's features work together end to end across environments | `test` |
|
|
125
|
+
| `service` | `tests/service/**/*.test.ts` | The live external services this package drives, driven for real | `prepublishOnly`; `test` when private |
|
|
126
|
+
|
|
127
|
+
`conformance`, `integration`, `distribution`, and `service` are four subjects, not four names for
|
|
128
|
+
one.
|
|
129
|
+
Keep `conformance` in `test`: measure this package against an installed official artifact, and start
|
|
130
|
+
any server the proof drives itself. Keep `integration` in `test`: compose the workspace's public
|
|
131
|
+
surfaces without packing, installing, or driving an external service. In a publishing workspace,
|
|
132
|
+
run `distribution` and `service` from `prepublishOnly`: pack and install the package in
|
|
133
|
+
`distribution`, and drive the real service with `tests/setupService.ts`, longer timeouts, and no file
|
|
134
|
+
parallelism in `service`. In a `private: true` workspace, never declare `prepublishOnly`; omit
|
|
135
|
+
`distribution`, reach `service` from `test`, and retain the service project's isolated
|
|
136
|
+
configuration.
|
|
132
137
|
|
|
133
138
|
One project sits on neither axis. `probe` includes `tmp/probe/**/*.test.ts` so an agent can run a
|
|
134
139
|
throwaway instrument against real sources, aliases and setup. Declare no proof there. Every test
|
|
@@ -139,9 +144,9 @@ script names its project, so no gate runs it; its directory is ignored by git; a
|
|
|
139
144
|
- A live-service project is the fifth kind. It is the `service` project above, `scripts/service.sh`
|
|
140
145
|
provisions what it drives, and `.claude/rules/tests.md` governs it. Name it `service` whatever it
|
|
141
146
|
drives.
|
|
142
|
-
-
|
|
143
|
-
is hermetic but slow — it spawns processes, packs, installs, or drives a real build.
|
|
144
|
-
-
|
|
147
|
+
- In a publishing workspace, a project leaves the default run when it drives a live external
|
|
148
|
+
service or is hermetic but slow — it spawns processes, packs, installs, or drives a real build.
|
|
149
|
+
- Give every isolated project its own script, and place that script by the paragraph above.
|
|
145
150
|
|
|
146
151
|
Setup assets:
|
|
147
152
|
|
|
@@ -151,7 +156,7 @@ Setup assets:
|
|
|
151
156
|
|
|
152
157
|
Scope with `test:src`, `test:src:core`, `test:app`, `test:app:server`, and equivalent scripts. Each
|
|
153
158
|
cross-cutting project has its own script too: `test:policy`, `test:config`, `test:guides`,
|
|
154
|
-
`test:conformance`, `test:integration`, `test:service`.
|
|
159
|
+
`test:conformance`, `test:distribution`, `test:integration`, `test:service`.
|
|
155
160
|
|
|
156
161
|
## Typechecking and environment isolation
|
|
157
162
|
|
|
@@ -206,7 +211,7 @@ Build/check config alignment:
|
|
|
206
211
|
| `test` | Environment projects plus non-isolated cross-cutting proofs |
|
|
207
212
|
| `clean` | Remove `dist/` |
|
|
208
213
|
| `copy <from> <to>` | Copy while creating parent directories |
|
|
209
|
-
| `prepublishOnly` |
|
|
214
|
+
| `prepublishOnly` | Publishing workspaces only: the gate chain, then isolated proofs |
|
|
210
215
|
|
|
211
216
|
Run `show` only **after** formatting. The committed `demo/showcase.html` is generated/minified; formatting after generation would expand its inlined bundle.
|
|
212
217
|
|
|
@@ -71,8 +71,8 @@ Exported from `@orkestrel/scaffold`, and reachable from
|
|
|
71
71
|
| `HostArtifact` | interface | A file byte-copied from the vendored data root, planned before its bytes are read. |
|
|
72
72
|
| `HydratedArtifact` | interface | A vendored file whose exact bytes have been read, so its content can be compared. |
|
|
73
73
|
| `Override` | interface | One artifact override. |
|
|
74
|
-
| `PlanSummary` | interface | The tally of one plan by artifact origin. |
|
|
75
74
|
| `Plan` | interface | The compiled, ordered artifact list and the selection it covers. |
|
|
75
|
+
| `PlanSummary` | interface | The tally of one plan by artifact origin. |
|
|
76
76
|
| `Question` | interface | One validation issue raised against a blueprint or a plan. |
|
|
77
77
|
| `Scaffolding` | interface | The replayable outcome of one compile. |
|
|
78
78
|
| `SrcDefinition` | interface | The build and export settings one published `src` environment contributes. |
|
|
@@ -94,20 +94,22 @@ Exported from `@orkestrel/scaffold`, and reachable from
|
|
|
94
94
|
| `CONFIG_TEMPLATES` | const | Formatter-stable template text for every configuration artifact. |
|
|
95
95
|
| `CONFORMANCE_TEST_PATH` | const | The official-tooling drift proof whose presence makes a workspace `conformance`. |
|
|
96
96
|
| `CONTROL_CHARACTER_PATTERN` | const | Unicode controls, formatting controls, and line and paragraph separators rejected in text. |
|
|
97
|
+
| `DECLARATION_DEV_DEPENDENCIES` | const | The development dependencies that emit declarations for published source or an executable. |
|
|
97
98
|
| `DEFAULT_ENGINES` | const | The `engines.node` range a workspace starts with. |
|
|
98
99
|
| `DEFAULT_VERSION` | const | The version a workspace starts at. |
|
|
99
100
|
| `DEPENDENCY_NAME_PATTERN` | const | The runtime dependency name syntax: the `@orkestrel` scope and a bare name. |
|
|
101
|
+
| `DISTRIBUTION_TEST_PATH` | const | The packed-package proof whose presence makes a workspace `distribution`. |
|
|
100
102
|
| `ENGINES_PATTERN` | const | The minimum-Node engine syntax a blueprint declares. |
|
|
101
103
|
| `ENVIRONMENTS` | const | The three `Environment` values, frozen. |
|
|
104
|
+
| `EXECUTABLE_PATHS` | const | The vendored paths a target receives with its executable bit set, frozen. |
|
|
102
105
|
| `EXTRA_NAME_PATTERN` | const | The development extra name syntax: any valid npm package name. |
|
|
103
106
|
| `EXTRA_RANGE_PATTERN` | const | The registry-only semver subset accepted for a development extra's range. |
|
|
104
107
|
| `GLOBAL_SETUP_PATH` | const | The shared Vitest global-setup module whose presence makes a workspace `global`. |
|
|
105
|
-
| `GUIDES_TEST_PATH` | const | The guide-parity proof whose physical file selects the fixed `guides` project. |
|
|
106
|
-
| `EXECUTABLE_PATHS` | const | The vendored paths a target receives with its executable bit set, frozen. |
|
|
107
108
|
| `GROUPS` | const | The seven `Group` values in plan order, frozen. |
|
|
109
|
+
| `GUIDES_TEST_PATH` | const | The guide-parity proof whose presence selects the planned `guides` project. |
|
|
108
110
|
| `HEX_PATTERN` | const | Exact lowercase hexadecimal bytes: two digits per byte, and empty content is valid. |
|
|
109
111
|
| `HOST_PATHS` | const | The paths byte-copied from the vendored data root, frozen. |
|
|
110
|
-
| `INTEGRATION_TEST_PATH` | const | The
|
|
112
|
+
| `INTEGRATION_TEST_PATH` | const | The cross-environment composition proof whose presence makes a workspace `integration`. |
|
|
111
113
|
| `INVALID_PATH_CHARACTER_PATTERN` | const | Visible characters a target-relative path and a Markdown path cell both forbid. |
|
|
112
114
|
| `MAX_ARTIFACT_BYTES` | const | Maximum bytes accepted for one artifact. |
|
|
113
115
|
| `MAX_ARTIFACT_HEX_LENGTH` | const | Maximum length of the hexadecimal string carrying one artifact's bytes. |
|
|
@@ -124,6 +126,7 @@ Exported from `@orkestrel/scaffold`, and reachable from
|
|
|
124
126
|
| `ORCHESTRATION_PATH_NAMES` | const | The exact root filenames that wire an agent bench rather than the toolchain, frozen. |
|
|
125
127
|
| `ORCHESTRATION_PATH_PREFIXES` | const | The path prefixes whose contents instruct or wire an agent, frozen. |
|
|
126
128
|
| `ORKESTREL_RANGE_PATTERN` | const | The exact caret-pinned pre-1.0 range accepted for an `@orkestrel/*` runtime dependency. |
|
|
129
|
+
| `PRINT_WIDTH` | const | Columns one emitted line may occupy, matching `printWidth` in `.oxfmtrc.json`. |
|
|
127
130
|
| `SERVICE_SCRIPT_PATH` | const | The provisioner skeleton a workspace with declared service vendors is given once. |
|
|
128
131
|
| `SERVICE_SETUP_PATH` | const | The live-service readiness module whose presence makes a workspace `service`. |
|
|
129
132
|
| `SERVICE_TEST_INCLUDE` | const | The include the live-service project covers, which is a directory rather than one proof. |
|
|
@@ -131,7 +134,9 @@ Exported from `@orkestrel/scaffold`, and reachable from
|
|
|
131
134
|
| `SHOWCASE_DEV_DEPENDENCIES` | const | The development dependency used only by the optional single-file showcase build. |
|
|
132
135
|
| `SOURCE_BROWSER_DEV_DEPENDENCIES` | const | The development dependencies a published browser `src` environment adds. |
|
|
133
136
|
| `SRC_MATRIX` | const | The build and export settings each published `src` environment contributes, frozen. |
|
|
137
|
+
| `TAB_WIDTH` | const | Columns one tab occupies when the formatter measures a line, matching `tabWidth`. |
|
|
134
138
|
| `VERSION_PATTERN` | const | The exact three-component version syntax a blueprint declares. |
|
|
139
|
+
| `WORKSPACE_OWNED_PATHS` | const | The vendored paths whose present bytes belong to each workspace, frozen. |
|
|
135
140
|
|
|
136
141
|
#### Guards
|
|
137
142
|
|
|
@@ -145,8 +150,8 @@ Exported from `@orkestrel/scaffold`, and reachable from
|
|
|
145
150
|
| `isCompilerHooks` | const | Narrow a value to the compiler's initial listener record. |
|
|
146
151
|
| `isCompilerOptions` | const | Narrow a value to `CompilerOptions`. |
|
|
147
152
|
| `isContent` | const | Narrow a value to text this package will accept as one artifact's content. |
|
|
148
|
-
| `isDependencyName` | const | Narrow a value to the scoped package name a runtime dependency carries. |
|
|
149
153
|
| `isDependency` | const | Narrow a value to a `Dependency`. |
|
|
154
|
+
| `isDependencyName` | const | Narrow a value to the scoped package name a runtime dependency carries. |
|
|
150
155
|
| `isEnvironment` | const | Narrow a value to one `Environment` a workspace may select. |
|
|
151
156
|
| `isFinding` | const | Narrow a value to a `Finding`. |
|
|
152
157
|
| `isGroup` | const | Narrow a value to one `Group` a plan selects over. |
|
|
@@ -189,9 +194,10 @@ Exported from `@orkestrel/scaffold`, and reachable from
|
|
|
189
194
|
| `matchesDriftReachability` | function | Test whether `inferDrift` could have produced a finding for an ownership. |
|
|
190
195
|
| `matchesEngines` | function | Test whether a declared engines floor is at or above the supported minimum. |
|
|
191
196
|
| `matchesOrchestrationPath` | function | Test whether a path instructs or wires an agent rather than the toolchain. |
|
|
197
|
+
| `matchesPrintWidth` | function | Test whether one emitted line fits the vendored formatter width. |
|
|
192
198
|
| `matchesRange` | function | Test whether a declared range already admits a published version. |
|
|
193
199
|
| `nameToGuide` | function | Derive the guide mirror path a package name answers for. |
|
|
194
|
-
| `nameToRewrite` | function | Derive the declaration rewrite a published face
|
|
200
|
+
| `nameToRewrite` | function | Derive the declaration rewrite a published face's `beforeWriteFile` applies. |
|
|
195
201
|
| `planToSummary` | function | Project a plan into its tally by artifact origin. |
|
|
196
202
|
| `selectGroups` | function | Select the groups a compile covers, in plan order. |
|
|
197
203
|
| `selectHostPaths` | function | Select the host paths a named workspace vendors. |
|
|
@@ -458,9 +464,10 @@ An option a verb does not list is refused by name rather than parsed and ignored
|
|
|
458
464
|
the registry, so `new` fails when the registry names no release for a package it was given: the
|
|
459
465
|
workspace would otherwise declare a dependency that does not resolve.
|
|
460
466
|
|
|
461
|
-
`new --bin` creates the executable entry, its test, and its scoped Vite and TypeScript wrappers.
|
|
462
|
-
|
|
463
|
-
`
|
|
467
|
+
`new --bin` creates the executable entry, its test, and its scoped Vite and TypeScript wrappers. The
|
|
468
|
+
other structural facts do not need creation flags. Add `tests/guides.test.ts` for `guides`,
|
|
469
|
+
`tests/distribution.test.ts` for `distribution`, `tests/integration.test.ts` for `integration`,
|
|
470
|
+
`tests/conformance.test.ts` for `conformance`, `tests/setupService.ts` for `service`,
|
|
464
471
|
`tests/setupGlobal.ts` for `global`, and `configs/app/vite.showcase.config.ts` for `showcase`;
|
|
465
472
|
reading verbs detect each exact-case file and register its fixed machinery. Add `scripts/service.sh`
|
|
466
473
|
for `vendors`. Reading verbs preserve and protect that birth-owned script, but do not infer its
|
|
@@ -471,8 +478,9 @@ vendor list from edited text.
|
|
|
471
478
|
`audit`, `repair`, `catalog`, and `overwrite` derive the blueprint from the target itself. The name
|
|
472
479
|
and the declared `@orkestrel/*` packages come from `package.json`. The two environment axes come
|
|
473
480
|
from the directories the target actually ships, because a directory is the fact and a declaration
|
|
474
|
-
beside it could disagree.
|
|
475
|
-
`bin`, `tests/
|
|
481
|
+
beside it could disagree. Eight more facts come from exact-case files: `src/bin/main.ts` selects
|
|
482
|
+
`bin`, `tests/guides.test.ts` selects `guides`, `tests/distribution.test.ts` selects `distribution`,
|
|
483
|
+
`tests/integration.test.ts` selects `integration`, `tests/conformance.test.ts` selects
|
|
476
484
|
`conformance`, `tests/setupService.ts` selects `service`, `tests/setupGlobal.ts` selects `global`,
|
|
477
485
|
and `configs/app/vite.showcase.config.ts` selects `showcase`. A containing directory does not select
|
|
478
486
|
the fact by itself.
|
|
@@ -488,27 +496,43 @@ cannot recover would leave every such workspace unplannable. `tests/setupService
|
|
|
488
496
|
is the module the root configuration names by path, and is what a live proof needs anyway, so it
|
|
489
497
|
carries the fact and the vendor list keeps its own separate job.
|
|
490
498
|
|
|
491
|
-
The root Vite configuration
|
|
492
|
-
|
|
493
|
-
directory or a case-folded spelling does not select it. A
|
|
494
|
-
guides project. A
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
The three plan-reading verbs compare
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
499
|
+
The root Vite configuration defines and registers the fixed `guides` project only when the derived
|
|
500
|
+
blueprint carries `guides`. Reading verbs set that fact only when `tests/guides.test.ts` is a
|
|
501
|
+
physical file with that exact path case. A directory or a case-folded spelling does not select it. A
|
|
502
|
+
fresh workspace therefore carries no guides project or script. A developer who adds the proof must
|
|
503
|
+
also add the exact `test:guides` script line that the plan reports; the manifest remains
|
|
504
|
+
birth-owned.
|
|
505
|
+
|
|
506
|
+
The three plan-reading verbs compare the Vitest project set named by the target manifest with the
|
|
507
|
+
project set the planned root configuration registers. Every planned proof project must also be
|
|
508
|
+
reachable from the manifest's `test` chain. A target whose manifest does not set `private: true` may
|
|
509
|
+
also reach it from `prepublishOnly`. A private target cannot use that chain, because npm refuses the
|
|
510
|
+
package before a publish lifecycle script runs, so crediting it would report a dead gate as a live
|
|
511
|
+
one. Generated integration runs from `test`. One shell-token pass reads quoted and unquoted
|
|
512
|
+
`--project value` and `--project=value` forms and follows literal `npm run` calls. A shell expansion
|
|
513
|
+
or malformed quote that prevents a project or script name from being resolved statically produces a
|
|
514
|
+
question instead of licensing a write. The classifier is deliberately bounded to manifest script
|
|
515
|
+
text that names `vitest`; an external wrapper whose name does not identify its runner supplies no
|
|
516
|
+
static Vitest fact to infer.
|
|
504
517
|
|
|
505
518
|
`audit` still completes the comparison and reports one non-blocking `projects` question. For a
|
|
506
519
|
literal absent project, its advisory tells the developer to register the project or remove the
|
|
507
|
-
script.
|
|
520
|
+
script. For a planned project absent from both gate chains, the advisory gives the exact direct
|
|
521
|
+
script line to add to `package.json`. `repair` and `overwrite` refuse either mismatch and do not
|
|
522
|
+
write the manifest or configuration. Their absent-project refusal tells the developer to remove the
|
|
508
523
|
script or not use scaffold writing verbs for a workspace that needs custom Vitest projects. It does
|
|
509
524
|
not recommend editing the content-owned configuration that the refusing verb would restore. An
|
|
510
525
|
advisory alone does not make an aligned target drift.
|
|
511
526
|
|
|
527
|
+
The same three plan-reading verbs compare the tooling set the derived blueprint plans against
|
|
528
|
+
`dependencies` and `devDependencies` together. A missing planned package produces one non-blocking
|
|
529
|
+
`dependencies` question naming every missing package and the exact manifest lines to add, in stable
|
|
530
|
+
order. The comparison measures membership only: range differences and workspace-owned extras are
|
|
531
|
+
outside it, and a planned tool may live in either section. A present section that is not an object
|
|
532
|
+
produces a question instead of a crash. `audit` reports the question without changing its exit
|
|
533
|
+
semantics. `repair` and `overwrite` refuse before writing configuration, and no verb edits the
|
|
534
|
+
birth-owned `package.json`.
|
|
535
|
+
|
|
512
536
|
### Exit codes
|
|
513
537
|
|
|
514
538
|
`0` means the target matched its plan and every step completed. `1` means the target drifted or a
|
|
@@ -572,22 +596,50 @@ because the shape is chosen once and read afterwards: `new` refuses the advisory
|
|
|
572
596
|
`repair` need the plan to describe and restore a target that already has that shape. A library
|
|
573
597
|
caller creating a workspace holds the same refusal, and the Compile section below states it.
|
|
574
598
|
|
|
575
|
-
`bin`, `integration`, `conformance`, `service`, `vendors`, `global`, and
|
|
576
|
-
facts. Each is set only when the workspace physically ships the directory
|
|
577
|
-
defines it, never because of the workspace's name and never because a
|
|
599
|
+
`bin`, `guides`, `distribution`, `integration`, `conformance`, `service`, `vendors`, `global`, and
|
|
600
|
+
`showcase` are structural facts. Each is set only when the workspace physically ships the directory
|
|
601
|
+
or exact-case file that defines it, never because of the workspace's name and never because a
|
|
602
|
+
sibling fact is set.
|
|
603
|
+
|
|
604
|
+
A structural fact is read when a verb runs, not when the file appears. Writing
|
|
605
|
+
`tests/integration.test.ts` into a workspace sets the fact, but the root configuration on disk was
|
|
606
|
+
generated before that file existed and still registers no `integration` project, so `test:config`
|
|
607
|
+
fails with `integration has no project factory or configuration` until a plan-writing verb
|
|
608
|
+
regenerates it.
|
|
609
|
+
|
|
610
|
+
`repair` alone does not close that, and refusing is correct rather than a gap. `package.json` is
|
|
611
|
+
birth-owned, so the verb cannot add the project's script, and it will not register a project the
|
|
612
|
+
manifest reaches from no gate. It exits 1 naming the target and writes nothing.
|
|
613
|
+
|
|
614
|
+
Adding a structural proof is therefore three steps, in order: write the file; declare its
|
|
615
|
+
`test:<project>` script and invoke that script from a gate chain; then run `repair`, which
|
|
616
|
+
regenerates the root configuration and registers the project. `audit` reports whichever piece is
|
|
617
|
+
still outstanding at each step.
|
|
618
|
+
|
|
619
|
+
`distribution` projects only when the workspace also publishes at least one `src` environment. It
|
|
620
|
+
packs and installs the published artifact, so without that axis there is nothing to pack, and the
|
|
621
|
+
declared flag alone adds no project, no `test:distribution` script, and no gate entry.
|
|
578
622
|
|
|
579
623
|
`service` says the workspace runs a live-service Vitest project over `tests/service`, and it alone
|
|
580
624
|
registers that project, its `test:service` script, and the `tests/setupService.ts` readiness module
|
|
581
|
-
the project names.
|
|
582
|
-
`
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
625
|
+
the project names. A publishing workspace invokes it from `prepublishOnly`; a `private: true`
|
|
626
|
+
workspace invokes it from `test`, which is the only gate it has. Its longer timeouts and disabled
|
|
627
|
+
file parallelism are the same in both. `vendors` names each external service the workspace drives
|
|
628
|
+
and emits `scripts/service.sh`, the provisioner that starts them. Neither is derivable from the
|
|
629
|
+
other: a workspace may declare vendors before it writes a suite, and a suite may drive a service the
|
|
630
|
+
skeleton does not start.
|
|
631
|
+
|
|
632
|
+
`integration` projects a cross-environment composition proof for any workspace, independently of
|
|
633
|
+
whether it has a published `src`. Its generated seed imports every selected `src` and `app`
|
|
634
|
+
environment through its public barrel. It starts no process and does not pack or install the
|
|
635
|
+
workspace. The proof composes across environments, so when `src` and `app` together declare fewer
|
|
636
|
+
than two the gate reports a non-blocking `integration` question: the project, the script, and the
|
|
637
|
+
`test` entry are all still registered, and the advisory reports that the seed composes nothing
|
|
638
|
+
rather than withholding it.
|
|
639
|
+
|
|
640
|
+
`showcase` projects only when the browser `app` environment exists. Without that axis the flag adds
|
|
641
|
+
no artifact, configuration, script, or dependency, and the gate reports a non-blocking question on
|
|
642
|
+
that field so the caller who set it learns it emitted nothing.
|
|
591
643
|
|
|
592
644
|
`createBlueprint` enforces shape only. Whether the name is a name, the version a version, and the
|
|
593
645
|
axis combination one this package can generate are the gate's laws, and the gate answers them with
|
|
@@ -688,15 +740,21 @@ content is produced. `Ownership` says what scaffold claims at the path.
|
|
|
688
740
|
| `presence` | Existence only | Restore an absent file, never touch present bytes |
|
|
689
741
|
| `birth` | Nothing | Create the file only while it is absent |
|
|
690
742
|
|
|
743
|
+
Presence ownership has two separate mechanisms, and a reader needs to know which applies:
|
|
744
|
+
|
|
745
|
+
| Mechanism | Paths | Bytes belong to | Cost |
|
|
746
|
+
| --------------- | ------------------------------------------------- | --------------------- | ------------------------------------------------------- |
|
|
747
|
+
| Verb-owned | `CATALOG_AGENT_PATH` and dependency guide mirrors | `catalog` or `mirror` | The owning verb is the only route for a later update. |
|
|
748
|
+
| Workspace-owned | `WORKSPACE_OWNED_PATHS`, currently `.gitignore` | The target workspace | Present bytes receive no later canonical ignore update. |
|
|
749
|
+
|
|
691
750
|
Birth ownership is what makes a generated workspace the consumer's. `package.json`, the source
|
|
692
751
|
barrels, the tests, `README.md`, and `guides/README.md` are written once and are never rewritten by
|
|
693
752
|
a later verb.
|
|
694
753
|
|
|
695
754
|
Content ownership does not preserve an arbitrary custom Vitest project. Fixed optional proofs are
|
|
696
|
-
selected by their defining paths, as `guides`, `
|
|
697
|
-
needs other local configuration
|
|
698
|
-
|
|
699
|
-
project set.
|
|
755
|
+
selected by their defining paths, as `guides`, `distribution`, `integration`, `conformance`, and
|
|
756
|
+
`service` are. A workspace that needs other local configuration must keep those edits outside a
|
|
757
|
+
content-owned file; `repair` restores that file to the canonical project set.
|
|
700
758
|
|
|
701
759
|
An audit reports one `Finding` per planned path, followed by any foreign path beneath the groups
|
|
702
760
|
the plan covers. Every planned finding carries its artifact's `ownership`. A foreign finding has
|
|
@@ -840,7 +898,10 @@ except the manifest.
|
|
|
840
898
|
selection declares the `playwright` this module imports.
|
|
841
899
|
- One template artifact per source and test file the selection needs: an `index.ts` barrel per
|
|
842
900
|
selected environment, `main.ts` and `index.html` for an application browser, `tests/setup.ts`
|
|
843
|
-
plus the host setup modules the selection reaches, and one entry test per axis project.
|
|
901
|
+
plus the host setup modules the selection reaches, and one entry test per axis project. An
|
|
902
|
+
integration selection also emits a birth-owned `tests/integration.test.ts` seed that imports each
|
|
903
|
+
selected public barrel and records its initial empty exports for the consumer to replace with an
|
|
904
|
+
observable cross-environment flow.
|
|
844
905
|
- One template artifact each for `README.md` and `guides/README.md`.
|
|
845
906
|
- One host artifact per vendored path the workspace selects. A vendored directory is one planned
|
|
846
907
|
path that expands into the files the data root stores beneath it.
|
|
@@ -998,7 +1059,7 @@ proves that every fence imports only real exports of the two barrels, and that e
|
|
|
998
1059
|
in this file resolves to one. It neither runs a fence nor typechecks one, so a trailing `// value`
|
|
999
1060
|
comment inside a fence states what this guide claims rather than what the build answered. The
|
|
1000
1061
|
verdicts that are measured are the ones a consumer hovers:
|
|
1001
|
-
[`tests/
|
|
1062
|
+
[`tests/distribution.test.ts`](../tests/distribution.test.ts) drives every `@example` the built
|
|
1002
1063
|
declarations print against the installed package, scores each verdict it can read as a value, and
|
|
1003
1064
|
names exactly the ones it cannot. Fences are not added to that instrument, because most of them
|
|
1004
1065
|
cannot be run: several declare an ambient value that has no runtime, and several write to a
|
|
@@ -1043,29 +1104,43 @@ nothing. This is deliberate: a generated sample entity is repeatedly mistaken fo
|
|
|
1043
1104
|
implementation. What a consumer does first is write the module's `types.ts`, then the
|
|
1044
1105
|
implementation that conforms to it, then export both from the barrel — the order `AGENTS.md` fixes.
|
|
1045
1106
|
|
|
1046
|
-
**A conformance or live-service proof is registered, but
|
|
1047
|
-
registers
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
`
|
|
1107
|
+
**A selected distribution, conformance, or live-service proof is registered, but none is written for
|
|
1108
|
+
you.** Scaffold registers `conformance` and `service` when their structural facts are set, and
|
|
1109
|
+
registers `distribution` only when the workspace also publishes `src`. In a publishing workspace,
|
|
1110
|
+
`distribution` and `service` run from `prepublishOnly` and `conformance` stays in `test`. In a
|
|
1111
|
+
`private: true` workspace, `distribution` is absent, `service` runs from `test`, and there is no
|
|
1112
|
+
`prepublishOnly` at all. Scaffold emits no proof into any registered project, because each names
|
|
1113
|
+
something only the package knows: the behavior its own packed artifact must hold once installed, the
|
|
1114
|
+
official artifact a conformance check measures against, and the service a live proof drives. A
|
|
1115
|
+
generated placeholder would read as a proof while measuring nothing, so the file a consumer writes
|
|
1116
|
+
is the file that selects the project.
|
|
1117
|
+
|
|
1118
|
+
A distribution proof carries one contract scaffold does enforce from the outside. The generated
|
|
1119
|
+
`prepublishOnly` invokes it as `npm run test:distribution -- --mode release`, and a proof that reads
|
|
1120
|
+
`import.meta.env.MODE === 'release'` must **fail** on an unreachable registry rather than skip. An
|
|
1121
|
+
ordinary local run may skip that case, because a developer offline is not a defect; a release run
|
|
1122
|
+
may not, because skipping there passes the publish gate without ever proving the artifact installs.
|
|
1123
|
+
Scaffold writes no proof, so honouring the flag is the consumer's, and a proof that ignores it
|
|
1124
|
+
reports green on exactly the runs that matter.
|
|
1125
|
+
|
|
1126
|
+
The consequence is one empty-project case per registered proof. A publishing blueprint carrying
|
|
1127
|
+
`distribution` with no `tests/distribution.test.ts`, or any blueprint carrying `conformance` with no
|
|
1128
|
+
`tests/conformance.test.ts`, registers a project whose include resolves to nothing, and Vitest exits
|
|
1129
|
+
non-zero on it. A blueprint carrying `service` gets `tests/setupService.ts` — the root configuration
|
|
1130
|
+
names that module by path, so an absent one fails the project's load rather than its run — and still
|
|
1131
|
+
no suite beneath `tests/service`, so `test:service` reports no test files until the consumer writes
|
|
1132
|
+
the first one. Every case is visible the first time the script runs, which is why none is silent.
|
|
1133
|
+
|
|
1134
|
+
None of the three folds into `integration`, which measures a different axis rather than a smaller
|
|
1135
|
+
one: the workspace's selected environments compose through their public barrels. The generated seed
|
|
1136
|
+
proves only that those barrels load together and expose the initial empty surfaces; the consumer
|
|
1137
|
+
replaces it with an observable cross-environment flow. The seed starts no process and does not pack
|
|
1138
|
+
or install the workspace, so the project stays in `test`. Two fleet packages hold the distinction.
|
|
1139
|
+
`@orkestrel/ollama` drives a real Ollama daemon through a `service` project, so a real service
|
|
1140
|
+
answers it and it runs from `prepublishOnly`. `@orkestrel/mcp` measures its server against the
|
|
1141
|
+
specification's own runner, `@modelcontextprotocol/conformance`, through a `conformance` project. It pins that runner as a development dependency and resolves it out
|
|
1142
|
+
of `node_modules`, and the server the runner drives is one the fixture starts itself on a loopback
|
|
1143
|
+
port, so the run drives nothing external and stays in `test`.
|
|
1069
1144
|
|
|
1070
1145
|
## Tests
|
|
1071
1146
|
|