@orkestrel/scaffold 0.0.20 → 0.0.21

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.
@@ -163,10 +163,12 @@ absorbed.
163
163
  patches serially; route cross-cutting findings.
164
164
  5. **Audit adversarially.** Every nontrivial implementation gets `reviewer` (Opus 5, design
165
165
  fit) and `analyst` (Sol, correctness and constraints) independently, plus `checker` for
166
- mechanical conformance. In a fix round the unit's auditor is an engine that did not write
167
- it. Multi-round audits use one fixed verdict shape with a single terminal line, defined by
168
- the campaign skill. Reconcile their evidence; a finding neither engine can substantiate
169
- against the evidence is dropped on the record.
166
+ mechanical conformance. An audit brief states its subject as numbered falsifiable claims
167
+ and requires per-claim CONFIRMED/BROKEN with evidence, per the Falsification law in
168
+ `.claude/rules/quality.md`. In a fix round the unit's auditor is an engine that did not
169
+ write it. Multi-round audits use one fixed verdict shape with a single terminal line,
170
+ defined by the campaign skill. Reconcile their evidence; a finding neither engine can
171
+ substantiate against the evidence is dropped on the record.
170
172
  6. **Verify.** One independent `verifier` runs the authoritative gates.
171
173
  7. **Accept.** The Orchestrator decides and reports concise outcomes, decisions, evidence, and
172
174
  remaining risk.
@@ -198,13 +200,34 @@ Workflow failures use the same ladder; do not absorb their raw logs into the mai
198
200
  writing nodes — never two concurrent writers in the tree.
199
201
  - Every node names a role and its engine.
200
202
 
203
+ Every dispatch is a file before it is a launch:
204
+
205
+ - The brief is written to a file under `tmp/`, named for its unit, before the unit is
206
+ launched, whatever engine executes it. A brief composed only inside a launch argument
207
+ cannot be corrected, resumed, or re-run once that call ends.
208
+ - The unit's returned report is captured to a file beside its brief under the same unit name,
209
+ so a unit's instruction and its outcome are one pair on disk.
210
+ - A re-run amends its brief instead of restating it: a mid-campaign correction produces a
211
+ successor file recording what changed and why, and the original stays. A fix round's brief
212
+ names the findings it carries and where each came from.
213
+ - Brief and report files are unit evidence, not deliverables. They are never committed, and
214
+ they are swept when the campaign that produced them is accepted.
215
+ - Anything in a brief or a report that must outlive the campaign is promoted into a durable
216
+ artifact — a commit message, a guide, a rule, a retrospective — before the sweep. What is
217
+ only in a swept file did not survive.
218
+
201
219
  Every dispatch contains:
202
220
 
203
221
  - **Role/engine** — named role and explicit engine.
204
222
  - **Objective** — one concrete outcome.
205
223
  - **Context** — the evidence slice, paths, decisions, `AGENTS.md`, applicable rules, the
206
224
  skill name and required references (or explicit none), and the guide/spec.
225
+ - **Unknowns** — what the Orchestrator does not yet know that the unit needs, named as
226
+ unknown, with how the unit reports back on it. A brief that cannot be fully specified says
227
+ so instead of shipping a guess the executor would have to invent an answer around.
207
228
  - **Scope** — owned files, shared and off-limits files, allowed tools, permission limits.
229
+ - **Execution** — the executor performs the assignment directly and spawns nothing. Every
230
+ brief states it; an executor deep in a task does not re-read this contract.
208
231
  - **Output** — the exact distilled return shape; no process diary.
209
232
  - **Deviation contract** — required stop/report behaviour for writers.
210
233
  - **Acceptance criteria** — independently checkable completion conditions.
@@ -230,22 +253,20 @@ Four bench laws apply to every external engine:
230
253
  transport where one exists. Long-running work — audits, implementation units, anything
231
254
  multi-minute — uses the journaled CLI and never MCP: an interrupted MCP call loses its
232
255
  session invisibly, while a journal survives any client-side failure.
233
- - **Journal first.** Every bench invocation leaves a tailable on-disk record under
234
- `tmp/<bench>/` (`tmp/codex/`, `tmp/cursor/`): the brief as a file, the event stream or
235
- output log, and the final answer. Every long exec also carries exactly one Monitor on its
236
- journal — a filtered tail that emits milestones (commands run, files changed, agent
237
- messages, terminal states) and never the raw event firehose — so progress arrives in the
238
- conversation while the journal stays tailable for depth. The filter exits on the exec's
239
- terminal event, so the monitor's lifecycle matches the exec's and no watcher outlives its
240
- subject. The journal's mtime is the liveness signal; the session id in the journal head is
241
- the recovery handle. Briefs never travel as fragile shell arguments.
256
+ - **Journal first.** Every bench invocation leaves a tailable on-disk record beside its brief
257
+ under `tmp/<bench>/` (`tmp/codex/`, `tmp/cursor/`): the event stream or output log and the
258
+ final answer. Every long exec also carries exactly one Monitor on its journal — a filtered
259
+ tail that emits milestones (commands run, files changed, agent messages, terminal states)
260
+ and never the raw event firehose — so progress arrives in the conversation while the
261
+ journal stays tailable for depth. The filter exits on the exec's terminal event, so the
262
+ monitor's lifecycle matches the exec's and no watcher outlives its subject. The journal's
263
+ mtime is the liveness signal; the session id in the journal head is the recovery handle.
242
264
  - **Tracked, never loose.** Every bench unit is registered in the session task registry at
243
265
  launch — subject, journal path, session id — and completed there at acceptance, so "what is
244
266
  running" always has a first-class answer instead of a recollection of a command.
245
- - **Ephemeral journals.** Everything under `tmp/` is unit evidence, never committed. Bridges
246
- never delete journals; the Orchestrator sweeps `tmp/codex/` and `tmp/cursor/` once at
247
- campaign acceptance, after the final gate evidence is recorded. A journal surviving past
248
- its campaign is residue.
267
+ - **Ephemeral journals.** Bridges never delete journals; the Orchestrator sweeps them with
268
+ the campaign's other unit evidence at acceptance, after the final gate evidence is
269
+ recorded. A journal surviving past its campaign is residue.
249
270
 
250
271
  Every long bench exec is launched by the Orchestrator as a harness-tracked background command
251
272
  under a hard time cap, never detached from inside a bridge agent: the harness owns the
@@ -261,12 +282,12 @@ unit.
261
282
  ### Cursor Grok
262
283
 
263
284
  - Reached only through the `grok` role, in ask mode:
264
- `<agent-cli> -p --trust --mode=ask --model "$CURSOR_GROK_MODEL" "<brief>" | tee tmp/cursor/<unit>.log`.
285
+ `<agent-cli> -p --trust --mode=ask --model "$CURSOR_GROK_MODEL" "<prompt>" | tee tmp/cursor/<unit>.log`.
265
286
  `<agent-cli>` resolves as bare `agent`, then `agent.cmd` (Windows installs ship only
266
287
  `.cmd`/`.ps1` shims, so bare `agent` does not resolve in Bash), then
267
- `"$LOCALAPPDATA/cursor-agent/agent.cmd"` — verified with `--version` before first use. Long
268
- briefs are written to `tmp/cursor/<unit>-brief.md` and the prompt points at the file. The
269
- tee'd log is the bench's journal.
288
+ `"$LOCALAPPDATA/cursor-agent/agent.cmd"` — verified with `--version` before first use. The
289
+ prompt points at the unit's brief file, `tmp/cursor/<unit>-brief.md`. The tee'd log is the
290
+ bench's journal.
270
291
  - A long ask-mode run obeys the same launch, stream, and ledger discipline as a Codex exec:
271
292
  the Orchestrator starts it as a harness-tracked background command under a time cap,
272
293
  registers the unit in the task registry, and arms one Monitor on the tee'd log for
@@ -1,7 +1,7 @@
1
1
  # Field testing an agent-facing surface
2
2
 
3
- The method for producing the live evidence a debrief judges by. Proven across the
4
- supervisor MCP campaign's five-model roster; portable to any surface a model consumes.
3
+ The method for producing the live evidence a debrief judges by. Proven across a five-model
4
+ roster and portable to any surface a model consumes.
5
5
 
6
6
  ## The tier ladder
7
7
 
@@ -63,6 +63,18 @@ Findings land as one of:
63
63
  probe-first, journal-first, verdict shapes, launch ownership) land in the operating
64
64
  contract, mirrored across providers.
65
65
 
66
+ ## Three questions every round asks
67
+
68
+ - **Which findings came from falsification rather than diff reading**, and is the brief
69
+ shape that produced them doctrine yet? Route to the Falsification law in
70
+ `.claude/rules/quality.md`.
71
+ - **Which shipped gaps were accepted as untestable**, and was each one genuinely
72
+ irreducible or a missing seam? Route to the missing-seam law in
73
+ `.claude/rules/tests.md`.
74
+ - **Which units could not be re-run from their own recorded brief**, and why? A unit whose
75
+ instruction never became a file, or whose correction left its brief behind, is a defect in
76
+ the dispatch. Route to the brief-and-report laws in the operating contract.
77
+
66
78
  ## Mirror discipline
67
79
 
68
80
  Every roster or contract change lands on all provider surfaces in the same round: the
@@ -77,6 +77,13 @@ argument, diagnosis, correctness/security audit, and constraint review. Capture
77
77
  repository status before and after. Require evidence for every claim and return
78
78
  unsupported claims as dropped.
79
79
 
80
+ An audit brief states its subject as a numbered list of falsifiable claims rather
81
+ than a diff to read, and requires Sol to attempt refutation: each claim comes back
82
+ CONFIRMED with the evidence that convinced it, or BROKEN with the exact failing
83
+ input, state, or interleaving. The Falsification section of
84
+ `.claude/rules/quality.md` owns that method — point the brief at it instead of
85
+ restating it.
86
+
80
87
  ## Implementer
81
88
 
82
89
  Sandbox `workspace-write`, main checkout, sole serial writer from a clean
@@ -44,11 +44,11 @@ descriptions never enter agent instruction context.
44
44
  | @orkestrel/database | 0.0.7 |
45
45
  | @orkestrel/emitter | 0.0.5 |
46
46
  | @orkestrel/guide | 0.0.8 |
47
- | @orkestrel/html | 0.0.1 |
47
+ | @orkestrel/html | 0.0.2 |
48
48
  | @orkestrel/indexeddb | 0.0.6 |
49
49
  | @orkestrel/interpret | 0.0.7 |
50
50
  | @orkestrel/markdown | 0.0.7 |
51
- | @orkestrel/mcp | 0.0.11 |
51
+ | @orkestrel/mcp | 0.0.12 |
52
52
  | @orkestrel/middleware | 0.0.9 |
53
53
  | @orkestrel/msg | 0.0.5 |
54
54
  | @orkestrel/ndjson | 0.0.5 |
@@ -61,7 +61,7 @@ descriptions never enter agent instruction context.
61
61
  | @orkestrel/reason | 0.0.4 |
62
62
  | @orkestrel/relation | 0.0.7 |
63
63
  | @orkestrel/router | 0.0.8 |
64
- | @orkestrel/scaffold | 0.0.18 |
64
+ | @orkestrel/scaffold | 0.0.20 |
65
65
  | @orkestrel/sea | 0.0.5 |
66
66
  | @orkestrel/server | 0.0.10 |
67
67
  | @orkestrel/sqlite | 0.0.6 |
@@ -33,6 +33,11 @@ Audit the changed work only through Opus 5's subjective and creative lens:
33
33
  5. **Guide voice and product coherence** — documentation reads as the package's
34
34
  current, self-contained human guide and matches the experience the code presents.
35
35
 
36
+ Test a design claim by asking whether the shipped artifact still matches it — a
37
+ guide, charter, or name that described the work two revisions ago is drift, and
38
+ that question is what finds it. Anything you cannot settle on subjective grounds
39
+ becomes an Analyst referral rather than a verdict of yours.
40
+
36
41
  For a rendered or externally driven surface, the supplied capture portfolio is the
37
42
  primary evidence and source is corroboration only: cite a capture for every rendered
38
43
  claim, mark what the portfolio cannot show as NOT-EVIDENCED instead of inferring it,
@@ -21,6 +21,16 @@ paths:
21
21
  - Treat existing code, tests, `old/`, branches, and copied projects as evidence rather than authority.
22
22
  - Build a capability/defect matrix before a broad API or production-readiness change. Every row ends as implement, repair, retain, or intentionally exclude with evidence.
23
23
 
24
+ ## Falsification
25
+
26
+ A review that reads a diff finds what the diff shows; a review that tries to break named claims finds what the diff hides. Code that has already passed diff review several times can still carry a defect nobody has yet tried to trigger.
27
+
28
+ - State an audit's subject as a numbered list of the claims the work makes, never as “review this diff”. Each claim is falsifiable: a property some concrete input, state, or interleaving could show false.
29
+ - Instruct the auditor to attempt refutation rather than confirmation. A claim it cannot break is reported CONFIRMED with the evidence that convinced it; a claim it breaks is reported BROKEN with the exact failing input, state, or interleaving, plus the smallest correct fix.
30
+ - Derive claims from what the change asserts under adverse conditions: cancellation, restart, concurrency, partial failure, hostile input, resource exhaustion, and the orderings a happy path never reaches.
31
+ - Read the installed declaration or implementation of every substrate a claim depends on. A claim about `stop()` is unfalsifiable until you know what `stop()` does when the status is not the one the caller assumed.
32
+ - An audit returning only confirmations has either audited trivial claims or has not tried. Name the claims you could not break, so the next round knows what has already been attacked.
33
+
24
34
  ## Ecosystem reuse
25
35
 
26
36
  The root laws on inspecting declared `@orkestrel/*` capabilities, reusing a matching primitive, and updating every consumer without shims bind here without restatement. They leave this file the judgment calls:
@@ -111,6 +111,16 @@ Keep Vitest/provider configuration minimal:
111
111
  - Remove exploratory settings after fixing the cause.
112
112
  - Config comments explain the current reason, not the history of failed experiments.
113
113
 
114
+ ## Untestable usually means missing seam
115
+
116
+ Before accepting that a behavior cannot be tested, look for the seam that would make it testable.
117
+
118
+ - A collaborator reached through a hard-coded global — a stream, a clock source, a spawn, a fetch — is a missing injection point, not an untestable truth. An injected collaborator with a real minimal implementation is a sanctioned boundary stub, not a mock of project-owned behavior.
119
+ - Prefer adding the seam over shipping the gap whenever the seam is one the design would welcome anyway, and whenever a sibling collaborator is already injected.
120
+ - When a gap is genuinely irreducible, record it where a reader meets it: what is unproven, why it cannot be driven, and what would change that. A silent untested guard reads exactly like a tested one.
121
+
122
+ Coverage reporting is a discovery input, never evidence of proof. It answers one cheap mechanical question — which code no test even executed — and that question reliably finds forgotten branches and rules nothing calls. It cannot tell you whether an executed line is asserted, so a fully covered file can still be entirely unproven. Read a coverage report to find candidates for the adequacy audit; never cite it as the audit's result, and never let a percentage become the target.
123
+
114
124
  ## Discovery and adequacy audit
115
125
 
116
126
  Before acceptance:
@@ -9,7 +9,11 @@ governing guide/spec, and the exact scope. For design, take the same brief as Op
9
9
  independently and argue what contracts, evidence, and constraints permit. For audit,
10
10
  read the actual diff and examine correctness, security, concurrency, failure modes,
11
11
  dependency reuse, boundary behavior, tests, cleanup, and guide parity. Require
12
- file:line evidence and drop unsupported claims. For a rendered or externally driven
12
+ file:line evidence and drop unsupported claims. When the brief states its subject as
13
+ numbered falsifiable claims, attempt refutation rather than confirmation and return
14
+ each claim CONFIRMED with the evidence that convinced you or BROKEN with the exact
15
+ failing input, state, or interleaving, per the Falsification section of
16
+ .claude/rules/quality.md. For a rendered or externally driven
13
17
  surface, the supplied capture portfolio is primary evidence and source is
14
18
  corroboration; mark a claim the portfolio cannot show NOT-EVIDENCED rather than
15
19
  inferring it from source. Never edit, implement, reconcile, accept, or spawn. Return
@@ -15,7 +15,9 @@ Verify that the `claude` CLI is available before dispatch. If it is absent or th
15
15
  dispatch fails, return the failure immediately so the Sol main session can run the
16
16
  design-audit pass itself and record that Opus was unavailable for this round.
17
17
  Require PASS/FAIL, design-fit blockers with file:line evidence, nonblocking
18
- advisories, and acceptance-criterion confirmations. For a rendered or externally
18
+ advisories, and acceptance-criterion confirmations. Require each design claim to be
19
+ tested by asking whether the shipped artifact still matches it, and anything not
20
+ settleable on subjective grounds to come back as an Analyst referral. For a rendered or externally
19
21
  driven surface, require capture citations as primary evidence and the verdict shape
20
22
  of the dispatch-named skill. Forbid edits, commands, orchestration, reconciliation,
21
23
  and acceptance. Never invoke Fable, read credentials,
@@ -53,10 +53,16 @@ concurrently.
53
53
 
54
54
  Every dispatch includes objective, context, AGENTS.md, applicable rules, governing
55
55
  skill name/path and required references (or explicit none), guide/spec, owned and
56
- off-limits files, permissions, exact output, deviation behavior, and independently
57
- checkable acceptance criteria. Writers stop when reality diverges; they do not re-plan
58
- or debug beyond scope. After reconciling findings into briefs, every retained finding
59
- names the brief item that carries it; a finding with no carrier is a dropped finding.
56
+ off-limits files, permissions, exact output, deviation behavior, declared unknowns
57
+ with how the unit reports them, the requirement that the executor spawns nothing,
58
+ and independently checkable acceptance criteria. Every brief is a file under tmp/
59
+ written before its unit launches, with the unit's report captured beside it; a
60
+ re-run amends the brief in a successor file rather than restating it; both are
61
+ swept at campaign acceptance, so anything that must outlive the campaign is
62
+ promoted into a durable artifact first. Writers stop when reality diverges; they do
63
+ not re-plan or debug beyond scope. After reconciling findings into briefs, every
64
+ retained finding names the brief item that carries it; a finding with no carrier is
65
+ a dropped finding.
60
66
 
61
67
  A claim about a rendered or externally driven surface is evidenced by its capture
62
68
  portfolio or a real foreign client driving it, never by source alone; the campaign
@@ -67,11 +67,41 @@ than aspirational:
67
67
  - `app/browser` reaches server behavior only through shared `app/core` contracts and transports,
68
68
  never through a server implementation import.
69
69
 
70
- A generated `app/server` owns strict `APP_HOST`, `APP_PORT`, and `APP_START_TIMEOUT` parsing, a
71
- repeat-safe HTTP lifecycle, bounded connection behavior, and process signal cleanup. Its exported
70
+ A generated `app/server` owns strict grouped `server.host`, `server.port`, and `server.timeout`
71
+ options plus the `APP_HOST`, `APP_PORT`, and `APP_START_TIMEOUT` environment boundaries. It
72
+ composes the installed router, server, and boundary/security/deadline middleware substrates around
73
+ a fresh `GET /health` dispatcher from `createApplicationDispatcher`, supports repeated start/stop
74
+ cycles and terminal destroy of both the server and its owned dispatcher, and writes exactly one
75
+ `[READY] <name> <url>` diagnostic after process-owned readiness. The process runner owns an emitter
76
+ whose `ApplicationServerRunnerEventMap` publishes `ready(url)` and `fail(error)`; initial
77
+ `ApplicationServerRunnerOptions.on` hooks run before the runner's own announcement and reporting
78
+ listeners; a synchronous fail hook sees an otherwise-unset `process.exitCode` as `undefined` before
79
+ the default reporter sets it to `1`. Concurrent stops join one substrate shutdown. In-process tests park on those events,
80
+ while child-process tests still observe the readiness line across the process boundary. Its exported
72
81
  `reportApplicationServerError` handler writes only a stable configuration, lifecycle, or unknown
73
- failure code; process-owned diagnostics never serialize a rejected value, nested cause, stack, or
74
- other error context.
82
+ failure code; process-owned failures never serialize a rejected value, nested cause, stack,
83
+ secret, or other error context. `ApplicationState` extends middleware's `IdentifierState` and adds
84
+ only the connection fact. `ApplicationServer.url` is `undefined` until a real port is bound and
85
+ again after stop or destroy; the redundant `listening` projection is not part of the generated
86
+ interface. The runner narrows the post-start URL before writing `[READY]`, so it never announces a
87
+ stale or unbound address, and it stops the server as part of failing that narrowing rather than
88
+ leaving a bound listener without a shutdown owner. It also serializes every start and stop on one
89
+ lifecycle queue, so a stop waits for the startup it aborted to settle before closing the server,
90
+ and a restart issued during that shutdown is honoured after it rather than lost.
91
+
92
+ The health contract belongs to whichever layer both hosts can reach. While the server alone reads
93
+ it, `ApplicationRecord`, `APP_HEALTH_METHOD`, and `APP_HEALTH_PATH` stay declared in `app/server`.
94
+ The moment a blueprint declares `app/browser` beside `app/server` — a combination that already
95
+ requires `app/core` — those three declarations move to `app/core` and gain `APP_HEALTH_TIMEOUT`,
96
+ the `isApplicationRecord` guard, and `readApplicationHealth`. That one asynchronous read is the
97
+ whole browser/server boundary: it fetches the running server's health route, reads the body as
98
+ `unknown`, narrows it with the shared guard, and returns the shared `Application` identity or
99
+ `undefined` for an unreachable, slow, or off-contract answer. Nothing is duplicated by the move —
100
+ `app/server` imports the relocated contract from `@app/core`, and `app/browser` still never imports
101
+ a server module. The generated browser entry then mounts `mountBrowserApplication`, which performs
102
+ that single read before mounting and falls back to the locally configured identity when the
103
+ boundary yields `undefined`. A rejected mount reports the context-free
104
+ `[ERROR] Browser application failed`, the browser twin of that server-side discipline.
75
105
 
76
106
  Every environment barrel is an export-star barrel: `index.ts` contains only `export * from './x.js'`
77
107
  rows and nothing else. Named, default, namespace, and type-only barrel rows are absent by design,
@@ -416,7 +446,9 @@ From [`constants.ts`](../../src/core/constants.ts).
416
446
  | `SCAFFOLD_RANGE` | const |
417
447
  | `BASE_DEV_DEPENDENCIES` | const |
418
448
  | `SOURCE_BROWSER_DEV_DEPENDENCIES` | const |
449
+ | `APP_DEV_DEPENDENCIES` | const |
419
450
  | `APP_BROWSER_DEV_DEPENDENCIES` | const |
451
+ | `APP_SERVER_DEV_DEPENDENCIES` | const |
420
452
  | `CHECKOUT_ACTION_SHA` | const |
421
453
  | `SETUP_NODE_ACTION_SHA` | const |
422
454
  | `COMPILER_ID` | const |
@@ -464,8 +496,11 @@ form. `HEX_PATTERN` requires whole lowercase byte pairs, and `SYNC_BASELINE_PATT
464
496
  `MINIMUM_NODE_VERSION` is `22.12.0`, `DEFAULT_ENGINES` derives from it, and `DEFAULT_VERSION` is
465
497
  `0.0.1`. `BASE_DEV_DEPENDENCIES` is the host-neutral tooling baseline every generated workspace
466
498
  gets; `SOURCE_BROWSER_DEV_DEPENDENCIES` adds the real browser providers a published browser environment
467
- needs, and `APP_BROWSER_DEV_DEPENDENCIES` extends that with the Vue toolchain a private browser
468
- application needs. Vite is minor-pinned at `~8.2.0`: the generated boundary consumes the reviewed
499
+ needs; `APP_DEV_DEPENDENCIES` is the baseline every private application environment gets;
500
+ `APP_BROWSER_DEV_DEPENDENCIES` adds the Vue toolchain and `@orkestrel/html` start-tag parser a
501
+ private browser application needs;
502
+ and `APP_SERVER_DEV_DEPENDENCIES` adds the emitter, middleware, router, and server packages a private
503
+ server application needs. Vite is minor-pinned at `~8.2.0`: the generated boundary consumes the reviewed
469
504
  8.2 `CSSOptions`, `preprocessCSS`, and `isCSSRequest` surface, while the selected
470
505
  `css.transformer` / `lightningcss` path is experimental and must not float into an unreviewed minor.
471
506
  `SCAFFOLD_RANGE` is the range generated workspaces pin this package at.
@@ -788,6 +823,8 @@ From [`helpers.ts`](../../src/core/helpers.ts).
788
823
  | `pascalCase` | function |
789
824
  | `escapeHtmlText` | function |
790
825
  | `serializeTypeScriptString` | function |
826
+ | `hasApplicationBoundary` | function |
827
+ | `hasApplicationShowcase` | function |
791
828
  | `blueprintToMembers` | function |
792
829
  | `catalogNames` | function |
793
830
  | `alignTable` | function |
@@ -824,6 +861,7 @@ From [`helpers.ts`](../../src/core/helpers.ts).
824
861
  | `renderArray` | function |
825
862
  | `renderObject` | function |
826
863
  | `renderValue` | function |
864
+ | `renderStringArray` | function |
827
865
  | `formatJson` | function |
828
866
  | `pinPlan` | function |
829
867
 
@@ -836,7 +874,9 @@ guard. `blueprint` fills the defaults: `version` and `engines` from their consta
836
874
  derives the entity name from a lowercase-hyphen package name, and `blueprintToMembers` derives the
837
875
  declared public `Member[]` — a full entity, options type, interface, and factory per published
838
876
  environment, plus the exact declaration inventory each selected application environment
839
- contributes.
877
+ contributes. `hasApplicationBoundary` recognizes exactly app/core + app/browser + app/server,
878
+ while `hasApplicationShowcase` requires showcase intent beside app/browser; plan assembly, tests,
879
+ guides, and member inventory share those predicates.
840
880
 
841
881
  `escapeHtmlText` and `serializeTypeScriptString` are the two escaping leaves used when a
842
882
  caller-supplied name reaches generated HTML or generated TypeScript source; the latter preserves
@@ -888,7 +928,12 @@ id is already registered: an identical plan is idempotent, while a distinct payl
888
928
  `formatJson` and its leaves — `renderValue`,
889
929
  `renderArray`, `renderObject`, `computeColumnWidth`, and `fitsPrintWidth` — emit JSON that matches the fleet
890
930
  formatter byte for byte, collapsing a short array onto one line and breaking a long one, so
891
- computed configuration JSON is format-stable by construction.
931
+ computed configuration JSON is format-stable by construction. `renderStringArray` applies the same
932
+ inline-or-broken width rule to single-quoted TypeScript string-array literals — with a trailing
933
+ comma on every broken line, matching `oxfmt`'s `trailingComma: "all"` for non-JSON files — so
934
+ generated TypeScript configuration is format-stable too. It serializes every string element through
935
+ `serializeTypeScriptString`, so quotes, backslashes, controls, and line separators remain inert in
936
+ both layouts.
892
937
 
893
938
  ### Helpers — server
894
939
 
@@ -1049,6 +1094,7 @@ From [`compilers.ts`](../../src/core/compilers.ts).
1049
1094
  | `renderViteTest` | function |
1050
1095
  | `viteHeader` | function |
1051
1096
  | `policyViteProject` | function |
1097
+ | `configViteProject` | function |
1052
1098
  | `guidesViteProject` | function |
1053
1099
  | `binViteProject` | function |
1054
1100
  | `integrationViteProject` | function |
@@ -1136,7 +1182,7 @@ formatter's 100-column fixed point: a complete registration-array line, includin
1136
1182
  its trailing comma, stays collapsed when it fits and expands one entry per line otherwise.
1137
1183
  `viteProjectRegistrations` is the one registration derivation every root shape consumes: it derives
1138
1184
  the selected source and application projects from the canonical environment order, then appends
1139
- `policy`, `guides`, and the optional `srcBin`, `integration`, and `service` projects.
1185
+ `policy`, `config`, `guides`, and the optional `srcBin`, `integration`, and `service` projects.
1140
1186
  `viteProjectDefinitions` renders the standalone proof and structural-fact definitions in that same
1141
1187
  order with one blank line between declarations. Both consume `ViteFacts`, so each optional project
1142
1188
  is controlled only by its matching `bin`, `integration`, or `service` blueprint fact; the same
@@ -1149,8 +1195,9 @@ the root `srcCore` factory and its wrapper stay host-independent even when the w
1149
1195
  browser target. `binTsconfig` emits the executable declaration scope; `rootViteConfig`,
1150
1196
  `singleSrcViteConfig`, and `applicationViteConfig` emit the root configuration for a library-only,
1151
1197
  single non-core `src` environment, and application-bearing workspace respectively; and
1152
- `policyViteProject`, `guidesViteProject`, `integrationViteProject`, and `serviceViteProject` emit
1153
- the standalone Node proof projects, with `binViteProject` the single executable-project emitter. A
1198
+ `policyViteProject`, `configViteProject`, `guidesViteProject`, `integrationViteProject`, and
1199
+ `serviceViteProject` emit the standalone Node proof projects, with `binViteProject` the single
1200
+ executable-project emitter. A
1154
1201
  proof project is structurally derived from the directory holding its tests and never wraps a source
1155
1202
  or application environment project. The guides project therefore uses only `tests/setup.ts`, never
1156
1203
  `setupServer.ts`, `setupBrowser.ts`, or `setupService.ts`; and its `tests/src/**/*.test.ts` and
@@ -1540,13 +1587,13 @@ a fixed, interleaved order so aggregates sit immediately before their per-enviro
1540
1587
  - `format`, `format:check`, `lint:check`
1541
1588
  - `test`, then `test:src` and its per-environment scopes, the optional `test:integration`,
1542
1589
  `test:equivalence`, and `test:service` proofs, `test:app` and its per-environment scopes, then
1543
- `test:policy` and `test:guides`
1590
+ `test:policy`, `test:config`, and `test:guides`
1544
1591
  - `build`, then `build:src` and its per-environment targets, `build:app` and its runtime targets, and
1545
1592
  `build:host` for a bin workspace
1546
1593
  - `dev` when a browser application is selected; `serve` and `serve:build` when a server application
1547
1594
  is selected
1548
1595
  - `showcase`, `build:showcase`, and `show` only when the physical showcase wrapper is present;
1549
- `show` builds and copies `dist/showcase/index.html` to `demo/showcase.html`
1596
+ `show` formats, then builds, then copies `dist/showcase/index.html` to `demo/showcase.html`
1550
1597
  - `prepublishOnly` chaining `format:check → lint:check → check → build → test`, followed by
1551
1598
  `test:integration` when the integration axis is selected
1552
1599
 
@@ -1560,8 +1607,9 @@ only where `bin` and `integration` are both set:
1560
1607
  | `test:equivalence` | no | no | no |
1561
1608
  | `test:service` | no | never | after `scripts/service.sh` |
1562
1609
 
1563
- No proof joins the default chain: `npm test` runs the source, application, policy, and guide
1564
- projects, and nothing there needs a build artifact or a foreign process. Publication is the one
1610
+ No opt-in proof joins the default chain: `npm test` runs the source, application, policy,
1611
+ configuration, and guide projects, and nothing there needs a build artifact or a foreign process.
1612
+ Publication is the one
1565
1613
  asymmetry — `prepublishOnly` appends `test:integration`, because a package about to be published
1566
1614
  should prove itself against its own built output, while `test:service` is never in that chain.
1567
1615
  Neither default testing nor publication starts or requires a foreign process.
@@ -1633,10 +1681,16 @@ comments, text, raw blocks, attributes, adjacent tokens, casing, and user-author
1633
1681
  byte-stable. The trusted preparation hook owns the final pre-parse phase; inline proxy code is
1634
1682
  restored before module analysis, and the first normal post-parse hook restores the original HTML
1635
1683
  spelling. The browser entry begins with a generated, byte-stable security prologue: the doctype,
1636
- document and head opening, and a `Content-Security-Policy` meta element are one required prefix.
1637
- Preparation rejects a missing, moved, or changed prologue before Vite parses the document, and the
1638
- final trusted post-hook verifies that Vite retained the policy. CRLF and LF files are both accepted;
1639
- the prologue's markup and ordering are otherwise exact. Vite's `%ENV%` HTML substitution is rejected
1684
+ head opening, and `Content-Security-Policy` meta markup, ordering, and indentation are exact. The
1685
+ opening `html` start tag is parsed by `@orkestrel/html`'s fail-closed `parseStartTag` boundary,
1686
+ so ASCII case and well-formed attributes such as `lang`, `data-bs-theme`, and `data-bs-core`
1687
+ may vary without weakening the position of the following head and policy. A malformed, incomplete,
1688
+ duplicate-attribute, wrong-name, or syntactically slashed root still fails closed. Preparation owns
1689
+ that positional check while the document is still generated bytes; the final trusted post-hook
1690
+ checks only that the exact
1691
+ policy survived because Vite may legitimately inject into the head. CRLF and LF files are both
1692
+ accepted. Vite's
1693
+ `%ENV%` HTML substitution is rejected
1640
1694
  before parsing because Vite performs that expansion after every plugin pre-hook, where it could
1641
1695
  otherwise create a late control attribute. The guard walks the exact left-to-right `%(\S+?)%`
1642
1696
  tokens Vite recognizes instead of performing a substring search, and each preparation plugin owns
@@ -1685,15 +1739,31 @@ still rejects public directories, browser asset inlining, and output path overri
1685
1739
  post-factory composition as defense in depth; that narrow check is not a general extension seam.
1686
1740
 
1687
1741
  When the showcase fact is present, the generated root also exports closed
1688
- `appShowcase(...config: never[])`; both factories reject every argument at runtime and
1689
- share one fixed internal browser configuration. The ordinary factory retains its strict
1742
+ `appShowcase(...config: never[])`; both factories reject every argument at runtime. The
1743
+ ordinary factory retains its strict
1690
1744
  `script-src 'self'` policy, external asset auditing, and `dist/app/browser` output. The showcase
1691
- factory alone writes `dist/showcase`, applies `viteSingleFile` with
1745
+ factory is a standalone configuration with `base: './'`, unlimited asset inlining, and
1746
+ `dist/showcase` output. It applies `viteSingleFile` with
1692
1747
  `removeViteModuleLoader: true` and `useRecommendedBuildConfig: true`, uses Oxc and Lightning CSS
1693
- minification for an `esnext` build without source maps or module preload, and inserts a non-empty ISO
1694
- `build-id` meta. Its generated CSP admits only the inline script and style required by the
1695
- self-contained `file://` artifact while retaining `script-src-attr 'none'`, `object-src 'none'`, and
1696
- `base-uri 'none'`.
1748
+ minification for an `esnext` build without source maps or module preload, and inserts a SHA-256
1749
+ `build-id` derived from the secured, fully inlined document. An unchanged document therefore keeps
1750
+ the same id, while any changed byte changes it. The showcase development CSP keeps scripts
1751
+ same-origin and permits Vue's injected inline styles. Its built CSP swaps that script permission to
1752
+ inline and admits only inline styles plus data images and fonts, while both policies retain
1753
+ `default-src 'none'`, `script-src-attr 'none'`, `object-src 'none'`, and `base-uri 'none'`.
1754
+
1755
+ The showcase fact also emits its own entry pair, `app/browser/showcase.html` and
1756
+ `app/browser/showcase.ts`, beside the application's `index.html` and `main.ts`. Both HTML entries
1757
+ open with a generated security prologue: the application carries the ordinary strict policy and the
1758
+ showcase carries its development policy. The boundary plugins select and validate the matching
1759
+ prologue; the showcase build alone swaps in the self-contained policy before hashing and renames its
1760
+ single HTML output to `index.html`, which is what `show` copies to `demo/showcase.html`. The showcase entry
1761
+ mounts `mountShowcaseApplication`, and `app/browser/seeders.ts` exports exactly one seeder,
1762
+ `seedApplication`, returning a frozen identity of the same shape the shipped root view receives.
1763
+ The two mount factories differ in the seed expression alone. Both explicitly pass
1764
+ `{ name: seed.name }` to the same `createBrowserApplication` root: the showcase seed comes from
1765
+ `seedApplication()`, while the shipped application seed comes from `readApplicationHealth` with
1766
+ the configured identity as its fallback.
1697
1767
 
1698
1768
  The browser development server applies the same trust boundary before Vite's internal middleware.
1699
1769
  Its explicit filesystem allowlist contains only browser/core source roots, browser tests, their
@@ -1756,12 +1826,26 @@ it is not a general-purpose source analyzer. Generated workspaces receive the sa
1756
1826
  module as a host-origin file and run it as a dedicated Node-only `policy` test project over
1757
1827
  `tests/policy.test.ts`.
1758
1828
 
1829
+ **The configuration suite.** Policy reads source, the `config` project exercises the root
1830
+ configuration, and integration builds for real. Every generated workspace therefore receives a
1831
+ universal Node-only
1832
+ `config` project over `tests/config/**/*.test.ts`. Its base cases execute the root module's physical
1833
+ workspace containment and environment-direction helpers; conditional cases exercise output
1834
+ containment when the workspace builds, managed/system browser discovery when a browser environment
1835
+ exists, and the HTML/CSP boundary only for an application browser. Those cases import the generated
1836
+ root `vite.config.ts` itself, so a failure is repaired in the generator rather than patched into a
1837
+ consumer. The generated-consumer integration matrix remains the fidelity boundary for real builds;
1838
+ the configuration suite supplies deterministic edge coverage without duplicating build orchestration.
1839
+ When scaffold changes a generated configuration invariant, an existing consumer's `vite.config.ts`
1840
+ is intentionally reported stale until that consumer accepts the regenerated configuration and its
1841
+ matching config test.
1842
+
1759
1843
  **Real browser capability.** Browser test projects are gated on one centralized discovery chain:
1760
1844
  Playwright's pinned Chromium executable first, then a managed Chromium alias or cached revision,
1761
1845
  then stable system Chrome, then stable system Edge. Managed candidates must be executable regular
1762
1846
  files. System channels are selected only when their executable exists at Playwright's standard
1763
1847
  Linux, macOS, or Windows installation location; custom installations are not guessed. The generated
1764
- policy test consumes the same discovery helpers and accepts either an executable managed path or the
1848
+ configuration test consumes the same discovery helpers and accepts either an executable managed path or the
1765
1849
  stable `chrome` / `msedge` channel, so it does not maintain a second heuristic.
1766
1850
 
1767
1851
  A browser suite runs when any one of those real browser capabilities is available and is skipped
@@ -1900,7 +1984,10 @@ renderer behind the table and blockquote work; the template engine behind every
1900
1984
  artifact; and, consumed only at the executable boundary, the terminal prompt toolkit and the console
1901
1985
  reporter. The core face uses the first four and stays pure; the server face adds only `node:*`
1902
1986
  builtins. Development dependencies are the shared tooling baseline plus the guide-parity toolkit
1903
- that drives [`parity.test.ts`](../../tests/guides/src/parity.test.ts). The engines floor is Node
1987
+ that drives [`parity.test.ts`](../../tests/guides/src/parity.test.ts) and `@orkestrel/html`,
1988
+ which this package's real emitted-configuration tests execute. Generated manifests keep that HTML
1989
+ dependency scoped to `app/browser`; source-only, `app/core`, and `app/server` workspaces do not
1990
+ receive it. The engines floor is Node
1904
1991
  `>=22.12.0`, and the build emits ES and CJS for both library faces plus an ES executable.
1905
1992
 
1906
1993
  ## Patterns
@@ -2245,6 +2332,7 @@ import {
2245
2332
  appViteConfig,
2246
2333
  applicationViteConfig,
2247
2334
  binViteProject,
2335
+ configViteProject,
2248
2336
  coreTsconfig,
2249
2337
  coreViteConfig,
2250
2338
  guidesViteProject,
@@ -2276,13 +2364,14 @@ coreViteConfig()
2276
2364
  srcViteConfig('browser')
2277
2365
  appViteConfig('server')
2278
2366
  policyViteProject()
2367
+ configViteProject()
2279
2368
  guidesViteProject()
2280
2369
  binViteProject()
2281
2370
  integrationViteProject({ bin: true, integration: true, global: true })
2282
2371
  serviceViteProject()
2283
2372
  viteProjectDefinitions({ integration: true }).includes('export const integration =') // true
2284
2373
  viteProjectRegistrations(['core'], [], { integration: true }).map(({ project }) => project)
2285
- // ['srcCore', 'policy', 'guides', 'integration']
2374
+ // ['srcCore', 'policy', 'config', 'guides', 'integration']
2286
2375
 
2287
2376
  rootViteConfig(['core', 'server'], { bin: true })
2288
2377
  singleSrcViteConfig('server').includes('srcServer') // true
@@ -2576,6 +2665,8 @@ isMissingPathError(caught) // true only for an ENOENT error
2576
2665
  ids, the batch-overload semantics, and all-or-nothing list removal.
2577
2666
  - [`tests/src/core/policy.test.ts`](../../tests/src/core/policy.test.ts) — the repository coding-law
2578
2667
  policy module against this workspace and against deliberately hostile fixtures.
2668
+ - [`tests/config/vite.test.ts`](../../tests/config/vite.test.ts) — the executable root Vite
2669
+ invariants for workspace, environment, and output containment.
2579
2670
  - [`tests/src/server/helpers.test.ts`](../../tests/src/server/helpers.test.ts) — containment,
2580
2671
  digests, host staging, hydration, derivation, prune scanning, and the local catalog.
2581
2672
  - [`tests/src/server/validators.test.ts`](../../tests/src/server/validators.test.ts) — the portable