@orkestrel/scaffold 0.0.44 → 0.0.45

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.
Files changed (66) hide show
  1. package/README.md +10 -10
  2. package/dist/bin/main.js +31 -31
  3. package/dist/bin/main.js.map +1 -1
  4. package/dist/host/AGENTS.md +7 -2
  5. package/dist/host/agents/orchestration.md +232 -56
  6. package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +7 -7
  7. package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +11 -9
  8. package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +3 -3
  9. package/dist/host/agents/skills/enterprise-bootstrap/references/frontend-design.md +3 -3
  10. package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +1 -1
  11. package/dist/host/agents/skills/orkestrel-align-packages/SKILL.md +1 -1
  12. package/dist/host/agents/skills/orkestrel-build-application/SKILL.md +2 -2
  13. package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +1 -1
  14. package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +2 -2
  15. package/dist/host/agents/skills/orkestrel-debrief/references/instruction-audit.md +8 -8
  16. package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +16 -14
  17. package/dist/host/agents/skills/orkestrel-falsify/references/brief.md +3 -3
  18. package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +14 -14
  19. package/dist/host/agents/skills/orkestrel-harden-package/SKILL.md +1 -1
  20. package/dist/host/agents/skills/orkestrel-human-journey/SKILL.md +3 -3
  21. package/dist/host/agents/skills/orkestrel-human-journey/references/captures.md +3 -3
  22. package/dist/host/agents/skills/orkestrel-human-journey/references/layer.md +3 -3
  23. package/dist/host/agents/skills/orkestrel-polish-surface/SKILL.md +3 -3
  24. package/dist/host/claude/agents/analyst.md +2 -2
  25. package/dist/host/claude/agents/checker.md +2 -2
  26. package/dist/host/claude/agents/codex.md +5 -5
  27. package/dist/host/claude/agents/orkestrel.md +9 -8
  28. package/dist/host/claude/agents/planner.md +1 -1
  29. package/dist/host/claude/agents/researcher.md +2 -2
  30. package/dist/host/claude/agents/reviewer.md +1 -1
  31. package/dist/host/claude/agents/scout.md +2 -2
  32. package/dist/host/claude/agents/sol.md +3 -3
  33. package/dist/host/claude/agents/verifier.md +8 -0
  34. package/dist/host/claude/rules/application.md +7 -7
  35. package/dist/host/claude/rules/architecture.md +6 -6
  36. package/dist/host/claude/rules/documentation.md +1 -0
  37. package/dist/host/claude/rules/patterns.md +3 -3
  38. package/dist/host/claude/rules/quality.md +3 -3
  39. package/dist/host/claude/rules/tests.md +9 -2
  40. package/dist/host/claude/rules/workspace.md +7 -7
  41. package/dist/host/claude/rules/writing.md +12 -2
  42. package/dist/host/codex/agents/planner.toml +1 -1
  43. package/dist/host/configs/helpers.ts +21 -1
  44. package/dist/host/cursor/rules/orchestration.mdc +1 -1
  45. package/dist/host/dotfiles/gitignore +4 -1
  46. package/dist/host/guides/scaffold.md +134 -119
  47. package/dist/host/scripts/codex.sh +0 -0
  48. package/dist/host/scripts/cursor.sh +0 -0
  49. package/dist/host/scripts/deps.sh +0 -0
  50. package/dist/host/scripts/ollama.sh +0 -0
  51. package/dist/host/tests/config.test.ts +40 -1
  52. package/dist/host/tests/policy.test.ts +2 -2
  53. package/dist/host/tests/setupPolicy.ts +8 -5
  54. package/dist/src/core/index.cjs +143 -149
  55. package/dist/src/core/index.cjs.map +1 -1
  56. package/dist/src/core/index.d.cts +1624 -1643
  57. package/dist/src/core/index.d.ts +1624 -1643
  58. package/dist/src/core/index.js +144 -149
  59. package/dist/src/core/index.js.map +1 -1
  60. package/dist/src/server/index.cjs +82 -97
  61. package/dist/src/server/index.cjs.map +1 -1
  62. package/dist/src/server/index.d.cts +1844 -1871
  63. package/dist/src/server/index.d.ts +1844 -1871
  64. package/dist/src/server/index.js +83 -96
  65. package/dist/src/server/index.js.map +1 -1
  66. package/package.json +3 -3
@@ -16,7 +16,7 @@ export declare const APP_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
16
16
  * @remarks
17
17
  * An application environment declares no exports, so it carries a runtime
18
18
  * entry instead of a subpath and formats. Core carries none because it is
19
- * shared logic the other two import rather than a host that runs.
19
+ * shared logic the other environments import rather than a host that runs.
20
20
  */
21
21
  export declare const APP_MATRIX: Readonly<Record<Environment, AppDefinition>>;
22
22
 
@@ -159,7 +159,7 @@ export declare function artifactsToQuestions(artifacts: readonly Artifact[]): re
159
159
  * destination has no bytes to record, and every other verdict records the bytes
160
160
  * it was given, which is the precondition the mutation that follows is held to.
161
161
  * Ownership is copied rather than inferred from drift because aligned findings
162
- * span all three ownership tiers.
162
+ * span every ownership tier.
163
163
  *
164
164
  * `foreign` is not answerable here, because it describes a path no artifact was
165
165
  * planned for.
@@ -231,7 +231,7 @@ export declare const BIN_ENTRY_PATH = "src/bin/main.ts";
231
231
  *
232
232
  * @remarks
233
233
  * `src` selects published library environments and `app` selects private
234
- * runtime environments. The two axes are independent, so library-only,
234
+ * runtime environments. The axes are independent, so library-only,
235
235
  * application-only, and mixed workspaces are all first class. `dependencies`
236
236
  * are runtime `@orkestrel/*` packages. A peer in the `@orkestrel` scope is a
237
237
  * fleet pin; every other peer is a floor. `extras` are package-specific
@@ -313,7 +313,7 @@ export declare function blueprintToConfigArtifacts(blueprint: Blueprint): readon
313
313
  * not installed by the workspace that declares it and developing against one
314
314
  * requires it present. A runtime dependency is the opposite case and is removed:
315
315
  * it is already installed, so a second declaration would state one fact twice
316
- * and the two ranges would be free to disagree.
316
+ * and the ranges would be free to disagree.
317
317
  *
318
318
  * A workspace never declares itself, so its own package name is removed. That
319
319
  * matters for a workspace named after a package the baseline already carries:
@@ -351,7 +351,7 @@ export declare function blueprintToGuideArtifacts(blueprint: Blueprint): readonl
351
351
  * Derive the host-specific machinery a generated root Vite configuration carries.
352
352
  *
353
353
  * @param blueprint - The workspace specification.
354
- * @returns The four pipelines the generated configuration selects.
354
+ * @returns The pipelines the generated configuration selects.
355
355
  *
356
356
  * @remarks
357
357
  * The sole derivation of that set: every renderer reads it rather than
@@ -396,7 +396,7 @@ export declare function blueprintToMachinery(blueprint: Blueprint): ViteMachiner
396
396
  * artifact serialized directly.
397
397
  *
398
398
  * The artifact carrying this text is claimed by birth. A workspace owns its own
399
- * manifest once it exists: its description, its keywords, and any script it
399
+ * manifest after it exists: its description, its keywords, and any script it
400
400
  * added are the consumer's, so a repair that replaced the file would take them.
401
401
  * The one part scaffold keeps current afterwards is the declared `@orkestrel/*`
402
402
  * range set, and that is a region with its own writer rather than a claim over
@@ -437,14 +437,14 @@ export declare function blueprintToOrchestrationArtifacts(blueprint: Blueprint):
437
437
  * Only the laws a blueprint answers alone are here. The structural record and
438
438
  * its bounds are already settled by `isBlueprint`, which refuses a value that is
439
439
  * not a blueprint at all; what remains is the syntax of a name, a version, a
440
- * range, and an engines floor, the combinations the two environment axes admit,
441
- * and the overlaps between the three declared package lists. The laws that need
440
+ * range, and an engines floor, the combinations the environment axes admit,
441
+ * and the overlaps between the declared package lists. The laws that need
442
442
  * a drafted plan belong to {@link artifactsToQuestions} and
443
443
  * {@link overridesToQuestions}.
444
444
  *
445
445
  * A question blocks when it describes a workspace this package cannot generate.
446
- * Three do not, because each describes a workspace it can describe honestly and
447
- * should not create: a published axis of several environments without core,
446
+ * These do not, because each describes a workspace it can describe honestly but
447
+ * will not create: a published axis of several environments without core,
448
448
  * whose manifest names a core build the workspace never runs; a showcase flag
449
449
  * whose required browser axis is absent, which emits nothing; and an
450
450
  * integration flag over fewer than two environments, whose seed does emit and
@@ -555,7 +555,7 @@ export declare function blueprintToScripts(blueprint: Blueprint): Readonly<Recor
555
555
  *
556
556
  * @remarks
557
557
  * The barrels and every runtime entry intentionally hold nothing. A generated
558
- * sample entity is too easy to mistake for package implementation, so the
558
+ * sample entity would read as package implementation, so the
559
559
  * scaffold establishes only the selected environment boundaries. An application
560
560
  * entry is empty for the same reason the bin entry is, and because the vendored
561
561
  * lint config refuses an unassigned import outside a stylesheet, so the entry
@@ -683,8 +683,8 @@ export declare type CatalogEntry = {
683
683
  *
684
684
  * The order matters because these packages are `0.0.x`, where a caret pins one
685
685
  * exact release. Publishing a dependent before its dependency leaves the
686
- * dependent pinned to the older release, and two ranges that disagree install
687
- * two copies of one package that the compiler reads as two distinct types.
686
+ * dependent pinned to the older release, and ranges that disagree install
687
+ * duplicate copies of one package that the compiler reads as distinct types.
688
688
  *
689
689
  * A cycle cannot be published in rounds, so its members are omitted rather than
690
690
  * placed in an order that would be wrong. An absent name is the report: compare
@@ -793,7 +793,7 @@ export declare interface CompileFailure {
793
793
  * `INVALID`. Structure raises; the laws a well-formed blueprint can still break
794
794
  * are the gate's, and they answer with questions.
795
795
  *
796
- * Two consequences of that order are worth stating, because they are the ones a
796
+ * The consequences of that order are worth stating, because they are the ones a
797
797
  * JavaScript caller meets first. A property backed by an accessor is refused
798
798
  * rather than read, which is what closes the race a guard cannot close from
799
799
  * inside; the accessor never runs. And an optional field present with the value
@@ -845,21 +845,21 @@ export declare class Compiler implements CompilerInterface {
845
845
  * carries no plan, then `compile` with the whole outcome either way, so an
846
846
  * observer reads every compile from one event and the refusals from the other.
847
847
  *
848
- * A plan says the blueprint can be built. It does not say the blueprint should
849
- * be created, and the questions beside it are what this compiler could not
850
- * settle. A caller creating a fresh workspace answers them first and writes
851
- * nothing while any remains, which is the rule the `new` verb applies; a caller
852
- * describing or repairing an existing target carries them through instead.
853
- * Nothing downstream repeats that check, because only the caller knows which of
854
- * the two it is.
848
+ * A plan says the blueprint can be built. It does not decide whether to create
849
+ * it, and the questions beside it are what this compiler could not settle. A
850
+ * caller creating a fresh workspace answers them first and writes nothing while
851
+ * any remains, which is the rule the `new` verb applies; a caller describing or
852
+ * repairing an existing target carries them through instead.
853
+ * Nothing downstream repeats that check, because only the caller knows which
854
+ * case it is.
855
855
  *
856
856
  * @example
857
857
  * ```ts
858
- * import { createBlueprint, createCompiler } from '@orkestrel/scaffold'
858
+ * import { Compiler, createBlueprint } from '@orkestrel/scaffold'
859
859
  *
860
860
  * const blueprint = createBlueprint('router', { src: ['core'] })
861
861
  *
862
- * createCompiler().compile(blueprint, ['manifest']).plan?.artifacts.length // 1
862
+ * new Compiler().compile(blueprint, ['manifest']).plan?.artifacts.length // 1
863
863
  * ```
864
864
  */
865
865
  compile(blueprint: Blueprint, groups?: readonly Group[]): Scaffolding;
@@ -885,12 +885,12 @@ export declare class Compiler implements CompilerInterface {
885
885
  *
886
886
  * @example
887
887
  * ```ts
888
- * import { createBlueprint, createCompiler } from '@orkestrel/scaffold'
888
+ * import { Compiler, createBlueprint } from '@orkestrel/scaffold'
889
889
  *
890
890
  * const blueprint = createBlueprint('router', { src: ['core'] })
891
891
  *
892
- * createCompiler().audit(blueprint, {}, ['manifest']).findings[0]?.drift // 'aligned'
893
- * createCompiler().audit(blueprint, {}, ['configs']).findings[0]?.drift // 'missing'
892
+ * new Compiler().audit(blueprint, {}, ['manifest']).findings[0]?.drift // 'aligned'
893
+ * new Compiler().audit(blueprint, {}, ['configs']).findings[0]?.drift // 'missing'
894
894
  * ```
895
895
  */
896
896
  audit(blueprint: Blueprint, current: Snapshot, groups?: readonly Group[]): Audit;
@@ -901,9 +901,9 @@ export declare class Compiler implements CompilerInterface {
901
901
  *
902
902
  * @example
903
903
  * ```ts
904
- * import { createCompiler } from '@orkestrel/scaffold'
904
+ * import { Compiler } from '@orkestrel/scaffold'
905
905
  *
906
- * const compiler = createCompiler()
906
+ * const compiler = new Compiler()
907
907
  * compiler.destroy()
908
908
  * compiler.emitter.destroyed // true
909
909
  * ```
@@ -967,7 +967,7 @@ export declare class Compiler implements CompilerInterface {
967
967
  readonly error?: EmitterErrorHandler;
968
968
  }
969
969
 
970
- /** The three compile phases, in the order they run. */
970
+ /** The compile phases, in the order they run. */
971
971
  export declare type CompileStage = 'draft' | 'gate' | 'pin';
972
972
 
973
973
  /**
@@ -1043,15 +1043,15 @@ export declare class Compiler implements CompilerInterface {
1043
1043
  }>;
1044
1044
  factories: Readonly<{
1045
1045
  src: Readonly<{
1046
- core: "export const srcCore = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tsourcemap: true,\n\t\t\t\tminify: false,\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'src:core', color: 'magenta' },\n\t\t\t\tinclude: ['tests/src/core/**/*.test.ts'],\n\t\t\t\tsetupFiles: ['./tests/setup.ts'],\n\t\t\t\tenvironment: 'node',\n\t\t\t\tbrowser: { enabled: false },\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
1047
- browser: "export const srcBrowser = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tplugins: [outputBoundary('dist/src/browser'), environmentBoundary('src/browser')],\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tsourcemap: true,\n\t\t\t\tminify: false,\n\t\t\t\tlib: {\n\t\t\t\t\tentry: resolveWorkspacePath('src/browser/index.ts'),\n\t\t\t\t\tformats: ['es'],\n\t\t\t\t\tfileName: () => 'index.js',\n\t\t\t\t},\n\t\t\t\toutDir: 'dist/src/browser',\n\t\t\t\trolldownOptions: {\n\t\t\t\t\t{{external}}\n{{output}}\n\t\t\t\t},\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'src:browser', color: 'yellow' },\n\t\t\t\tinclude: ['tests/src/browser/**/*.test.ts'],\n{{exclude}}\t\t\t\tsetupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts'],\n{{global}}\n\t\t\t\tbrowser: {\n\t\t\t\t\tenabled: true,\n\t\t\t\t\tprovider: playwright(browserOptions),\n\t\t\t\t\tinstances: [{ browser: 'chromium', headless: true }],\n\t\t\t\t},\n\t\t\t\tfileParallelism: false,\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
1048
- server: "export const srcServer = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tplugins: [outputBoundary('dist/src/server'), environmentBoundary('src/server')],\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tsourcemap: true,\n\t\t\t\tminify: false,\n\t\t\t\tlib: {\n\t\t\t\t\tentry: resolveWorkspacePath('src/server/index.ts'),\n\t\t\t\t\tformats: ['es', 'cjs'],\n\t\t\t\t\tfileName: (format: string) => (format === 'es' ? 'index.js' : 'index.cjs'),\n\t\t\t\t},\n\t\t\t\toutDir: 'dist/src/server',\n\t\t\t\ttarget: 'node22',\n\t\t\t\trolldownOptions: {\n\t\t\t\t\tplatform: 'node',\n\t\t\t\t\t{{external}}\n{{output}}\n\t\t\t\t},\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'src:server', color: 'red' },\n\t\t\t\tinclude: ['tests/src/server/**/*.test.ts'],\n{{exclude}}\t\t\t\tsetupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],\n\t\t\t\tenvironment: 'node',\n\t\t\t\tbrowser: { enabled: false },\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
1049
- bin: "export const srcBin = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tplugins: [outputBoundary('dist/bin')],\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tsourcemap: true,\n\t\t\t\tminify: false,\n\t\t\t\tlib: {\n\t\t\t\t\tentry: resolveWorkspacePath('src/bin/main.ts'),\n\t\t\t\t\tformats: ['es'],\n\t\t\t\t\tfileName: () => 'main.js',\n\t\t\t\t},\n\t\t\t\toutDir: 'dist/bin',\n\t\t\t\ttarget: 'node22',\n\t\t\t\trolldownOptions: {\n\t\t\t\t\texternal: (id: string) =>\n\t\t\t\t\t\tid.startsWith('node:') ||\n\t\t\t\t\t\tid.startsWith('@orkestrel/') ||\n\t\t\t\t\t\tid.startsWith('@src/') ||\n\t\t\t\t\t\tpeers.some((peer) => id === peer || id.startsWith(peer + '/')),\n\t\t\t\t},\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'src:bin', color: 'yellow' },\n\t\t\t\tinclude: ['tests/src/bin/**/*.test.ts'],\n\t\t\t\tsetupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],\n\t\t\t\tenvironment: 'node',\n\t\t\t\tbrowser: { enabled: false },\n\t\t\t\t// A bin test drives the real executable over a real temporary repository, so it\n\t\t\t\t// spends seconds in process startup and filesystem work rather than milliseconds.\n\t\t\t\t// Vitest's five-second default clears one alone and times out under a full suite.\n\t\t\t\ttestTimeout: 15_000,\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
1046
+ core: "export const srcCore = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tsourcemap: true,\n\t\t\t\tminify: false,\n\t\t\t\trolldownOptions: { onLog: enforceBuildLog },\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'src:core', color: 'magenta' },\n\t\t\t\tinclude: ['tests/src/core/**/*.test.ts'],\n\t\t\t\tsetupFiles: ['./tests/setup.ts'],\n\t\t\t\tenvironment: 'node',\n\t\t\t\tbrowser: { enabled: false },\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
1047
+ browser: "export const srcBrowser = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tplugins: [outputBoundary('dist/src/browser'), environmentBoundary('src/browser')],\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tsourcemap: true,\n\t\t\t\tminify: false,\n\t\t\t\tlib: {\n\t\t\t\t\tentry: resolveWorkspacePath('src/browser/index.ts'),\n\t\t\t\t\tformats: ['es'],\n\t\t\t\t\tfileName: () => 'index.js',\n\t\t\t\t},\n\t\t\t\toutDir: 'dist/src/browser',\n\t\t\t\trolldownOptions: {\n\t\t\t\t\tonLog: enforceBuildLog,\n\t\t\t\t\t{{external}}\n{{output}}\n\t\t\t\t},\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'src:browser', color: 'yellow' },\n\t\t\t\tinclude: ['tests/src/browser/**/*.test.ts'],\n{{exclude}}\t\t\t\tsetupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts'],\n{{global}}\n\t\t\t\tbrowser: {\n\t\t\t\t\tenabled: true,\n\t\t\t\t\tprovider: playwright(browserOptions),\n\t\t\t\t\tinstances: [{ browser: 'chromium', headless: true }],\n\t\t\t\t},\n\t\t\t\tfileParallelism: false,\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
1048
+ server: "export const srcServer = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tplugins: [outputBoundary('dist/src/server'), environmentBoundary('src/server')],\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tsourcemap: true,\n\t\t\t\tminify: false,\n\t\t\t\tlib: {\n\t\t\t\t\tentry: resolveWorkspacePath('src/server/index.ts'),\n\t\t\t\t\tformats: ['es', 'cjs'],\n\t\t\t\t\tfileName: (format: string) => (format === 'es' ? 'index.js' : 'index.cjs'),\n\t\t\t\t},\n\t\t\t\toutDir: 'dist/src/server',\n\t\t\t\ttarget: 'node22',\n\t\t\t\trolldownOptions: {\n\t\t\t\t\tonLog: enforceBuildLog,\n\t\t\t\t\tplatform: 'node',\n\t\t\t\t\t{{external}}\n{{output}}\n\t\t\t\t},\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'src:server', color: 'red' },\n\t\t\t\tinclude: ['tests/src/server/**/*.test.ts'],\n{{exclude}}\t\t\t\tsetupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],\n\t\t\t\tenvironment: 'node',\n\t\t\t\tbrowser: { enabled: false },\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
1049
+ bin: "export const srcBin = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tplugins: [outputBoundary('dist/bin')],\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tsourcemap: true,\n\t\t\t\tminify: false,\n\t\t\t\tlib: {\n\t\t\t\t\tentry: resolveWorkspacePath('src/bin/main.ts'),\n\t\t\t\t\tformats: ['es'],\n\t\t\t\t\tfileName: () => 'main.js',\n\t\t\t\t},\n\t\t\t\toutDir: 'dist/bin',\n\t\t\t\ttarget: 'node22',\n\t\t\t\trolldownOptions: {\n\t\t\t\t\tonLog: enforceBuildLog,\n\t\t\t\t\texternal: (id: string) =>\n\t\t\t\t\t\tid.startsWith('node:') ||\n\t\t\t\t\t\tid.startsWith('@orkestrel/') ||\n\t\t\t\t\t\tid.startsWith('@src/') ||\n\t\t\t\t\t\tpeers.some((peer) => id === peer || id.startsWith(peer + '/')),\n\t\t\t\t},\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'src:bin', color: 'yellow' },\n\t\t\t\tinclude: ['tests/src/bin/**/*.test.ts'],\n\t\t\t\tsetupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],\n\t\t\t\tenvironment: 'node',\n\t\t\t\tbrowser: { enabled: false },\n\t\t\t\t// A bin test drives the real executable over a real temporary repository, so it\n\t\t\t\t// spends seconds in process startup and filesystem work rather than milliseconds.\n\t\t\t\t// Vitest's five-second default clears one alone and times out under a full suite.\n\t\t\t\ttestTimeout: 15_000,\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
1050
1050
  }>;
1051
1051
  app: Readonly<{
1052
1052
  core: "export const appCore = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tplugins: [environmentBoundary('app/core')],\n\t\t\ttest: {\n\t\t\t\tname: { label: 'app:core', color: 'cyan' },\n\t\t\t\tinclude: ['tests/app/core/**/*.test.ts'],\n\t\t\t\tsetupFiles: ['./tests/setup.ts'],\n\t\t\t\tenvironment: 'node',\n\t\t\t\tbrowser: { enabled: false },\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
1053
- browser: "function applicationBrowser(showcase: boolean): UserConfig {\n\tconst output = showcase ? 'dist/showcase' : 'dist/app/browser'\n{{showcasePlugins}}\treturn {\n\t\tresolve,\n{{plugins}}\t\troot: resolveWorkspacePath('app/browser'),\n\t\tpublicDir: false,\n\t\tbuild: {\n{{showcaseBuild}}\t\t\temptyOutDir: true,\n\t\t\toutDir: resolveWorkspacePath(output),\n\t\t\trolldownOptions: { input: resolveWorkspacePath('app/browser/index.html') },\n\t\t},\n\t\ttest: {\n\t\t\tname: { label: 'app:browser', color: 'blue' },\n\t\t\troot: resolveWorkspacePath('.'),\n\t\t\tdir: resolveWorkspacePath('.'),\n\t\t\tinclude: ['tests/app/browser/**/*.test.ts'],\n\t\t\tsetupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts'],\n\t\t\tbrowser: {\n\t\t\t\tenabled: true,\n\t\t\t\tprovider: playwright(browserOptions),\n\t\t\t\tinstances: [{ browser: 'chromium', headless: true }],\n\t\t\t},\n\t\t\tfileParallelism: false,\n\t\t},\n\t}\n}\n\nexport function appBrowser(): UserConfig {\n\treturn applicationBrowser(false)\n}\n{{showcaseFactory}}";
1054
- server: "export const appServer = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tplugins: [outputBoundary('dist/app/server'), environmentBoundary('app/server')],\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tlib: {\n\t\t\t\t\tentry: resolveWorkspacePath('app/server/main.ts'),\n\t\t\t\t\tformats: ['cjs'],\n\t\t\t\t\tfileName: () => 'main.cjs',\n\t\t\t\t},\n\t\t\t\toutDir: resolveWorkspacePath('dist/app/server'),\n\t\t\t\ttarget: 'node22',\n\t\t\t\trolldownOptions: { external: (id: string) => id.startsWith('node:') },\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'app:server', color: 'green' },\n\t\t\t\tinclude: ['tests/app/server/**/*.test.ts'],\n\t\t\t\tsetupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],\n\t\t\t\tenvironment: 'node',\n\t\t\t\tbrowser: { enabled: false },\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
1053
+ browser: "function applicationBrowser(showcase: boolean): UserConfig {\n\tconst output = showcase ? 'dist/showcase' : 'dist/app/browser'\n{{showcasePlugins}}\treturn {\n\t\tresolve,\n{{plugins}}\t\troot: resolveWorkspacePath('app/browser'),\n\t\tpublicDir: false,\n\t\tbuild: {\n{{showcaseBuild}}\t\t\temptyOutDir: true,\n\t\t\toutDir: resolveWorkspacePath(output),\n\t\t\trolldownOptions: {\n\t\t\t\tonLog: enforceBuildLog,\n\t\t\t\tinput: resolveWorkspacePath('app/browser/index.html'),\n\t\t\t},\n\t\t},\n\t\ttest: {\n\t\t\tname: { label: 'app:browser', color: 'blue' },\n\t\t\troot: resolveWorkspacePath('.'),\n\t\t\tdir: resolveWorkspacePath('.'),\n\t\t\tinclude: ['tests/app/browser/**/*.test.ts'],\n\t\t\tsetupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts'],\n\t\t\tbrowser: {\n\t\t\t\tenabled: true,\n\t\t\t\tprovider: playwright(browserOptions),\n\t\t\t\tinstances: [{ browser: 'chromium', headless: true }],\n\t\t\t},\n\t\t\tfileParallelism: false,\n\t\t},\n\t}\n}\n\nexport function appBrowser(): UserConfig {\n\treturn applicationBrowser(false)\n}\n{{showcaseFactory}}";
1054
+ server: "export const appServer = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\tpublicDir: false,\n\t\t\tplugins: [outputBoundary('dist/app/server'), environmentBoundary('app/server')],\n\t\t\tbuild: {\n\t\t\t\temptyOutDir: true,\n\t\t\t\tlib: {\n\t\t\t\t\tentry: resolveWorkspacePath('app/server/main.ts'),\n\t\t\t\t\tformats: ['cjs'],\n\t\t\t\t\tfileName: () => 'main.cjs',\n\t\t\t\t},\n\t\t\t\toutDir: resolveWorkspacePath('dist/app/server'),\n\t\t\t\ttarget: 'node22',\n\t\t\t\trolldownOptions: {\n\t\t\t\t\tonLog: enforceBuildLog,\n\t\t\t\t\texternal: (id: string) => id.startsWith('node:'),\n\t\t\t\t},\n\t\t\t},\n\t\t\ttest: {\n\t\t\t\tname: { label: 'app:server', color: 'green' },\n\t\t\t\tinclude: ['tests/app/server/**/*.test.ts'],\n\t\t\t\tsetupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],\n\t\t\t\tenvironment: 'node',\n\t\t\t\tbrowser: { enabled: false },\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
1055
1055
  }>;
1056
1056
  policy: "export const policy = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\ttest: {\n\t\t\t\tname: { label: 'policy', color: 'white' },\n\t\t\t\tinclude: ['tests/policy.test.ts'],\n\t\t\t\tsetupFiles: ['./tests/setup.ts'],\n\t\t\t\tenvironment: 'node',\n\t\t\t\tbrowser: { enabled: false },\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
1057
1057
  config: "export const config = (options?: UserConfig): UserConfig =>\n\tmergeConfig(\n\t\t{\n\t\t\tresolve,\n\t\t\ttest: {\n\t\t\t\tname: { label: 'config', color: 'yellow' },\n\t\t\t\tinclude: ['tests/config.test.ts'],\n\t\t\t\tsetupFiles: ['./tests/setup.ts'],\n\t\t\t\tenvironment: 'node',\n\t\t\t\tbrowser: { enabled: false },\n\t\t\t\t// A config test validates every target wrapper and runs the real linter twice with\n\t\t\t\t// 15-second child caps, so this budget clears both caps and reports their diagnostics.\n\t\t\t\ttestTimeout: 45_000,\n\t\t\t},\n\t\t},\n\t\toptions ?? {},\n\t)\n";
@@ -1082,14 +1082,14 @@ export declare class Compiler implements CompilerInterface {
1082
1082
  browser: "import { defineConfig } from 'vite'\nimport dts from 'vite-plugin-dts'\nimport { srcBrowser, resolveWorkspacePath } from '../../vite.config.ts'\n\n// vite-plugin-dts rolls this face into one declaration, and the roll-up reaches\n// src/core through a relative source path the tarball does not carry. The path\n// keeps each source module's own depth, so a module in a browser subfolder emits\n// one that leaves dist/src entirely. The rewrite below externalizes core through\n// the package's own published root export, on the final roll-up only.\nexport default defineConfig(\n\tsrcBrowser({\n\t\tplugins: [\n\t\t\tdts({\n\t\t\t\ttsconfigPath: resolveWorkspacePath('configs/src/tsconfig.browser.json'),\n\t\t\t\tbundleTypes: true,\n\t\t\t\tbeforeWriteFile: (path, content) => ({\n\t\t\t\t\tcontent: /[\\\\/]dist[\\\\/]src[\\\\/]browser[\\\\/]index\\.d\\.ts$/.test(path)\n{{replacement}}\n\t\t\t\t\t\t: content,\n\t\t\t\t}),\n\t\t\t}),\n\t\t],\n\t}),\n)\n";
1083
1083
  server: "import { defineConfig } from 'vite'\nimport dts from 'vite-plugin-dts'\nimport { srcServer, resolveWorkspacePath } from '../../vite.config.ts'\n\n// vite-plugin-dts rolls this face into one declaration, and the roll-up reaches\n// src/core through a relative source path the tarball does not carry. The rewrite\n// below externalizes core through the package's own published root export, on the\n// final roll-up only.\nexport default defineConfig(\n\tsrcServer({\n\t\tplugins: [\n\t\t\tdts({\n\t\t\t\ttsconfigPath: resolveWorkspacePath('configs/src/tsconfig.server.json'),\n\t\t\t\tbundleTypes: true,\n\t\t\t\tbeforeWriteFile: (path, content) => ({\n\t\t\t\t\tcontent: /[\\\\/]dist[\\\\/]src[\\\\/]server[\\\\/]index\\.d\\.ts$/.test(path)\n{{replacement}}\n\t\t\t\t\t\t: content,\n\t\t\t\t}),\n\t\t\t}),\n\t\t],\n\t}),\n)\n";
1084
1084
  }>;
1085
- bin: "import { defineConfig } from 'vite'\nimport { srcBin } from '../../vite.config.ts'\n\n// The `scaffold` executable build — a single ESM lib file, no declarations (an\n// executable ships no types), with the `#!/usr/bin/env node` shebang re-emitted via\n// `output.banner` (rolldown strips shebangs from source during bundling), and\n// `output.paths` rewriting the externalized `@src/*` specifiers to the built sibling\n// src environments (relative to `dist/bin/`), so the emitted bin resolves at runtime.\nexport default defineConfig(\n\tsrcBin({\n\t\tbuild: {\n\t\t\trolldownOptions: {\n\t\t\t\toutput: {\n\t\t\t\t\tbanner: '#!/usr/bin/env node',\n{{paths}}\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}),\n)\n";
1085
+ bin: "import { defineConfig } from 'vite'\nimport { srcBin } from '../../vite.config.ts'\n\n// The `scaffold` executable build — a single ESM lib file, no declarations (an\n// executable ships no types), with the `#!/usr/bin/env node` shebang re-emitted through\n// `output.banner` (rolldown strips shebangs from source during bundling), and\n// `output.paths` rewriting the externalized `@src/*` specifiers to the built sibling\n// src environments (relative to `dist/bin/`), so the emitted bin resolves at runtime.\nexport default defineConfig(\n\tsrcBin({\n\t\tbuild: {\n\t\t\trolldownOptions: {\n\t\t\t\toutput: {\n\t\t\t\t\tbanner: '#!/usr/bin/env node',\n{{paths}}\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}),\n)\n";
1086
1086
  app: Readonly<{
1087
1087
  browser: "import { defineConfig } from 'vite'\nimport { appBrowser } from '../../vite.config.ts'\n\nexport default defineConfig(appBrowser())\n";
1088
1088
  server: "import { defineConfig } from 'vite'\nimport { appServer } from '../../vite.config.ts'\n\nexport default defineConfig(appServer())\n";
1089
1089
  showcase: "import { defineConfig } from 'vite'\nimport { appShowcase } from '../../vite.config.ts'\n\nexport default defineConfig(appShowcase())\n";
1090
1090
  }>;
1091
1091
  }>;
1092
- browsers: "// A generated browser workspace resolves its own Chromium here rather than in\n// `configs/helpers.ts`, because that leaf is vendored byte-identical to every\n// workspace and most of them declare no `playwright` to import.\n\nimport type { PlaywrightProviderOptions } from '@vitest/browser-playwright'\nimport { chromium } from 'playwright'\nimport { accessSync, constants as FS_CONSTANTS, globSync, readdirSync, statSync } from 'node:fs'\nimport { basename, dirname, join, resolve as resolvePath } from 'node:path'\n\n/**\n * Chromium executable layouts inside a `chromium-<revision>` browsers-directory entry, per\n * platform.\n *\n * @remarks\n * The current Playwright build ships Chrome for Testing on macOS. The trailing `Chromium.app`\n * layouts are what earlier builds shipped, so the list spans Playwright versions instead of\n * pinning to the installed one.\n */\nexport const CHROMIUM_LAYOUTS = Object.freeze([\n\t'chrome-linux/chrome',\n\t'chrome-linux64/chrome',\n\t'chrome-win/chrome.exe',\n\t'chrome-win64/chrome.exe',\n\t'chrome-mac-x64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',\n\t'chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',\n\t'chrome-mac/Chromium.app/Contents/MacOS/Chromium',\n\t'chrome-mac-arm64/Chromium.app/Contents/MacOS/Chromium',\n])\n\n/** The `chromium-<revision>` entry name Playwright installs one managed build into. */\nexport const CHROMIUM_ENTRY_PATTERN = /^chromium-\\d+$/\n\n/** The revision number carried by any path containing a `chromium-<revision>` segment. */\nexport const CHROMIUM_REVISION_PATTERN = /chromium-(\\d+)/\n\n/** The directory a managed Linux container installs its bundled Playwright browsers into. */\nexport const BUNDLED_BROWSERS_ROOT = '/opt/pw-browsers'\n\n/**\n * Bundled Chromium layouts under the managed-container browsers root, as glob patterns.\n *\n * @remarks\n * The revision directory and its inner layout both drift across Playwright builds, and the\n * container also carries a top-level `chromium` alias, so every known shape is globbed.\n */\nexport const BUNDLED_CHROMIUM_LAYOUTS = Object.freeze([\n\t'chromium',\n\t'chromium-*/chrome-linux64/chrome',\n\t'chromium-*/chrome-linux/chrome',\n])\n\n/** Stable Playwright Chromium channels and their standard executable layouts. */\nexport const SYSTEM_BROWSER_CHANNELS = Object.freeze([\n\tObject.freeze({\n\t\tchannel: 'chrome',\n\t\tlayouts: Object.freeze({\n\t\t\tlinux: '/opt/google/chrome/chrome',\n\t\t\tdarwin: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',\n\t\t\twin32: Object.freeze(['Google', 'Chrome', 'Application', 'chrome.exe']),\n\t\t}),\n\t}),\n\tObject.freeze({\n\t\tchannel: 'msedge',\n\t\tlayouts: Object.freeze({\n\t\t\tlinux: '/opt/microsoft/msedge/msedge',\n\t\t\tdarwin: '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',\n\t\t\twin32: Object.freeze(['Microsoft', 'Edge', 'Application', 'msedge.exe']),\n\t\t}),\n\t}),\n])\n\n/**\n * Determine whether a path identifies an executable regular file.\n *\n * @param path - The filesystem path to inspect.\n * @returns Whether the path is a regular file with execute access.\n *\n * @example\n * ```ts\n * isBrowserExecutable('/opt/google/chrome/chrome')\n * ```\n */\nexport function isBrowserExecutable(path: string): boolean {\n\ttry {\n\t\tif (!statSync(path).isFile()) return false\n\t\taccessSync(path, FS_CONSTANTS.X_OK)\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Order two Chromium paths so the highest revision sorts first.\n *\n * @param left - The first path or directory entry to compare.\n * @param right - The second path or directory entry to compare.\n * @returns A negative number when `left` sorts first, positive when `right` does.\n *\n * @remarks\n * Revisions are numbers, so `chromium-1200` outranks `chromium-999` despite sorting below it\n * lexically. A path carrying no revision falls back to descending name order.\n *\n * @example\n * ```ts\n * ['chromium-999', 'chromium-1200'].sort(compareRevisions)\n * ```\n */\nexport function compareRevisions(left: string, right: string): number {\n\tconst leftRevision = CHROMIUM_REVISION_PATTERN.exec(left)?.[1]\n\tconst rightRevision = CHROMIUM_REVISION_PATTERN.exec(right)?.[1]\n\tif (leftRevision === undefined || rightRevision === undefined) return right.localeCompare(left)\n\treturn Number(rightRevision) - Number(leftRevision)\n}\n\n/**\n * Read the executable path of Playwright's pinned Chromium revision.\n *\n * @returns The pinned executable path, or `undefined` when this platform has none.\n *\n * @remarks\n * Playwright throws rather than returning a path when the current platform carries no initialized\n * executable, and an unguarded call would fail configuration evaluation for every project.\n *\n * @example\n * ```ts\n * resolvePinnedBrowser()\n * ```\n */\nexport function resolvePinnedBrowser(): string | undefined {\n\ttry {\n\t\tconst pinned = chromium.executablePath()\n\t\treturn pinned.length === 0 ? undefined : pinned\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\n/**\n * Resolve a launchable Playwright-managed Chromium executable: the pinned revision when installed,\n * otherwise a `chromium` / `chromium.exe` alias or any other `chromium-*` revision under the same\n * Playwright browsers directory. A pinned-revision miss is not Chromium absence — managed\n * containers ship one usable build, often behind a revision-agnostic alias, for many Playwright\n * versions.\n *\n * @param pinned - The executable path for Playwright's pinned Chromium revision.\n * @returns The managed executable path, or `undefined` when none is executable.\n *\n * @example\n * ```ts\n * resolveManagedBrowser('/root/.cache/ms-playwright/chromium-1234/chrome-linux64/chrome')\n * ```\n */\nexport function resolveManagedBrowser(pinned: string): string | undefined {\n\tif (isBrowserExecutable(pinned)) return pinned\n\tlet revisionRoot = dirname(pinned)\n\tfor (;;) {\n\t\tif (CHROMIUM_ENTRY_PATTERN.test(basename(revisionRoot))) break\n\t\tconst parent = dirname(revisionRoot)\n\t\tif (parent === revisionRoot) return undefined\n\t\trevisionRoot = parent\n\t}\n\tconst browsersRoot = dirname(revisionRoot)\n\tfor (const alias of ['chromium', 'chromium.exe']) {\n\t\tconst candidate = resolvePath(browsersRoot, alias)\n\t\tif (isBrowserExecutable(candidate)) return candidate\n\t}\n\tlet entries: readonly string[]\n\ttry {\n\t\tentries = readdirSync(browsersRoot)\n\t} catch {\n\t\treturn undefined\n\t}\n\tconst revisions = entries\n\t\t.filter((entry) => CHROMIUM_ENTRY_PATTERN.test(entry))\n\t\t.sort(compareRevisions)\n\tfor (const revision of revisions) {\n\t\tfor (const layout of CHROMIUM_LAYOUTS) {\n\t\t\tconst candidate = resolvePath(browsersRoot, revision, layout)\n\t\t\tif (isBrowserExecutable(candidate)) return candidate\n\t\t}\n\t}\n\treturn undefined\n}\n\n/**\n * Resolve the Chromium a managed Linux container bundles outside the Playwright cache.\n *\n * @param platform - The Node platform the container runs on.\n * @param root - The bundled browsers directory to search.\n * @returns The highest matching executable path, or `undefined` when none is executable.\n *\n * @example\n * ```ts\n * resolveBundledBrowser('linux', BUNDLED_BROWSERS_ROOT)\n * ```\n */\nexport function resolveBundledBrowser(platform: NodeJS.Platform, root: string): string | undefined {\n\tif (platform !== 'linux') return undefined\n\tfor (const layout of BUNDLED_CHROMIUM_LAYOUTS) {\n\t\tlet matches: readonly string[]\n\t\ttry {\n\t\t\tmatches = globSync(layout, { cwd: root })\n\t\t} catch {\n\t\t\treturn undefined\n\t\t}\n\t\tfor (const match of [...matches].sort(compareRevisions)) {\n\t\t\tconst candidate = resolvePath(root, match)\n\t\t\tif (isBrowserExecutable(candidate)) return candidate\n\t\t}\n\t}\n\treturn undefined\n}\n\n/**\n * Resolve the first installed stable system Chromium channel.\n *\n * @param platform - The Node platform whose standard layouts should be probed.\n * @param environment - The process environment supplying Windows installation roots.\n * @returns `chrome`, then `msedge`, or `undefined` when neither is executable.\n *\n * @example\n * ```ts\n * resolveSystemBrowser(process.platform, process.env)\n * ```\n */\nexport function resolveSystemBrowser(\n\tplatform: NodeJS.Platform,\n\tenvironment: NodeJS.ProcessEnv,\n): string | undefined {\n\tif (platform !== 'linux' && platform !== 'darwin' && platform !== 'win32') return undefined\n\tconst roots = new Set<string>()\n\tif (platform === 'win32') {\n\t\tfor (const root of [\n\t\t\tenvironment.LOCALAPPDATA,\n\t\t\tenvironment.PROGRAMFILES,\n\t\t\tenvironment['PROGRAMFILES(X86)'],\n\t\t]) {\n\t\t\tif (root !== undefined && root.length > 0) roots.add(root)\n\t\t}\n\t\tconst homeDrive = environment.HOMEDRIVE\n\t\tif (homeDrive !== undefined && homeDrive.length > 0) {\n\t\t\troots.add(join(homeDrive, 'Program Files'))\n\t\t\troots.add(join(homeDrive, 'Program Files (x86)'))\n\t\t}\n\t}\n\tfor (const browser of SYSTEM_BROWSER_CHANNELS) {\n\t\tif (platform === 'win32') {\n\t\t\tfor (const root of roots) {\n\t\t\t\tif (isBrowserExecutable(join(root, ...browser.layouts.win32))) return browser.channel\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif (isBrowserExecutable(browser.layouts[platform])) return browser.channel\n\t}\n\treturn undefined\n}\n\n/**\n * Resolve Playwright provider options for whatever browser this host can actually launch.\n *\n * @param pinned - The executable path for Playwright's pinned Chromium revision, when it has one.\n * @param platform - The Node platform whose standard layouts should be probed.\n * @param environment - The process environment supplying operator overrides and Windows roots.\n * @param root - The managed-container bundled browsers directory to search.\n * @returns Provider options naming an executable, a WebSocket endpoint, or a channel.\n *\n * @remarks\n * Precedence, most important first: `PLAYWRIGHT_EXECUTABLE_PATH`, `PLAYWRIGHT_WS_ENDPOINT`,\n * `PLAYWRIGHT_CHANNEL`, the managed Playwright Chromium, the container's bundled Chromium, a\n * verified system channel, then the platform default channel. An operator override outranks\n * discovery and is returned exactly as given: none of those three environment values is checked\n * against the filesystem, because verifying an override would defeat the override. The pinned\n * managed revision outranks anything found on the host because it is deterministic. The installed\n * pinned revision returns empty options so Playwright keeps its own default launch semantics. Only\n * a discovered system channel is verified before it is named. The platform default is unverified\n * as well and exists only as a last resort: Windows takes `msedge`, which ships with the OS and\n * never collides with a foreground Chrome.\n *\n * @example\n * ```ts\n * resolveBrowser(resolvePinnedBrowser(), process.platform, process.env)\n * ```\n */\nexport function resolveBrowser(\n\tpinned: string | undefined,\n\tplatform: NodeJS.Platform,\n\tenvironment: NodeJS.ProcessEnv,\n\troot: string = BUNDLED_BROWSERS_ROOT,\n): PlaywrightProviderOptions {\n\tconst executable = environment.PLAYWRIGHT_EXECUTABLE_PATH\n\tif (executable !== undefined && executable.length > 0) {\n\t\treturn { launchOptions: { executablePath: executable } }\n\t}\n\tconst endpoint = environment.PLAYWRIGHT_WS_ENDPOINT\n\tif (endpoint !== undefined && endpoint.length > 0) {\n\t\treturn { connectOptions: { wsEndpoint: endpoint } }\n\t}\n\tconst requested = environment.PLAYWRIGHT_CHANNEL\n\tif (requested !== undefined && requested.length > 0) {\n\t\treturn { launchOptions: { channel: requested } }\n\t}\n\tconst managed = pinned === undefined ? undefined : resolveManagedBrowser(pinned)\n\tif (managed !== undefined) {\n\t\treturn managed === pinned ? {} : { launchOptions: { executablePath: managed } }\n\t}\n\tconst bundled = resolveBundledBrowser(platform, root)\n\tif (bundled !== undefined) return { launchOptions: { executablePath: bundled } }\n\tconst fallback = platform === 'win32' ? 'msedge' : 'chrome'\n\treturn { launchOptions: { channel: resolveSystemBrowser(platform, environment) ?? fallback } }\n}\n";
1092
+ browsers: "// A generated browser workspace resolves its own Chromium here rather than in\n// `configs/helpers.ts`, because that leaf is vendored byte-identical to every\n// workspace and most of them declare no `playwright` to import.\n\nimport type { PlaywrightProviderOptions } from '@vitest/browser-playwright'\nimport { chromium } from 'playwright'\nimport { accessSync, constants as FS_CONSTANTS, globSync, readdirSync, statSync } from 'node:fs'\nimport { basename, dirname, join, resolve as resolvePath } from 'node:path'\n\n/**\n * Chromium executable layouts inside a `chromium-<revision>` browsers-directory entry, per\n * platform.\n *\n * @remarks\n * The current Playwright build ships Chrome for Testing on macOS. The trailing `Chromium.app`\n * layouts are what earlier builds shipped, so the list spans Playwright versions instead of\n * pinning to the installed one.\n */\nexport const CHROMIUM_LAYOUTS = Object.freeze([\n\t'chrome-linux/chrome',\n\t'chrome-linux64/chrome',\n\t'chrome-win/chrome.exe',\n\t'chrome-win64/chrome.exe',\n\t'chrome-mac-x64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',\n\t'chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',\n\t'chrome-mac/Chromium.app/Contents/MacOS/Chromium',\n\t'chrome-mac-arm64/Chromium.app/Contents/MacOS/Chromium',\n])\n\n/** The `chromium-<revision>` entry name Playwright installs one managed build into. */\nexport const CHROMIUM_ENTRY_PATTERN = /^chromium-\\d+$/\n\n/** The revision number carried by any path containing a `chromium-<revision>` segment. */\nexport const CHROMIUM_REVISION_PATTERN = /chromium-(\\d+)/\n\n/** The directory a managed Linux container installs its bundled Playwright browsers into. */\nexport const BUNDLED_BROWSERS_ROOT = '/opt/pw-browsers'\n\n/**\n * Bundled Chromium layouts under the managed-container browsers root, as glob patterns.\n *\n * @remarks\n * The revision directory and its inner layout both drift across Playwright builds, and the\n * container also carries a top-level `chromium` alias, so every known shape is globbed.\n */\nexport const BUNDLED_CHROMIUM_LAYOUTS = Object.freeze([\n\t'chromium',\n\t'chromium-*/chrome-linux64/chrome',\n\t'chromium-*/chrome-linux/chrome',\n])\n\n/** Stable Playwright Chromium channels and their standard executable layouts. */\nexport const SYSTEM_BROWSER_CHANNELS = Object.freeze([\n\tObject.freeze({\n\t\tchannel: 'chrome',\n\t\tlayouts: Object.freeze({\n\t\t\tlinux: '/opt/google/chrome/chrome',\n\t\t\tdarwin: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',\n\t\t\twin32: Object.freeze(['Google', 'Chrome', 'Application', 'chrome.exe']),\n\t\t}),\n\t}),\n\tObject.freeze({\n\t\tchannel: 'msedge',\n\t\tlayouts: Object.freeze({\n\t\t\tlinux: '/opt/microsoft/msedge/msedge',\n\t\t\tdarwin: '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',\n\t\t\twin32: Object.freeze(['Microsoft', 'Edge', 'Application', 'msedge.exe']),\n\t\t}),\n\t}),\n])\n\n/**\n * Determine whether a path identifies an executable regular file.\n *\n * @param path - The filesystem path to inspect.\n * @returns Whether the path is a regular file with execute access.\n *\n * @example\n * ```ts\n * isBrowserExecutable('/opt/google/chrome/chrome')\n * ```\n */\nexport function isBrowserExecutable(path: string): boolean {\n\ttry {\n\t\tif (!statSync(path).isFile()) return false\n\t\taccessSync(path, FS_CONSTANTS.X_OK)\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Order two Chromium paths so the highest revision sorts first.\n *\n * @param left - The first path or directory entry to compare.\n * @param right - The second path or directory entry to compare.\n * @returns A negative number when `left` sorts first, positive when `right` does.\n *\n * @remarks\n * Revisions are numbers, so `chromium-1200` outranks `chromium-999` despite sorting below it\n * lexically. A path carrying no revision falls back to descending name order.\n *\n * @example\n * ```ts\n * ['chromium-999', 'chromium-1200'].sort(compareRevisions)\n * ```\n */\nexport function compareRevisions(left: string, right: string): number {\n\tconst leftRevision = CHROMIUM_REVISION_PATTERN.exec(left)?.[1]\n\tconst rightRevision = CHROMIUM_REVISION_PATTERN.exec(right)?.[1]\n\tif (leftRevision === undefined || rightRevision === undefined) return right.localeCompare(left)\n\treturn Number(rightRevision) - Number(leftRevision)\n}\n\n/**\n * Read the executable path of Playwright's pinned Chromium revision.\n *\n * @returns The pinned executable path, or `undefined` when this platform has none.\n *\n * @remarks\n * Playwright throws rather than returning a path when the current platform carries no initialized\n * executable, and an unguarded call would fail configuration evaluation for every project.\n *\n * @example\n * ```ts\n * resolvePinnedBrowser()\n * ```\n */\nexport function resolvePinnedBrowser(): string | undefined {\n\ttry {\n\t\tconst pinned = chromium.executablePath()\n\t\treturn pinned.length === 0 ? undefined : pinned\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\n/**\n * Resolve a launchable Playwright-managed Chromium executable: the pinned revision when installed,\n * otherwise a `chromium` / `chromium.exe` alias or any other `chromium-*` revision under the same\n * Playwright browsers directory. A pinned-revision miss is not Chromium absence — managed\n * containers ship one usable build, often behind a revision-agnostic alias, for many Playwright\n * versions.\n *\n * @param pinned - The executable path for Playwright's pinned Chromium revision.\n * @returns The managed executable path, or `undefined` when none is executable.\n *\n * @example\n * ```ts\n * resolveManagedBrowser('/root/.cache/ms-playwright/chromium-1234/chrome-linux64/chrome')\n * ```\n */\nexport function resolveManagedBrowser(pinned: string): string | undefined {\n\tif (isBrowserExecutable(pinned)) return pinned\n\tlet revisionRoot = dirname(pinned)\n\tfor (;;) {\n\t\tif (CHROMIUM_ENTRY_PATTERN.test(basename(revisionRoot))) break\n\t\tconst parent = dirname(revisionRoot)\n\t\tif (parent === revisionRoot) return undefined\n\t\trevisionRoot = parent\n\t}\n\tconst browsersRoot = dirname(revisionRoot)\n\tfor (const alias of ['chromium', 'chromium.exe']) {\n\t\tconst candidate = resolvePath(browsersRoot, alias)\n\t\tif (isBrowserExecutable(candidate)) return candidate\n\t}\n\tlet entries: readonly string[]\n\ttry {\n\t\tentries = readdirSync(browsersRoot)\n\t} catch {\n\t\treturn undefined\n\t}\n\tconst revisions = entries\n\t\t.filter((entry) => CHROMIUM_ENTRY_PATTERN.test(entry))\n\t\t.sort(compareRevisions)\n\tfor (const revision of revisions) {\n\t\tfor (const layout of CHROMIUM_LAYOUTS) {\n\t\t\tconst candidate = resolvePath(browsersRoot, revision, layout)\n\t\t\tif (isBrowserExecutable(candidate)) return candidate\n\t\t}\n\t}\n\treturn undefined\n}\n\n/**\n * Resolve the Chromium a managed Linux container bundles outside the Playwright cache.\n *\n * @param platform - The Node platform the container runs on.\n * @param root - The bundled browsers directory to search.\n * @returns The highest matching executable path, or `undefined` when none is executable.\n *\n * @example\n * ```ts\n * resolveBundledBrowser('linux', BUNDLED_BROWSERS_ROOT)\n * ```\n */\nexport function resolveBundledBrowser(platform: NodeJS.Platform, root: string): string | undefined {\n\tif (platform !== 'linux') return undefined\n\tfor (const layout of BUNDLED_CHROMIUM_LAYOUTS) {\n\t\tlet matches: readonly string[]\n\t\ttry {\n\t\t\tmatches = globSync(layout, { cwd: root })\n\t\t} catch {\n\t\t\treturn undefined\n\t\t}\n\t\tfor (const match of [...matches].sort(compareRevisions)) {\n\t\t\tconst candidate = resolvePath(root, match)\n\t\t\tif (isBrowserExecutable(candidate)) return candidate\n\t\t}\n\t}\n\treturn undefined\n}\n\n/**\n * Resolve the first installed stable system Chromium channel.\n *\n * @param platform - The Node platform whose standard layouts this call probes.\n * @param environment - The process environment supplying Windows installation roots.\n * @returns `chrome`, then `msedge`, or `undefined` when neither is executable.\n *\n * @example\n * ```ts\n * resolveSystemBrowser(process.platform, process.env)\n * ```\n */\nexport function resolveSystemBrowser(\n\tplatform: NodeJS.Platform,\n\tenvironment: NodeJS.ProcessEnv,\n): string | undefined {\n\tif (platform !== 'linux' && platform !== 'darwin' && platform !== 'win32') return undefined\n\tconst roots = new Set<string>()\n\tif (platform === 'win32') {\n\t\tfor (const root of [\n\t\t\tenvironment.LOCALAPPDATA,\n\t\t\tenvironment.PROGRAMFILES,\n\t\t\tenvironment['PROGRAMFILES(X86)'],\n\t\t]) {\n\t\t\tif (root !== undefined && root.length > 0) roots.add(root)\n\t\t}\n\t\tconst homeDrive = environment.HOMEDRIVE\n\t\tif (homeDrive !== undefined && homeDrive.length > 0) {\n\t\t\troots.add(join(homeDrive, 'Program Files'))\n\t\t\troots.add(join(homeDrive, 'Program Files (x86)'))\n\t\t}\n\t}\n\tfor (const browser of SYSTEM_BROWSER_CHANNELS) {\n\t\tif (platform === 'win32') {\n\t\t\tfor (const root of roots) {\n\t\t\t\tif (isBrowserExecutable(join(root, ...browser.layouts.win32))) return browser.channel\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif (isBrowserExecutable(browser.layouts[platform])) return browser.channel\n\t}\n\treturn undefined\n}\n\n/**\n * Resolve Playwright provider options for whatever browser this host can actually launch.\n *\n * @param pinned - The executable path for Playwright's pinned Chromium revision, when it has one.\n * @param platform - The Node platform whose standard layouts this call probes.\n * @param environment - The process environment supplying operator overrides and Windows roots.\n * @param root - The managed-container bundled browsers directory to search.\n * @returns Provider options naming an executable, a WebSocket endpoint, or a channel.\n *\n * @remarks\n * Precedence, most important first: `PLAYWRIGHT_EXECUTABLE_PATH`, `PLAYWRIGHT_WS_ENDPOINT`,\n * `PLAYWRIGHT_CHANNEL`, the managed Playwright Chromium, the container's bundled Chromium, a\n * verified system channel, then the platform default channel. An operator override outranks\n * discovery and is returned exactly as given: none of those environment values is checked\n * against the filesystem, because verifying an override would defeat the override. The pinned\n * managed revision outranks anything found on the host because it is deterministic. The installed\n * pinned revision returns empty options so Playwright keeps its own default launch semantics. Only\n * a discovered system channel is verified before it is named. The platform default is unverified\n * as well and exists only as a last resort: Windows takes `msedge`, which ships with the OS and\n * never collides with a foreground Chrome.\n *\n * @example\n * ```ts\n * resolveBrowser(resolvePinnedBrowser(), process.platform, process.env)\n * ```\n */\nexport function resolveBrowser(\n\tpinned: string | undefined,\n\tplatform: NodeJS.Platform,\n\tenvironment: NodeJS.ProcessEnv,\n\troot: string = BUNDLED_BROWSERS_ROOT,\n): PlaywrightProviderOptions {\n\tconst executable = environment.PLAYWRIGHT_EXECUTABLE_PATH\n\tif (executable !== undefined && executable.length > 0) {\n\t\treturn { launchOptions: { executablePath: executable } }\n\t}\n\tconst endpoint = environment.PLAYWRIGHT_WS_ENDPOINT\n\tif (endpoint !== undefined && endpoint.length > 0) {\n\t\treturn { connectOptions: { wsEndpoint: endpoint } }\n\t}\n\tconst requested = environment.PLAYWRIGHT_CHANNEL\n\tif (requested !== undefined && requested.length > 0) {\n\t\treturn { launchOptions: { channel: requested } }\n\t}\n\tconst managed = pinned === undefined ? undefined : resolveManagedBrowser(pinned)\n\tif (managed !== undefined) {\n\t\treturn managed === pinned ? {} : { launchOptions: { executablePath: managed } }\n\t}\n\tconst bundled = resolveBundledBrowser(platform, root)\n\tif (bundled !== undefined) return { launchOptions: { executablePath: bundled } }\n\tconst fallback = platform === 'win32' ? 'msedge' : 'chrome'\n\treturn { launchOptions: { channel: resolveSystemBrowser(platform, environment) ?? fallback } }\n}\n";
1093
1093
  }>;
1094
1094
 
1095
1095
  /** The official-tooling drift proof whose presence makes a workspace `conformance`. */
@@ -1143,17 +1143,17 @@ export declare class Compiler implements CompilerInterface {
1143
1143
  * workspace actually declares.
1144
1144
  *
1145
1145
  * This is the construction door, and {@link parseBlueprint} is the coercing one.
1146
- * They differ in all three of their parts: this fills the defaults and takes a
1146
+ * They differ in every part: this fills the defaults and takes a
1147
1147
  * partial specification, where the parser fills nothing and takes an untrusted
1148
1148
  * value; and this refuses by throwing, where the parser refuses by answering
1149
1149
  * `undefined`. What they share is the law — both accept exactly what
1150
1150
  * `isBlueprint` accepts.
1151
1151
  *
1152
1152
  * That law is structural only. Whether the name is a name, the version a
1153
- * version, and the two environment axes a combination this package can generate
1153
+ * version, and the environment axes a combination this package can generate
1154
1154
  * are the gate's laws, and the gate answers them with {@link Question}s carrying
1155
- * their accepted candidates. Deciding them here as well would state one law in
1156
- * two places and let the two answers disagree, so a blueprint the gate will
1155
+ * their accepted candidates. Deciding them here as well would restate that law
1156
+ * and let the answers disagree, so a blueprint the gate will
1157
1157
  * refuse is still constructible.
1158
1158
  *
1159
1159
  * @example
@@ -1166,1704 +1166,1685 @@ export declare class Compiler implements CompilerInterface {
1166
1166
  */
1167
1167
  export declare function createBlueprint(name: string, input?: Partial<Omit<Blueprint, 'name'>>): Blueprint;
1168
1168
 
1169
- /**
1170
- * Construct a {@link Compiler}.
1171
- *
1172
- * @param options - The initial listeners and the listener-error handler.
1173
- * @returns The compiler, typed as the contract consumers program against.
1174
- * @throws {@link ScaffoldError} coded `INVALID` when `options` is present but is
1175
- * not an option bag the compiler accepts.
1176
- *
1177
- * @example
1178
- * ```ts
1179
- * import { createCompiler } from '@orkestrel/scaffold'
1180
- *
1181
- * const compiler = createCompiler({ on: { block: (questions) => report(questions) } })
1182
- * compiler.destroy()
1183
- * ```
1184
- */
1185
- export declare function createCompiler(options?: CompilerOptions): CompilerInterface;
1186
-
1187
- /** The development dependencies that emit declarations for published source or an executable. */
1188
- export declare const DECLARATION_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
1169
+ /** The development dependencies that emit declarations for published source or an executable. */
1170
+ export declare const DECLARATION_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
1189
1171
 
1190
- /** The `engines.node` range a workspace starts with. */
1191
- export declare const DEFAULT_ENGINES = ">=22.12.0";
1172
+ /** The `engines.node` range a workspace starts with. */
1173
+ export declare const DEFAULT_ENGINES = ">=22.12.0";
1192
1174
 
1193
- /** The version a workspace starts at. */
1194
- export declare const DEFAULT_VERSION = "0.0.1";
1195
-
1196
- /**
1197
- * Measure one declared package list against the name and range syntax it accepts.
1198
- *
1199
- * @param dependencies - The declared list.
1200
- * @param field - The blueprint field the list came from, reported on each question.
1201
- * @param name - The package-name syntax the field accepts.
1202
- * @param range - The range syntax the field accepts.
1203
- * @returns One blocking question per rejected name, repeated name, and rejected
1204
- * range, in list order.
1205
- *
1206
- * @remarks
1207
- * The declared lists and peer partitions differ only in the two syntaxes they
1208
- * accept, so the rules live here once and each caller supplies its own patterns.
1209
- * A runtime dependency name reaches a path through its guide mirror and is
1210
- * fixed to the `@orkestrel` scope. A foreign peer or development extra reaches
1211
- * no path and admits any valid npm name.
1212
- *
1213
- * Both patterns must be stateless. A global or sticky pattern carries a
1214
- * `lastIndex` between calls, so it would answer differently for the same input
1215
- * depending on what was tested before it.
1216
- *
1217
- * @example
1218
- * ```ts
1219
- * import { DEPENDENCY_NAME_PATTERN, ORKESTREL_RANGE_PATTERN } from '@orkestrel/scaffold'
1220
- * import { dependenciesToQuestions } from '@orkestrel/scaffold'
1221
- *
1222
- * dependenciesToQuestions(
1223
- * [{ name: '@orkestrel/router', range: '0.0.8' }],
1224
- * 'dependencies',
1225
- * DEPENDENCY_NAME_PATTERN,
1226
- * ORKESTREL_RANGE_PATTERN,
1227
- * ).length // 1 — the range is not caret-pinned
1228
- * ```
1229
- */
1230
- export declare function dependenciesToQuestions(dependencies: readonly Dependency[], field: string, name: RegExp, range: RegExp): readonly Question[];
1175
+ /** The version a workspace starts at. */
1176
+ export declare const DEFAULT_VERSION = "0.0.1";
1231
1177
 
1232
- /**
1233
- * One runtime `@orkestrel/*` dependency of a generated workspace.
1234
- *
1235
- * @remarks
1236
- * `optional` is meaningful only on a blueprint's `peers`, where it emits a
1237
- * `peerDependenciesMeta` entry beside the peer.
1238
- */
1239
- export declare interface Dependency {
1240
- readonly name: string;
1241
- readonly range: string;
1242
- readonly optional?: boolean;
1243
- }
1244
-
1245
- /**
1246
- * The runtime dependency name syntax: the `@orkestrel` scope and a bare name.
1247
- *
1248
- * @remarks
1249
- * A dependency name reaches a path, because a workspace's guide mirror is
1250
- * derived from it. Fixing the scope and forbidding everything but the bare name
1251
- * after it is what stops a hand-built name from escaping the directory the
1252
- * mirror belongs in.
1253
- */
1254
- export declare const DEPENDENCY_NAME_PATTERN: RegExp;
1178
+ /**
1179
+ * Measure one declared package list against the name and range syntax it accepts.
1180
+ *
1181
+ * @param dependencies - The declared list.
1182
+ * @param field - The blueprint field the list came from, reported on each question.
1183
+ * @param name - The package-name syntax the field accepts.
1184
+ * @param range - The range syntax the field accepts.
1185
+ * @returns One blocking question per rejected name, repeated name, and rejected
1186
+ * range, in list order.
1187
+ *
1188
+ * @remarks
1189
+ * The declared lists and peer partitions differ only in the syntaxes they
1190
+ * accept, so the rules live here once and each caller supplies its own patterns.
1191
+ * A runtime dependency name reaches a path through its guide mirror and is
1192
+ * fixed to the `@orkestrel` scope. A foreign peer or development extra reaches
1193
+ * no path and admits any valid npm name.
1194
+ *
1195
+ * Both patterns must be stateless. A global or sticky pattern carries a
1196
+ * `lastIndex` between calls, so it would answer differently for the same input
1197
+ * depending on what was tested before it.
1198
+ *
1199
+ * @example
1200
+ * ```ts
1201
+ * import { DEPENDENCY_NAME_PATTERN, ORKESTREL_RANGE_PATTERN } from '@orkestrel/scaffold'
1202
+ * import { dependenciesToQuestions } from '@orkestrel/scaffold'
1203
+ *
1204
+ * dependenciesToQuestions(
1205
+ * [{ name: '@orkestrel/router', range: '0.0.8' }],
1206
+ * 'dependencies',
1207
+ * DEPENDENCY_NAME_PATTERN,
1208
+ * ORKESTREL_RANGE_PATTERN,
1209
+ * ).length // 1 — the range is not caret-pinned
1210
+ * ```
1211
+ */
1212
+ export declare function dependenciesToQuestions(dependencies: readonly Dependency[], field: string, name: RegExp, range: RegExp): readonly Question[];
1255
1213
 
1256
- /** The packed-package proof whose presence makes a workspace `distribution`. */
1257
- export declare const DISTRIBUTION_TEST_PATH = "tests/distribution.test.ts";
1214
+ /**
1215
+ * One runtime `@orkestrel/*` dependency of a generated workspace.
1216
+ *
1217
+ * @remarks
1218
+ * `optional` is meaningful only on a blueprint's `peers`, where it emits a
1219
+ * `peerDependenciesMeta` entry beside the peer.
1220
+ */
1221
+ export declare interface Dependency {
1222
+ readonly name: string;
1223
+ readonly range: string;
1224
+ readonly optional?: boolean;
1225
+ }
1258
1226
 
1259
- /**
1260
- * How one target path compares to the artifact planned for it.
1261
- *
1262
- * @remarks
1263
- * `foreign` is a path the plan does not own at all. It is also the set
1264
- * `overwrite` deletes from, narrowed by the paths no verb may remove and by
1265
- * what git tracks.
1266
- */
1267
- export declare type Drift = 'aligned' | 'stale' | 'missing' | 'foreign';
1227
+ /**
1228
+ * The runtime dependency name syntax: the `@orkestrel` scope and a bare name.
1229
+ *
1230
+ * @remarks
1231
+ * A dependency name reaches a path, because a workspace's guide mirror is
1232
+ * derived from it. Fixing the scope and forbidding everything but the bare name
1233
+ * after it is what stops a hand-built name from escaping the directory the
1234
+ * mirror belongs in.
1235
+ */
1236
+ export declare const DEPENDENCY_NAME_PATTERN: RegExp;
1268
1237
 
1269
- /** The minimum-Node engine syntax a blueprint declares. */
1270
- export declare const ENGINES_PATTERN: RegExp;
1238
+ /** The packed-package proof whose presence makes a workspace `distribution`. */
1239
+ export declare const DISTRIBUTION_TEST_PATH = "tests/distribution.test.ts";
1271
1240
 
1272
- /** One environment a generated workspace selects on its `src` or `app` axis. */
1273
- export declare type Environment = 'core' | 'browser' | 'server';
1241
+ /**
1242
+ * How one target path compares to the artifact planned for it.
1243
+ *
1244
+ * @remarks
1245
+ * `foreign` is a path the plan does not own at all. It is also the set
1246
+ * `overwrite` deletes from, narrowed by the paths no verb may remove and by
1247
+ * what git tracks.
1248
+ */
1249
+ export declare type Drift = 'aligned' | 'stale' | 'missing' | 'foreign';
1274
1250
 
1275
- /**
1276
- * The three `Environment` values, frozen.
1277
- *
1278
- * @remarks
1279
- * A blueprint's `src` and `app` axes are caller-supplied, so the gate measures
1280
- * each selection against this list and reports it as the accepted candidates
1281
- * when it rejects one. It is also the key order the per-environment matrices
1282
- * are read in.
1283
- */
1284
- export declare const ENVIRONMENTS: readonly Environment[];
1251
+ /** The minimum-Node engine syntax a blueprint declares. */
1252
+ export declare const ENGINES_PATTERN: RegExp;
1285
1253
 
1286
- /**
1287
- * The vendored paths a target receives with its executable bit set, frozen.
1288
- *
1289
- * @remarks
1290
- * Declared rather than read from the staging host's filesystem, because that
1291
- * reading is not portable: Windows carries no executable bit, so a host staged
1292
- * there reports every file non-executable and every target receives hooks it
1293
- * cannot run. Declaring the set here makes one checkout stage one manifest on
1294
- * every host.
1295
- *
1296
- * Every entry is also a {@link HOST_PATHS} member or sits beneath one. A file
1297
- * that must run when a target invokes it belongs here the moment it is vendored.
1298
- */
1299
- export declare const EXECUTABLE_PATHS: readonly string[];
1254
+ /** One environment a generated workspace selects on its `src` or `app` axis. */
1255
+ export declare type Environment = 'core' | 'browser' | 'server';
1300
1256
 
1301
- /** The registry-only semver subset accepted for a development extra's range. */
1302
- export declare const EXTRA_RANGE_PATTERN: RegExp;
1257
+ /**
1258
+ * The `Environment` values, frozen.
1259
+ *
1260
+ * @remarks
1261
+ * A blueprint's `src` and `app` axes are caller-supplied, so the gate measures
1262
+ * each selection against this list and reports it as the accepted candidates
1263
+ * when it rejects one. It is also the key order the per-environment matrices
1264
+ * are read in.
1265
+ */
1266
+ export declare const ENVIRONMENTS: readonly Environment[];
1303
1267
 
1304
- /**
1305
- * Extract the three numeric components of an exact version.
1306
- *
1307
- * @param version - The candidate version text.
1308
- * @returns The major, minor, and patch numbers, or `undefined` when the text is
1309
- * not the exact three-component syntax.
1310
- *
1311
- * @remarks
1312
- * Deliberately narrow: a prerelease or build suffix is not extracted, because
1313
- * this package compares released versions and refusing to read one is honest
1314
- * where guessing its precedence would not be.
1315
- *
1316
- * @example
1317
- * ```ts
1318
- * import { extractVersion } from '@orkestrel/scaffold'
1319
- *
1320
- * extractVersion('0.0.23') // [0, 0, 23]
1321
- * extractVersion('1.2.3-beta.1') // undefined
1322
- * ```
1323
- */
1324
- export declare function extractVersion(version: string): readonly [major: number, minor: number, patch: number] | undefined;
1268
+ /**
1269
+ * The vendored paths a target receives with its executable bit set, frozen.
1270
+ *
1271
+ * @remarks
1272
+ * Declared rather than read from the staging host's filesystem, because that
1273
+ * reading is not portable: Windows carries no executable bit, so a host staged
1274
+ * there reports every file non-executable and every target receives hooks it
1275
+ * cannot run. Declaring the set here makes one checkout stage one manifest on
1276
+ * every host.
1277
+ *
1278
+ * Every entry is also a {@link HOST_PATHS} member or sits beneath one. A file
1279
+ * that must run when a target invokes it belongs here the moment it is vendored.
1280
+ */
1281
+ export declare const EXECUTABLE_PATHS: readonly string[];
1325
1282
 
1326
- /**
1327
- * One drift verdict against a target path.
1328
- *
1329
- * @remarks
1330
- * `observed` carries the destination's exact bytes and is the precondition the
1331
- * mutation is held to: a write that replaces stale bytes and a deletion that
1332
- * removes a foreign file each fail when the destination no longer matches what
1333
- * the finding recorded. Both therefore require it, and the requirement is in
1334
- * the type rather than in prose, because a deletion that cannot bind to what
1335
- * the audit showed is the one thing the destructive verb must never do. A
1336
- * missing destination has no bytes to record. An aligned one may have gone
1337
- * uncompared, which is what a birth-owned path always does, so it records
1338
- * bytes only where they were actually read. Every planned finding carries its
1339
- * artifact's {@link Ownership}, so a consumer reads what scaffold claims at the
1340
- * path from the finding itself. A foreign finding has no planned artifact and
1341
- * therefore no ownership.
1342
- *
1343
- * Which combinations of `ownership`, `drift`, and `observed` a real audit
1344
- * produces is {@link inferDrift}'s law, not this type's. A birth-owned path is
1345
- * never compared and is always aligned, a presence-owned path compares existence
1346
- * only, and bytes are recorded only where they were read. This shape therefore
1347
- * admits a combination that law never produces — a birth-owned path reported
1348
- * stale is the plainest one — and it admits it deliberately: restating the
1349
- * comparison's case analysis here would be a second copy of it, able to disagree
1350
- * with the one that decides. What closes the gap is the writer: `repair` and
1351
- * `remove` re-derive every verdict themselves and refuse a caller's audit that
1352
- * does not match, so a verdict the comparison could not have reached is refused
1353
- * rather than acted on.
1354
- */
1355
- export declare type Finding = {
1356
- readonly path: string;
1357
- readonly group: Group;
1358
- readonly ownership: Ownership;
1359
- readonly drift: 'stale';
1360
- readonly observed: string;
1361
- } | {
1362
- readonly path: string;
1363
- readonly group: Group;
1364
- readonly ownership?: never;
1365
- readonly drift: 'foreign';
1366
- readonly observed: string;
1367
- } | {
1368
- readonly path: string;
1369
- readonly group: Group;
1370
- readonly ownership: Ownership;
1371
- readonly drift: 'missing';
1372
- readonly observed?: never;
1373
- } | {
1374
- readonly path: string;
1375
- readonly group: Group;
1376
- readonly ownership: Ownership;
1377
- readonly drift: 'aligned';
1378
- readonly observed?: string;
1379
- };
1380
-
1381
- /**
1382
- * The exact three-component floor accepted for a foreign peer's range.
1383
- *
1384
- * @remarks
1385
- * This is independent from {@link ENGINES_PATTERN}. An engine floors the Node
1386
- * runtime a workspace supports, while a peer floors a tool the consumer
1387
- * supplies. Either obligation may change without changing the other.
1388
- */
1389
- export declare const FLOOR_RANGE_PATTERN: RegExp;
1283
+ /** The registry-only semver subset accepted for a development extra's range. */
1284
+ export declare const EXTRA_RANGE_PATTERN: RegExp;
1390
1285
 
1391
- /**
1392
- * The package name syntax for a dependency this package does not publish.
1393
- *
1394
- * @remarks
1395
- * A foreign package is one this package does not publish, so its name reaches
1396
- * no path. It may carry any scope or no scope at all. Each name segment begins
1397
- * with an alphanumeric character after an optional leading `@`, and the name
1398
- * carries at most one `/`. No segment can therefore be `..`, and no backslash
1399
- * is admitted, so the shape cannot express a traversal.
1400
- */
1401
- export declare const FOREIGN_NAME_PATTERN: RegExp;
1286
+ /**
1287
+ * Extract the major, minor, and patch components of an exact version.
1288
+ *
1289
+ * @param version - The candidate version text.
1290
+ * @returns The major, minor, and patch numbers, or `undefined` when the text is
1291
+ * not the exact `major.minor.patch` syntax.
1292
+ *
1293
+ * @remarks
1294
+ * Deliberately narrow: a prerelease or build suffix is not extracted, because
1295
+ * this package compares released versions and refusing to read one is honest
1296
+ * where guessing its precedence would not be.
1297
+ *
1298
+ * @example
1299
+ * ```ts
1300
+ * import { extractVersion } from '@orkestrel/scaffold'
1301
+ *
1302
+ * extractVersion('0.0.23') // [0, 0, 23]
1303
+ * extractVersion('1.2.3-beta.1') // undefined
1304
+ * ```
1305
+ */
1306
+ export declare function extractVersion(version: string): readonly [major: number, minor: number, patch: number] | undefined;
1402
1307
 
1403
- /** The shared Vitest global-setup module whose presence makes a workspace `global`. */
1404
- export declare const GLOBAL_SETUP_PATH = "tests/setupGlobal.ts";
1308
+ /**
1309
+ * One drift verdict against a target path.
1310
+ *
1311
+ * @remarks
1312
+ * `observed` carries the destination's exact bytes and is the precondition the
1313
+ * mutation is held to: a write that replaces stale bytes and a deletion that
1314
+ * removes a foreign file each fail when the destination no longer matches what
1315
+ * the finding recorded. Both therefore require it, and the requirement is in
1316
+ * the type rather than in prose, because a deletion that cannot bind to what
1317
+ * the audit showed is the one thing the destructive verb must never do. A
1318
+ * missing destination has no bytes to record. An aligned one may have gone
1319
+ * uncompared, which is what a birth-owned path always does, so it records
1320
+ * bytes only where they were actually read. Every planned finding carries its
1321
+ * artifact's {@link Ownership}, so a consumer reads what scaffold claims at the
1322
+ * path from the finding itself. A foreign finding has no planned artifact and
1323
+ * therefore no ownership.
1324
+ *
1325
+ * Which combinations of `ownership`, `drift`, and `observed` a real audit
1326
+ * produces is {@link inferDrift}'s law, not this type's. A birth-owned path is
1327
+ * never compared and is always aligned, a presence-owned path compares existence
1328
+ * only, and bytes are recorded only where they were read. This shape therefore
1329
+ * admits a combination that law never produces — a birth-owned path reported
1330
+ * stale is the plainest one — and it admits it deliberately: restating the
1331
+ * comparison's case analysis here would be a second copy of it, able to disagree
1332
+ * with the one that decides. What closes the gap is the writer: `repair` and
1333
+ * `remove` re-derive every verdict themselves and refuse a caller's audit that
1334
+ * does not match, so a verdict the comparison could not have reached is refused
1335
+ * rather than acted on.
1336
+ */
1337
+ export declare type Finding = {
1338
+ readonly path: string;
1339
+ readonly group: Group;
1340
+ readonly ownership: Ownership;
1341
+ readonly drift: 'stale';
1342
+ readonly observed: string;
1343
+ } | {
1344
+ readonly path: string;
1345
+ readonly group: Group;
1346
+ readonly ownership?: never;
1347
+ readonly drift: 'foreign';
1348
+ readonly observed: string;
1349
+ } | {
1350
+ readonly path: string;
1351
+ readonly group: Group;
1352
+ readonly ownership: Ownership;
1353
+ readonly drift: 'missing';
1354
+ readonly observed?: never;
1355
+ } | {
1356
+ readonly path: string;
1357
+ readonly group: Group;
1358
+ readonly ownership: Ownership;
1359
+ readonly drift: 'aligned';
1360
+ readonly observed?: string;
1361
+ };
1405
1362
 
1406
- /** The artifact group a plan selects over. */
1407
- export declare type Group = 'manifest' | 'configs' | 'source' | 'tests' | 'guides' | 'docs' | 'orchestration';
1363
+ /**
1364
+ * The exact `major.minor.patch` floor accepted for a foreign peer's range.
1365
+ *
1366
+ * @remarks
1367
+ * This is independent from {@link ENGINES_PATTERN}. An engine floors the Node
1368
+ * runtime a workspace supports, while a peer floors a tool the consumer
1369
+ * supplies. Either obligation may change without changing the other.
1370
+ */
1371
+ export declare const FLOOR_RANGE_PATTERN: RegExp;
1408
1372
 
1409
- /**
1410
- * The seven `Group` values in plan order, frozen.
1411
- *
1412
- * @remarks
1413
- * A compile that names no groups covers every one of them, so this list is the
1414
- * default selection as well as the accepted candidates for a rejected one. The
1415
- * order is the order a plan lists its artifacts in.
1416
- */
1417
- export declare const GROUPS: readonly Group[];
1373
+ /**
1374
+ * The package name syntax for a dependency this package does not publish.
1375
+ *
1376
+ * @remarks
1377
+ * A foreign package is one this package does not publish, so its name reaches
1378
+ * no path. It may carry any scope or no scope at all. Each name segment begins
1379
+ * with an alphanumeric character after an optional leading `@`, and the name
1380
+ * carries at most one `/`. No segment can therefore be `..`, and no backslash
1381
+ * is admitted, so the shape cannot express a traversal.
1382
+ */
1383
+ export declare const FOREIGN_NAME_PATTERN: RegExp;
1418
1384
 
1419
- /** The guide-parity proof whose presence selects the planned `guides` project. */
1420
- export declare const GUIDES_TEST_PATH = "tests/guides.test.ts";
1385
+ /** The shared Vitest global-setup module whose presence makes a workspace `global`. */
1386
+ export declare const GLOBAL_SETUP_PATH = "tests/setupGlobal.ts";
1421
1387
 
1422
- /** Exact lowercase hexadecimal bytes: two digits per byte, and empty content is valid. */
1423
- export declare const HEX_PATTERN: RegExp;
1388
+ /** The artifact group a plan selects over. */
1389
+ export declare type Group = 'manifest' | 'configs' | 'source' | 'tests' | 'guides' | 'docs' | 'orchestration';
1424
1390
 
1425
- /**
1426
- * The paths byte-copied from the vendored data root, frozen.
1427
- *
1428
- * @remarks
1429
- * These are the files the fleet shares verbatim: the root instruction
1430
- * documents, the licence, the canonical orchestration contract every harness
1431
- * bridge points at, the four harness directories, the session hook scripts,
1432
- * the shared policy register, the byte-identical root dotfiles, and the two
1433
- * guide mirrors a generated workspace starts from. A directory entry vendors
1434
- * everything beneath it.
1435
- *
1436
- * A plan carries the subset its target selects, which is why the list is a
1437
- * candidate set rather than a plan: a workspace never mirrors its own guide.
1438
- */
1439
- export declare const HOST_PATHS: readonly string[];
1391
+ /**
1392
+ * The `Group` values in plan order, frozen.
1393
+ *
1394
+ * @remarks
1395
+ * A compile that names no groups covers every one of them, so this list is the
1396
+ * default selection as well as the accepted candidates for a rejected one. The
1397
+ * order is the order a plan lists its artifacts in.
1398
+ */
1399
+ export declare const GROUPS: readonly Group[];
1440
1400
 
1441
- /**
1442
- * A file byte-copied from the vendored data root, planned before its bytes are read.
1443
- *
1444
- * @remarks
1445
- * `source` falls back to `path` when absent. The pure core face cannot read the
1446
- * vendored root, so a plan it compiles alone claims only that these files
1447
- * exist. That is why the ownership here is narrowed away from `content`: a
1448
- * claim over bytes nobody has read is a claim that cannot be checked. Reading
1449
- * the vendored root turns the ones scaffold owns the bytes of into
1450
- * {@link HydratedArtifact}. Workspace-owned paths and paths whose bytes belong
1451
- * to another verb stay plain host artifacts, because this writer claims only
1452
- * their presence.
1453
- */
1454
- export declare interface HostArtifact extends ArtifactBase {
1455
- readonly origin: 'host';
1456
- readonly ownership: 'presence' | 'birth';
1457
- readonly source?: string;
1458
- readonly hex?: never;
1459
- readonly content?: never;
1460
- }
1461
-
1462
- /**
1463
- * A vendored file whose exact bytes have been read, so its content can be compared.
1464
- *
1465
- * @remarks
1466
- * `hex` is the canonical lowercase byte pairs of the vendored source. It is
1467
- * required, which is what makes content ownership honest: every artifact
1468
- * claiming a byte comparison carries the bytes that comparison needs. Hydration
1469
- * leaves a guide-mirror pointer and the catalog agent as plain host artifacts,
1470
- * because another verb owns those bytes.
1471
- */
1472
- export declare interface HydratedArtifact extends ArtifactBase {
1473
- readonly origin: 'host';
1474
- readonly ownership: 'content';
1475
- readonly source?: string;
1476
- readonly hex: string;
1477
- readonly content?: never;
1478
- }
1479
-
1480
- /**
1481
- * Infer how one target path compares to the artifact planned for it.
1482
- *
1483
- * @param artifact - The planned artifact.
1484
- * @param observed - The destination's exact bytes as hexadecimal; absent when
1485
- * the destination holds no file.
1486
- * @returns `aligned`, `stale`, or `missing`.
1487
- *
1488
- * @remarks
1489
- * Ownership decides the comparison and nothing else does. A `birth`-owned
1490
- * artifact is never compared and is always aligned, so a file the workspace has
1491
- * outgrown is never reported as drift. A `presence`-owned artifact compares
1492
- * existence only. A `content`-owned artifact compares bytes, and it always
1493
- * carries the bytes to compare, so the comparison can always be made.
1494
- *
1495
- * `foreign` is not answerable here: it describes a path the plan does not own,
1496
- * so no artifact exists to pass in.
1497
- *
1498
- * @example
1499
- * ```ts
1500
- * import type { Artifact } from '@orkestrel/scaffold'
1501
- * import { inferDrift } from '@orkestrel/scaffold'
1502
- *
1503
- * const artifact: Artifact = {
1504
- * path: 'README.md',
1505
- * group: 'docs',
1506
- * ownership: 'content',
1507
- * origin: 'computed',
1508
- * content: 'hi\n',
1509
- * }
1510
- *
1511
- * inferDrift(artifact, '68690a') // 'aligned'
1512
- * inferDrift(artifact, '6279650a') // 'stale'
1513
- * inferDrift(artifact) // 'missing'
1514
- * ```
1515
- */
1516
- export declare function inferDrift(artifact: Artifact, observed?: string): Exclude<Drift, 'foreign'>;
1401
+ /** The guide-parity proof whose presence selects the planned `guides` project. */
1402
+ export declare const GUIDES_TEST_PATH = "tests/guides.test.ts";
1517
1403
 
1518
- /**
1519
- * Infer the {@link Group} a path belongs to.
1520
- *
1521
- * @param path - The target-relative path to classify.
1522
- * @returns The group that owns the path.
1523
- *
1524
- * @remarks
1525
- * A path is grouped by what it governs rather than by where it sits. The two
1526
- * manifest files are named exactly; anything
1527
- * {@link matchesOrchestrationPath} accepts is orchestration; `src` and `app`
1528
- * are source; `tests`, `guides`, and `docs` carry their own names; the licence
1529
- * and a root Markdown document are docs; and everything else is configuration.
1530
- * A vendored path and a foreign path found in a target are classified here
1531
- * alike, so the plan and the audit never disagree about what a path is.
1532
- *
1533
- * @example
1534
- * ```ts
1535
- * import { inferGroup } from '@orkestrel/scaffold'
1536
- *
1537
- * inferGroup('src/core/index.ts') // 'source'
1538
- * inferGroup('AGENTS.md') // 'docs'
1539
- * inferGroup('.editorconfig') // 'configs'
1540
- * ```
1541
- */
1542
- export declare function inferGroup(path: string): Group;
1404
+ /** Exact lowercase hexadecimal bytes: two digits per byte, and empty content is valid. */
1405
+ export declare const HEX_PATTERN: RegExp;
1543
1406
 
1544
- /** The cross-environment composition proof whose presence makes a workspace `integration`. */
1545
- export declare const INTEGRATION_TEST_PATH = "tests/integration.test.ts";
1407
+ /**
1408
+ * The paths byte-copied from the vendored data root, frozen.
1409
+ *
1410
+ * @remarks
1411
+ * These are the files the fleet shares verbatim: the root instruction
1412
+ * documents, the licence, the canonical orchestration contract every harness
1413
+ * bridge points at, the harness directories, the session hook scripts,
1414
+ * the shared policy register, the byte-identical root dotfiles, and the
1415
+ * guide mirrors a generated workspace starts from. A directory entry vendors
1416
+ * everything beneath it.
1417
+ *
1418
+ * A plan carries the subset its target selects, which is why the list is a
1419
+ * candidate set rather than a plan: a workspace never mirrors its own guide.
1420
+ */
1421
+ export declare const HOST_PATHS: readonly string[];
1546
1422
 
1547
- /** Visible characters a target-relative path and a Markdown path cell both forbid. */
1548
- export declare const INVALID_PATH_CHARACTER_PATTERN: RegExp;
1423
+ /**
1424
+ * A file byte-copied from the vendored data root, planned before its bytes are read.
1425
+ *
1426
+ * @remarks
1427
+ * `source` falls back to `path` when absent. The pure core face cannot read the
1428
+ * vendored root, so a plan it compiles alone claims only that these files
1429
+ * exist. That is why the ownership here is narrowed away from `content`: a
1430
+ * claim over bytes nobody has read is a claim that cannot be checked. Reading
1431
+ * the vendored root turns the ones scaffold owns the bytes of into
1432
+ * {@link HydratedArtifact}. Workspace-owned paths and paths whose bytes belong
1433
+ * to another verb stay plain host artifacts, because this writer claims only
1434
+ * their presence.
1435
+ */
1436
+ export declare interface HostArtifact extends ArtifactBase {
1437
+ readonly origin: 'host';
1438
+ readonly ownership: 'presence' | 'birth';
1439
+ readonly source?: string;
1440
+ readonly hex?: never;
1441
+ readonly content?: never;
1442
+ }
1549
1443
 
1550
- /**
1551
- * Narrow a value to an {@link Artifact}.
1552
- *
1553
- * @remarks
1554
- * One branch per way content is produced, discriminated by `origin` and
1555
- * narrowed by `ownership`. Each branch declares only the keys its branch has,
1556
- * so a host artifact carrying `content`, a hydrated artifact carrying anything
1557
- * but `content` ownership, and a template artifact carrying `hex` are all
1558
- * refused rather than admitted to the wrong branch.
1559
- *
1560
- * @example
1561
- * ```ts
1562
- * import { isArtifact } from '@orkestrel/scaffold'
1563
- *
1564
- * isArtifact({ path: 'AGENTS.md', group: 'docs', ownership: 'presence', origin: 'host' }) // true
1565
- * ```
1566
- */
1567
- export declare const isArtifact: Guard<Artifact>;
1444
+ /**
1445
+ * A vendored file whose exact bytes have been read, so its content can be compared.
1446
+ *
1447
+ * @remarks
1448
+ * `hex` is the canonical lowercase byte pairs of the vendored source. It is
1449
+ * required, which is what makes content ownership honest: every artifact
1450
+ * claiming a byte comparison carries the bytes that comparison needs. Hydration
1451
+ * leaves a guide-mirror pointer and the catalog agent as plain host artifacts,
1452
+ * because another verb owns those bytes.
1453
+ */
1454
+ export declare interface HydratedArtifact extends ArtifactBase {
1455
+ readonly origin: 'host';
1456
+ readonly ownership: 'content';
1457
+ readonly source?: string;
1458
+ readonly hex: string;
1459
+ readonly content?: never;
1460
+ }
1568
1461
 
1569
- /**
1570
- * Narrow a value to an {@link Audit}.
1571
- *
1572
- * @remarks
1573
- * An audit reaches the writer and the destructive verb, so it is guarded as
1574
- * strictly as the plan beside it. Findings use the sum of the two producer
1575
- * bounds: one per planned artifact, then one per unplanned snapshot path.
1576
- */
1577
- export declare const isAudit: Guard<Audit>;
1462
+ /**
1463
+ * Infer how one target path compares to the artifact planned for it.
1464
+ *
1465
+ * @param artifact - The planned artifact.
1466
+ * @param observed - The destination's exact bytes as hexadecimal; absent when
1467
+ * the destination holds no file.
1468
+ * @returns `aligned`, `stale`, or `missing`.
1469
+ *
1470
+ * @remarks
1471
+ * Ownership decides the comparison and nothing else does. A `birth`-owned
1472
+ * artifact is never compared and is always aligned, so a file the workspace has
1473
+ * outgrown is never reported as drift. A `presence`-owned artifact compares
1474
+ * existence only. A `content`-owned artifact compares bytes, and it always
1475
+ * carries the bytes to compare, so the comparison can always be made.
1476
+ *
1477
+ * `foreign` is not answerable here: it describes a path the plan does not own,
1478
+ * so no artifact exists to pass in.
1479
+ *
1480
+ * @example
1481
+ * ```ts
1482
+ * import type { Artifact } from '@orkestrel/scaffold'
1483
+ * import { inferDrift } from '@orkestrel/scaffold'
1484
+ *
1485
+ * const artifact: Artifact = {
1486
+ * path: 'README.md',
1487
+ * group: 'docs',
1488
+ * ownership: 'content',
1489
+ * origin: 'computed',
1490
+ * content: 'hi\n',
1491
+ * }
1492
+ *
1493
+ * inferDrift(artifact, '68690a') // 'aligned'
1494
+ * inferDrift(artifact, '6279650a') // 'stale'
1495
+ * inferDrift(artifact) // 'missing'
1496
+ * ```
1497
+ */
1498
+ export declare function inferDrift(artifact: Artifact, observed?: string): Exclude<Drift, 'foreign'>;
1578
1499
 
1579
- /**
1580
- * Narrow a value to a {@link Blueprint}.
1581
- *
1582
- * @remarks
1583
- * The whole closed record, its literal axes, and the count and length bounds
1584
- * this package admits. The syntactic laws over a name, a version, a range, and
1585
- * an engines floor stay with the gate, which reports each one as a
1586
- * {@link Question} instead of refusing the value outright.
1587
- *
1588
- * @example
1589
- * ```ts
1590
- * import { isBlueprint } from '@orkestrel/scaffold'
1591
- *
1592
- * isBlueprint({ name: 'router', src: ['core'] }) // false not the whole record
1593
- * ```
1594
- */
1595
- export declare const isBlueprint: Guard<Blueprint>;
1500
+ /**
1501
+ * Infer the {@link Group} a path belongs to.
1502
+ *
1503
+ * @param path - The target-relative path to classify.
1504
+ * @returns The group that owns the path.
1505
+ *
1506
+ * @remarks
1507
+ * A path is grouped by what it governs rather than by where it sits. The
1508
+ * manifest files are named exactly; anything
1509
+ * {@link matchesOrchestrationPath} accepts is orchestration; `src` and `app`
1510
+ * are source; `tests`, `guides`, and `docs` carry their own names; the licence
1511
+ * and a root Markdown document are docs; and everything else is configuration.
1512
+ * A vendored path and a foreign path found in a target are classified here
1513
+ * alike, so the plan and the audit never disagree about what a path is.
1514
+ *
1515
+ * @example
1516
+ * ```ts
1517
+ * import { inferGroup } from '@orkestrel/scaffold'
1518
+ *
1519
+ * inferGroup('src/core/index.ts') // 'source'
1520
+ * inferGroup('AGENTS.md') // 'docs'
1521
+ * inferGroup('.editorconfig') // 'configs'
1522
+ * ```
1523
+ */
1524
+ export declare function inferGroup(path: string): Group;
1596
1525
 
1597
- /**
1598
- * Narrow a value to a {@link CatalogEntry}.
1599
- *
1600
- * @remarks
1601
- * A row that found no version carries the cause instead, and neither branch may
1602
- * carry the other's field.
1603
- */
1604
- export declare const isCatalogEntry: Guard<CatalogEntry>;
1526
+ /** The cross-environment composition proof whose presence makes a workspace `integration`. */
1527
+ export declare const INTEGRATION_TEST_PATH = "tests/integration.test.ts";
1605
1528
 
1606
- /**
1607
- * Narrow a value to an array within the limit one public collection accepts.
1608
- *
1609
- * @param value - The candidate collection.
1610
- * @returns `true` for an array of no more than `MAX_COLLECTION_ITEMS` items.
1611
- *
1612
- * @remarks
1613
- * Compose this ahead of an element guard so the item count is settled before
1614
- * anything walks the items. A hostile `length` accessor answers `false` here
1615
- * rather than escaping as a thrown error.
1616
- *
1617
- * @example
1618
- * ```ts
1619
- * import { isCollection } from '@orkestrel/scaffold'
1620
- *
1621
- * isCollection(['manifest']) // true
1622
- * isCollection('manifest') // false
1623
- * ```
1624
- */
1625
- export declare function isCollection(value: unknown): value is readonly unknown[];
1529
+ /** Visible characters a target-relative path and a Markdown path cell both forbid. */
1530
+ export declare const INVALID_PATH_CHARACTER_PATTERN: RegExp;
1626
1531
 
1627
- /**
1628
- * Narrow a value to the compiler's initial listener record.
1629
- *
1630
- * @remarks
1631
- * Every event is optional and every declared value is a function. A key outside
1632
- * the compiler's event map is refused, so a listener wired to a misspelled
1633
- * event fails at construction instead of never firing.
1634
- */
1635
- export declare const isCompilerHooks: Guard<EmitterHooks<CompilerEventMap>>;
1532
+ /**
1533
+ * Narrow a value to an {@link Artifact}.
1534
+ *
1535
+ * @remarks
1536
+ * One branch per way content is produced, discriminated by `origin` and
1537
+ * narrowed by `ownership`. Each branch declares only the keys its branch has,
1538
+ * so a host artifact carrying `content`, a hydrated artifact carrying anything
1539
+ * but `content` ownership, and a template artifact carrying `hex` are all
1540
+ * refused rather than admitted to the wrong branch.
1541
+ *
1542
+ * @example
1543
+ * ```ts
1544
+ * import { isArtifact } from '@orkestrel/scaffold'
1545
+ *
1546
+ * isArtifact({ path: 'AGENTS.md', group: 'docs', ownership: 'presence', origin: 'host' }) // true
1547
+ * ```
1548
+ */
1549
+ export declare const isArtifact: Guard<Artifact>;
1636
1550
 
1637
- /**
1638
- * Narrow a value to {@link CompilerOptions}.
1639
- *
1640
- * @example
1641
- * ```ts
1642
- * import { isCompilerOptions } from '@orkestrel/scaffold'
1643
- *
1644
- * isCompilerOptions({}) // true
1645
- * isCompilerOptions({ retries: 2 }) // false
1646
- * ```
1647
- */
1648
- export declare const isCompilerOptions: Guard<CompilerOptions>;
1551
+ /**
1552
+ * Narrow a value to an {@link Audit}.
1553
+ *
1554
+ * @remarks
1555
+ * An audit reaches the writer and the destructive verb, so it is guarded as
1556
+ * strictly as the plan beside it. Findings use the sum of the producer
1557
+ * bounds: one per planned artifact, then one per unplanned snapshot path.
1558
+ */
1559
+ export declare const isAudit: Guard<Audit>;
1649
1560
 
1650
- /**
1651
- * Narrow a value to text this package will accept as one artifact's content.
1652
- *
1653
- * @remarks
1654
- * The bound is a code-unit ceiling rather than a byte count, because a string
1655
- * of more code units than {@link MAX_ARTIFACT_BYTES} cannot encode within that
1656
- * budget under any encoding this package writes. The exact UTF-8 measurement
1657
- * belongs to the compiler and the writer, which are the two places the bytes
1658
- * are actually produced.
1659
- */
1660
- export declare const isContent: Guard<string>;
1561
+ /**
1562
+ * Narrow a value to a {@link Blueprint}.
1563
+ *
1564
+ * @remarks
1565
+ * The whole closed record, its literal axes, and the count and length bounds
1566
+ * this package admits. The syntactic laws over a name, a version, a range, and
1567
+ * an engines floor stay with the gate, which reports each one as a
1568
+ * {@link Question} instead of refusing the value outright.
1569
+ *
1570
+ * @example
1571
+ * ```ts
1572
+ * import { isBlueprint } from '@orkestrel/scaffold'
1573
+ *
1574
+ * isBlueprint({ name: 'router', src: ['core'] }) // false — not the whole record
1575
+ * ```
1576
+ */
1577
+ export declare const isBlueprint: Guard<Blueprint>;
1661
1578
 
1662
- /**
1663
- * Narrow a value to a {@link Dependency}.
1664
- *
1665
- * @remarks
1666
- * Structural and bounded: which names and ranges a blueprint may declare is a
1667
- * gate law, reported as a {@link Question} carrying its accepted candidates, so
1668
- * refusing it here would replace an answerable question with a bare `false`.
1669
- *
1670
- * @example
1671
- * ```ts
1672
- * import { isDependency } from '@orkestrel/scaffold'
1673
- *
1674
- * isDependency({ name: '@orkestrel/emitter', range: '^0.0.5' }) // true
1675
- * isDependency({ name: '@orkestrel/emitter' }) // false
1676
- * ```
1677
- */
1678
- export declare const isDependency: Guard<Dependency>;
1579
+ /**
1580
+ * Narrow a value to a {@link CatalogEntry}.
1581
+ *
1582
+ * @remarks
1583
+ * A row that found no version carries the cause instead, and neither branch may
1584
+ * carry the other's field.
1585
+ */
1586
+ export declare const isCatalogEntry: Guard<CatalogEntry>;
1679
1587
 
1680
- /**
1681
- * Narrow a value to the scoped package name a runtime dependency carries.
1682
- *
1683
- * @remarks
1684
- * A dependency name reaches a path, because a workspace's guide mirror is
1685
- * derived from it. Fixing the scope and admitting nothing but a bare name after
1686
- * it is what keeps that derivation inside the directory the mirror belongs in.
1687
- * A blueprint's development extras are deliberately wider and are measured by
1688
- * the gate instead, which is why {@link isDependency} does not apply this.
1689
- *
1690
- * @example
1691
- * ```ts
1692
- * import { isDependencyName } from '@orkestrel/scaffold'
1693
- *
1694
- * isDependencyName('@orkestrel/router') // true
1695
- * isDependencyName('@orkestrel/../etc') // false
1696
- * ```
1697
- */
1698
- export declare const isDependencyName: Guard<string>;
1588
+ /**
1589
+ * Narrow a value to an array within the limit one public collection accepts.
1590
+ *
1591
+ * @param value - The candidate collection.
1592
+ * @returns `true` for an array of no more than `MAX_COLLECTION_ITEMS` items.
1593
+ *
1594
+ * @remarks
1595
+ * Compose this ahead of an element guard so the item count is settled before
1596
+ * anything walks the items. A hostile `length` accessor answers `false` here
1597
+ * rather than escaping as a thrown error.
1598
+ *
1599
+ * @example
1600
+ * ```ts
1601
+ * import { isCollection } from '@orkestrel/scaffold'
1602
+ *
1603
+ * isCollection(['manifest']) // true
1604
+ * isCollection('manifest') // false
1605
+ * ```
1606
+ */
1607
+ export declare function isCollection(value: unknown): value is readonly unknown[];
1699
1608
 
1700
- /**
1701
- * Narrow a value to one {@link Environment} a workspace may select.
1702
- *
1703
- * @example
1704
- * ```ts
1705
- * import { isEnvironment } from '@orkestrel/scaffold'
1706
- *
1707
- * isEnvironment('browser') // true
1708
- * isEnvironment('worker') // false
1709
- * ```
1710
- */
1711
- export declare const isEnvironment: Guard<Environment>;
1609
+ /**
1610
+ * Narrow a value to the compiler's initial listener record.
1611
+ *
1612
+ * @remarks
1613
+ * Every event is optional and every declared value is a function. A key outside
1614
+ * the compiler's event map is refused, so a listener wired to a misspelled
1615
+ * event fails at construction instead of never firing.
1616
+ */
1617
+ export declare const isCompilerHooks: Guard<EmitterHooks<CompilerEventMap>>;
1712
1618
 
1713
- /**
1714
- * Narrow a value to a {@link Finding}.
1715
- *
1716
- * @remarks
1717
- * `observed` is required exactly where the mutation it precedes is held to it,
1718
- * absent where the destination had no bytes to record, and optional where the
1719
- * comparison may not have been made. Planned findings require `ownership`;
1720
- * foreign findings forbid it because no artifact was planned for their path.
1721
- *
1722
- * That is the whole claim. This guard proves the shape a reader may destructure
1723
- * and nothing about whether the verdict is one an audit could have reached: the
1724
- * correlation between `ownership`, `drift`, and `observed` belongs to
1725
- * {@link inferDrift}, and it is re-derived at the verb that acts on the finding.
1726
- */
1727
- export declare const isFinding: Guard<Finding>;
1619
+ /**
1620
+ * Narrow a value to {@link CompilerOptions}.
1621
+ *
1622
+ * @example
1623
+ * ```ts
1624
+ * import { isCompilerOptions } from '@orkestrel/scaffold'
1625
+ *
1626
+ * isCompilerOptions({}) // true
1627
+ * isCompilerOptions({ retries: 2 }) // false
1628
+ * ```
1629
+ */
1630
+ export declare const isCompilerOptions: Guard<CompilerOptions>;
1728
1631
 
1729
- /**
1730
- * Narrow a value to one {@link Group} a plan selects over.
1731
- *
1732
- * @example
1733
- * ```ts
1734
- * import { isGroup } from '@orkestrel/scaffold'
1735
- *
1736
- * isGroup('manifest') // true
1737
- * isGroup('readme') // false
1738
- * ```
1739
- */
1740
- export declare const isGroup: Guard<Group>;
1632
+ /**
1633
+ * Narrow a value to text this package will accept as one artifact's content.
1634
+ *
1635
+ * @remarks
1636
+ * The bound is a code-unit ceiling rather than a byte count, because a string
1637
+ * of more code units than {@link MAX_ARTIFACT_BYTES} cannot encode within that
1638
+ * budget under any encoding this package writes. The exact UTF-8 measurement
1639
+ * belongs to the compiler and the writer, which are the places the bytes
1640
+ * are actually produced.
1641
+ */
1642
+ export declare const isContent: Guard<string>;
1741
1643
 
1742
- /** Narrow a value to a bounded group selection. */
1743
- export declare const isGroups: Guard<readonly Group[]>;
1644
+ /**
1645
+ * Narrow a value to a {@link Dependency}.
1646
+ *
1647
+ * @remarks
1648
+ * Structural and bounded: which names and ranges a blueprint may declare is a
1649
+ * gate law, reported as a {@link Question} carrying its accepted candidates, so
1650
+ * refusing it here would replace an answerable question with a bare `false`.
1651
+ *
1652
+ * @example
1653
+ * ```ts
1654
+ * import { isDependency } from '@orkestrel/scaffold'
1655
+ *
1656
+ * isDependency({ name: '@orkestrel/emitter', range: '^0.0.5' }) // true
1657
+ * isDependency({ name: '@orkestrel/emitter' }) // false
1658
+ * ```
1659
+ */
1660
+ export declare const isDependency: Guard<Dependency>;
1744
1661
 
1745
- /**
1746
- * Narrow a value to exact lowercase hexadecimal bytes within one artifact's limit.
1747
- *
1748
- * @remarks
1749
- * Two digits per byte, so an odd length is refused and empty content is valid.
1750
- * The bound is exact rather than approximate: the encoding is ASCII, so the
1751
- * string's length is twice the byte count it stands for.
1752
- *
1753
- * @example
1754
- * ```ts
1755
- * import { isHex } from '@orkestrel/scaffold'
1756
- *
1757
- * isHex('68690a') // true
1758
- * isHex('68690A') // false
1759
- * ```
1760
- */
1761
- export declare const isHex: Guard<string>;
1662
+ /**
1663
+ * Narrow a value to the scoped package name a runtime dependency carries.
1664
+ *
1665
+ * @remarks
1666
+ * A dependency name reaches a path, because a workspace's guide mirror is
1667
+ * derived from it. Fixing the scope and admitting nothing but a bare name after
1668
+ * it is what keeps that derivation inside the directory the mirror belongs in.
1669
+ * A blueprint's development extras are deliberately wider and are measured by
1670
+ * the gate instead, which is why {@link isDependency} does not apply this.
1671
+ *
1672
+ * @example
1673
+ * ```ts
1674
+ * import { isDependencyName } from '@orkestrel/scaffold'
1675
+ *
1676
+ * isDependencyName('@orkestrel/router') // true
1677
+ * isDependencyName('@orkestrel/../etc') // false
1678
+ * ```
1679
+ */
1680
+ export declare const isDependencyName: Guard<string>;
1762
1681
 
1763
- /**
1764
- * Narrow a value to a {@link Mirror}.
1765
- *
1766
- * @remarks
1767
- * `content` is the fetched guide text and `observed` is the local mirror's
1768
- * exact bytes, so the two carry different laws: one is content this package
1769
- * writes, the other is the precondition that write is held to.
1770
- */
1771
- export declare const isMirror: Guard<Mirror>;
1682
+ /**
1683
+ * Narrow a value to one {@link Environment} a workspace may select.
1684
+ *
1685
+ * @example
1686
+ * ```ts
1687
+ * import { isEnvironment } from '@orkestrel/scaffold'
1688
+ *
1689
+ * isEnvironment('browser') // true
1690
+ * isEnvironment('worker') // false
1691
+ * ```
1692
+ */
1693
+ export declare const isEnvironment: Guard<Environment>;
1772
1694
 
1773
- /**
1774
- * Narrow a value to an {@link Override}.
1775
- *
1776
- * @remarks
1777
- * Whether the path names a planned artifact is a gate law; whether it names a
1778
- * destination at all is this guard's.
1779
- *
1780
- * @example
1781
- * ```ts
1782
- * import { isOverride } from '@orkestrel/scaffold'
1783
- *
1784
- * isOverride({ path: 'README.md', content: '# Title\n' }) // true
1785
- * ```
1786
- */
1787
- export declare const isOverride: Guard<Override>;
1695
+ /**
1696
+ * Narrow a value to a {@link Finding}.
1697
+ *
1698
+ * @remarks
1699
+ * `observed` is required exactly where the mutation it precedes is held to it,
1700
+ * absent where the destination had no bytes to record, and optional where the
1701
+ * comparison may not have been made. Planned findings require `ownership`;
1702
+ * foreign findings forbid it because no artifact was planned for their path.
1703
+ *
1704
+ * That is the whole claim. This guard proves the shape a reader may destructure
1705
+ * and nothing about whether the verdict is one an audit could have reached: the
1706
+ * correlation between `ownership`, `drift`, and `observed` belongs to
1707
+ * {@link inferDrift}, and it is re-derived at the verb that acts on the finding.
1708
+ */
1709
+ export declare const isFinding: Guard<Finding>;
1788
1710
 
1789
- /**
1790
- * Narrow a value to a logical target-relative path.
1791
- *
1792
- * @param value - The candidate path.
1793
- * @returns `true` for a bounded relative path with no traversal, empty segment,
1794
- * control character, or reserved syntax character.
1795
- *
1796
- * @remarks
1797
- * Every path this package reads or writes passes here, so one law covers a
1798
- * planned artifact, an override target, an audit finding, a guide mirror, and a
1799
- * snapshot key. Rejecting `..`, a leading `/`, and a backslash at the guard is
1800
- * what stops a caller-supplied path from naming a destination outside the
1801
- * target. Host-location validation is a separate server boundary: this guard
1802
- * does not reject a device spelling, a trailing dot or space, or a segment that
1803
- * exceeds a host filesystem's byte ceiling.
1804
- *
1805
- * @example
1806
- * ```ts
1807
- * import { isPath } from '@orkestrel/scaffold'
1808
- *
1809
- * isPath('configs/src/tsconfig.core.json') // true
1810
- * isPath('../secrets') // false
1811
- * ```
1812
- */
1813
- export declare function isPath(value: unknown): value is string;
1711
+ /**
1712
+ * Narrow a value to one {@link Group} a plan selects over.
1713
+ *
1714
+ * @example
1715
+ * ```ts
1716
+ * import { isGroup } from '@orkestrel/scaffold'
1717
+ *
1718
+ * isGroup('manifest') // true
1719
+ * isGroup('readme') // false
1720
+ * ```
1721
+ */
1722
+ export declare const isGroup: Guard<Group>;
1814
1723
 
1815
- /**
1816
- * Narrow a value to a {@link Plan}.
1817
- *
1818
- * @remarks
1819
- * A plan reaches the writer, and the writer has no question channel, so this
1820
- * carries the whole law of the value: every artifact path, every claimed byte,
1821
- * and the blueprint it was compiled from.
1822
- */
1823
- export declare const isPlan: Guard<Plan>;
1724
+ /** Narrow a value to a bounded group selection. */
1725
+ export declare const isGroups: Guard<readonly Group[]>;
1824
1726
 
1825
- /**
1826
- * Narrow a value to a {@link Question}.
1827
- *
1828
- * @example
1829
- * ```ts
1830
- * import { isQuestion } from '@orkestrel/scaffold'
1831
- *
1832
- * isQuestion({ field: 'src', message: 'Unknown environment', blocking: true }) // true
1833
- * ```
1834
- */
1835
- export declare const isQuestion: Guard<Question>;
1727
+ /**
1728
+ * Narrow a value to exact lowercase hexadecimal bytes within one artifact's limit.
1729
+ *
1730
+ * @remarks
1731
+ * Two digits per byte, so an odd length is refused and empty content is valid.
1732
+ * The bound is exact rather than approximate: the encoding is ASCII, so the
1733
+ * string's length is twice the byte count it stands for.
1734
+ *
1735
+ * @example
1736
+ * ```ts
1737
+ * import { isHex } from '@orkestrel/scaffold'
1738
+ *
1739
+ * isHex('68690a') // true
1740
+ * isHex('68690A') // false
1741
+ * ```
1742
+ */
1743
+ export declare const isHex: Guard<string>;
1836
1744
 
1837
- /**
1838
- * Narrow a caught value to a {@link ScaffoldError}.
1839
- *
1840
- * @param value - The caught value to narrow.
1841
- * @returns `true` when `value` is a {@link ScaffoldError}.
1842
- *
1843
- * @example
1844
- * ```ts
1845
- * import { isScaffoldError } from '@orkestrel/scaffold'
1846
- *
1847
- * isScaffoldError(new Error('plain')) // false
1848
- * isScaffoldError(undefined) // false
1849
- * ```
1850
- */
1851
- export declare function isScaffoldError(value: unknown): value is ScaffoldError;
1745
+ /**
1746
+ * Narrow a value to a {@link Mirror}.
1747
+ *
1748
+ * @remarks
1749
+ * `content` is the fetched guide text and `observed` is the local mirror's
1750
+ * exact bytes, so they carry different laws: one is content this package
1751
+ * writes, the other is the precondition that write is held to.
1752
+ */
1753
+ export declare const isMirror: Guard<Mirror>;
1852
1754
 
1853
- /**
1854
- * Narrow a value to a {@link Snapshot}.
1855
- *
1856
- * @param value - The candidate target snapshot.
1857
- * @returns `true` for a bounded plain record whose every key is a path and
1858
- * whose every value is exact lowercase hexadecimal bytes.
1859
- *
1860
- * @remarks
1861
- * Read through the shared total key lens, so a hostile `ownKeys` trap and a
1862
- * throwing accessor both answer `false` rather than escaping. There is no
1863
- * dictionary combinator upstream to compose this from: the key law and the
1864
- * value law are both this package's own.
1865
- *
1866
- * @example
1867
- * ```ts
1868
- * import { isSnapshot } from '@orkestrel/scaffold'
1869
- *
1870
- * isSnapshot({ 'AGENTS.md': '68690a' }) // true
1871
- * isSnapshot({ 'AGENTS.md': 'hi' }) // false
1872
- * ```
1873
- */
1874
- export declare function isSnapshot(value: unknown): value is Snapshot;
1755
+ /**
1756
+ * Narrow a value to an {@link Override}.
1757
+ *
1758
+ * @remarks
1759
+ * Whether the path names a planned artifact is a gate law; whether it names a
1760
+ * destination at all is this guard's.
1761
+ *
1762
+ * @example
1763
+ * ```ts
1764
+ * import { isOverride } from '@orkestrel/scaffold'
1765
+ *
1766
+ * isOverride({ path: 'README.md', content: '# Title\n' }) // true
1767
+ * ```
1768
+ */
1769
+ export declare const isOverride: Guard<Override>;
1875
1770
 
1876
- /**
1877
- * Whether an upstream lookup produced an answer.
1878
- *
1879
- * @remarks
1880
- * `found` carries the answer. `missing` is an upstream `404`, which is a
1881
- * definite answer that the package is not published there. `failed` is a
1882
- * transport fault, which is no answer at all and may succeed on a later run.
1883
- * Holding these apart from how a local copy compares is what lets a verdict
1884
- * omit the value it never received instead of inventing an empty one.
1885
- */
1886
- export declare type Lookup = 'found' | 'missing' | 'failed';
1771
+ /**
1772
+ * Narrow a value to a logical target-relative path.
1773
+ *
1774
+ * @param value - The candidate path.
1775
+ * @returns `true` for a bounded relative path with no traversal, empty segment,
1776
+ * control character, or reserved syntax character.
1777
+ *
1778
+ * @remarks
1779
+ * Every path this package reads or writes passes here, so one law covers a
1780
+ * planned artifact, an override target, an audit finding, a guide mirror, and a
1781
+ * snapshot key. Rejecting `..`, a leading `/`, and a backslash at the guard is
1782
+ * what stops a caller-supplied path from naming a destination outside the
1783
+ * target. Host-location validation is a separate server boundary: this guard
1784
+ * does not reject a device spelling, a trailing dot or space, or a segment that
1785
+ * exceeds a host filesystem's byte ceiling.
1786
+ *
1787
+ * @example
1788
+ * ```ts
1789
+ * import { isPath } from '@orkestrel/scaffold'
1790
+ *
1791
+ * isPath('configs/src/tsconfig.core.json') // true
1792
+ * isPath('../secrets') // false
1793
+ * ```
1794
+ */
1795
+ export declare function isPath(value: unknown): value is string;
1887
1796
 
1888
- /**
1889
- * Project a package manifest's text to the `@orkestrel/*` packages it declares.
1890
- *
1891
- * @param manifest - The `package.json` text.
1892
- * @returns One dependency per declared `@orkestrel` package, in section order,
1893
- * with the first declaration of a repeated name winning.
1894
- *
1895
- * @remarks
1896
- * Runtime, development, and peer sections are read in that order, because a
1897
- * package the fleet publishes is upstream of this workspace wherever it is
1898
- * declared. Every other name is skipped rather than refused: a workspace's
1899
- * unrelated dependencies are not this package's to report on.
1900
- *
1901
- * Never throws, and every row it returns satisfies `isDependency` while the
1902
- * list satisfies `isCollection`, so the result crosses the compiler's own
1903
- * boundary without a second cleaning.
1904
- *
1905
- * @example
1906
- * ```ts
1907
- * import { manifestToDependencies } from '@orkestrel/scaffold'
1908
- *
1909
- * manifestToDependencies('{"dependencies":{"@orkestrel/emitter":"^0.0.5","vite":"~8.2.0"}}')
1910
- * // [{ name: '@orkestrel/emitter', range: '^0.0.5' }]
1911
- * ```
1912
- */
1913
- export declare function manifestToDependencies(manifest: string): readonly Dependency[];
1797
+ /**
1798
+ * Narrow a value to a {@link Plan}.
1799
+ *
1800
+ * @remarks
1801
+ * A plan reaches the writer, and the writer has no question channel, so this
1802
+ * carries the whole law of the value: every artifact path, every claimed byte,
1803
+ * and the blueprint it was compiled from.
1804
+ */
1805
+ export declare const isPlan: Guard<Plan>;
1914
1806
 
1915
- /**
1916
- * Project a package manifest's text to its own name.
1917
- *
1918
- * @param manifest - The `package.json` text.
1919
- * @returns The declared name, or `undefined` when the text is oversized,
1920
- * malformed, not an object, or carries no bounded string name.
1921
- *
1922
- * @remarks
1923
- * Never throws: a manifest is a file a target owns, so unreadable is an answer
1924
- * rather than a fault. The name is bounded by the registry's own package-name
1925
- * ceiling, because it reaches a path through {@link nameToGuide}.
1926
- *
1927
- * @example
1928
- * ```ts
1929
- * import { manifestToName } from '@orkestrel/scaffold'
1930
- *
1931
- * manifestToName('{"name":"@orkestrel/router"}') // '@orkestrel/router'
1932
- * manifestToName('{') // undefined
1933
- * ```
1934
- */
1935
- export declare function manifestToName(manifest: string): string | undefined;
1807
+ /**
1808
+ * Narrow a value to a {@link Question}.
1809
+ *
1810
+ * @example
1811
+ * ```ts
1812
+ * import { isQuestion } from '@orkestrel/scaffold'
1813
+ *
1814
+ * isQuestion({ field: 'src', message: 'Unknown environment', blocking: true }) // true
1815
+ * ```
1816
+ */
1817
+ export declare const isQuestion: Guard<Question>;
1936
1818
 
1937
- /**
1938
- * Test whether {@link inferDrift} could have produced a finding for an ownership.
1939
- *
1940
- * @param ownership - What scaffold claims at the planned path.
1941
- * @param finding - The audit verdict to test.
1942
- * @returns Whether the ownership and verdict are reachable through {@link inferDrift}.
1943
- *
1944
- * @remarks
1945
- * This predicate keeps the comparison law beside the reachability law it
1946
- * restates. A mutation uses it so a refusal can distinguish an impossible
1947
- * verdict from a target that genuinely moved after its audit.
1948
- */
1949
- export declare function matchesDriftReachability(ownership: Ownership, finding: Finding): boolean;
1819
+ /**
1820
+ * Narrow a caught value to a {@link ScaffoldError}.
1821
+ *
1822
+ * @param value - The caught value to narrow.
1823
+ * @returns `true` when `value` is a {@link ScaffoldError}.
1824
+ *
1825
+ * @example
1826
+ * ```ts
1827
+ * import { isScaffoldError } from '@orkestrel/scaffold'
1828
+ *
1829
+ * isScaffoldError(new Error('plain')) // false
1830
+ * isScaffoldError(undefined) // false
1831
+ * ```
1832
+ */
1833
+ export declare function isScaffoldError(value: unknown): value is ScaffoldError;
1950
1834
 
1951
- /**
1952
- * Test whether a declared engines floor is at or above the supported minimum.
1953
- *
1954
- * @param engines - The declared `engines.node` range.
1955
- * @returns `true` when the range is the accepted syntax and its floor is at or
1956
- * above `MINIMUM_NODE_VERSION`.
1957
- *
1958
- * @remarks
1959
- * The declaration states a floor, so the comparison is against the oldest Node
1960
- * the generated toolchain supports rather than against a published version. The
1961
- * `>=` prefix is read here, so no caller repeats the offset.
1962
- *
1963
- * @example
1964
- * ```ts
1965
- * import { matchesEngines } from '@orkestrel/scaffold'
1966
- *
1967
- * matchesEngines('>=22.12.0') // true
1968
- * matchesEngines('>=20.0.0') // false
1969
- * matchesEngines('22.12.0') // false
1970
- * ```
1971
- */
1972
- export declare function matchesEngines(engines: string): boolean;
1835
+ /**
1836
+ * Narrow a value to a {@link Snapshot}.
1837
+ *
1838
+ * @param value - The candidate target snapshot.
1839
+ * @returns `true` for a bounded plain record whose every key is a path and
1840
+ * whose every value is exact lowercase hexadecimal bytes.
1841
+ *
1842
+ * @remarks
1843
+ * Read through the shared total key lens, so a hostile `ownKeys` trap and a
1844
+ * throwing accessor both answer `false` rather than escaping. There is no
1845
+ * dictionary combinator upstream to compose this from: the key law and the
1846
+ * value law are both this package's own.
1847
+ *
1848
+ * @example
1849
+ * ```ts
1850
+ * import { isSnapshot } from '@orkestrel/scaffold'
1851
+ *
1852
+ * isSnapshot({ 'AGENTS.md': '68690a' }) // true
1853
+ * isSnapshot({ 'AGENTS.md': 'hi' }) // false
1854
+ * ```
1855
+ */
1856
+ export declare function isSnapshot(value: unknown): value is Snapshot;
1973
1857
 
1974
- /**
1975
- * Test whether a path instructs or wires an agent rather than the toolchain.
1976
- *
1977
- * @param path - The target-relative path to test.
1978
- * @returns `true` when the path is beneath a harness directory or is one of the
1979
- * exact root filenames that wires an agent bench.
1980
- *
1981
- * @remarks
1982
- * The one home of the orchestration membership rule. A vendored path and a
1983
- * foreign path found in a target are both classified through here, so a new
1984
- * harness directory is admitted once in `ORCHESTRATION_PATH_PREFIXES` and every
1985
- * caller follows.
1986
- *
1987
- * @example
1988
- * ```ts
1989
- * import { matchesOrchestrationPath } from '@orkestrel/scaffold'
1990
- *
1991
- * matchesOrchestrationPath('.claude/rules/names.md') // true
1992
- * matchesOrchestrationPath('.mcp.json') // true
1993
- * matchesOrchestrationPath('.oxlintrc.json') // false
1994
- * ```
1995
- */
1996
- export declare function matchesOrchestrationPath(path: string): boolean;
1858
+ /**
1859
+ * Whether an upstream lookup produced an answer.
1860
+ *
1861
+ * @remarks
1862
+ * `found` carries the answer. `missing` is an upstream `404`, which is a
1863
+ * definite answer that the package is not published there. `failed` is a
1864
+ * transport fault, which is no answer at all and may succeed on a later run.
1865
+ * Holding these apart from how a local copy compares is what lets a verdict
1866
+ * omit the value it never received instead of inventing an empty one.
1867
+ */
1868
+ export declare type Lookup = 'found' | 'missing' | 'failed';
1997
1869
 
1998
- /**
1999
- * Test whether one emitted line fits the vendored formatter width.
2000
- *
2001
- * @param line - One emitted line, leading tabs included.
2002
- * @returns `true` when the expanded line fits.
2003
- *
2004
- * @remarks
2005
- * A generator writes source the formatter then reads back, so a line packed
2006
- * past the vendored width is rewrapped on the next `format` run and the emitted
2007
- * bytes stop matching the plan the audit compares against. The generator
2008
- * therefore measures a candidate line and chooses the shape the formatter would
2009
- * have chosen. Tabs are expanded first because the formatter counts them as
2010
- * `TAB_WIDTH` columns rather than as one character.
2011
- *
2012
- * @example
2013
- * ```ts
2014
- * import { matchesPrintWidth } from '@orkestrel/scaffold'
2015
- *
2016
- * matchesPrintWidth('\t\tprojects: [core],') // true
2017
- * ```
2018
- */
2019
- export declare function matchesPrintWidth(line: string): boolean;
1870
+ /**
1871
+ * Project a package manifest's text to the `@orkestrel/*` packages it declares.
1872
+ *
1873
+ * @param manifest - The `package.json` text.
1874
+ * @returns One dependency per declared `@orkestrel` package, in section order,
1875
+ * with the first declaration of a repeated name winning.
1876
+ *
1877
+ * @remarks
1878
+ * Runtime, development, and peer sections are read in that order, because a
1879
+ * package the fleet publishes is upstream of this workspace wherever it is
1880
+ * declared. Every other name is skipped rather than refused: a workspace's
1881
+ * unrelated dependencies are not this package's to report on.
1882
+ *
1883
+ * Never throws, and every row it returns satisfies `isDependency` while the
1884
+ * list satisfies `isCollection`, so the result crosses the compiler's own
1885
+ * boundary without a second cleaning.
1886
+ *
1887
+ * @example
1888
+ * ```ts
1889
+ * import { manifestToDependencies } from '@orkestrel/scaffold'
1890
+ *
1891
+ * manifestToDependencies('{"dependencies":{"@orkestrel/emitter":"^0.0.5","vite":"~8.2.0"}}')
1892
+ * // [{ name: '@orkestrel/emitter', range: '^0.0.5' }]
1893
+ * ```
1894
+ */
1895
+ export declare function manifestToDependencies(manifest: string): readonly Dependency[];
2020
1896
 
2021
- /**
2022
- * Test whether a declared range already admits a published version.
2023
- *
2024
- * @param range - The declared dependency range.
2025
- * @param latest - The version the registry reported as latest.
2026
- * @returns `true` when the range admits that version.
2027
- *
2028
- * @remarks
2029
- * The one place this comparison is made. A `Release` records the declared range
2030
- * and the reported version and stores no verdict beside them, because a stored
2031
- * verdict could only disagree with the two fields it sits next to.
2032
- *
2033
- * Readability is decided first, and it is `EXTRA_RANGE_PATTERN`: an optional
2034
- * caret or tilde over three numeric components and an optional prerelease
2035
- * suffix. That pattern already covers every `ORKESTREL_RANGE_PATTERN` range and
2036
- * every `VERSION_PATTERN` version, so the subset is stated once rather than
2037
- * assembled here. Text outside it is never admitted, including text handed in on
2038
- * both sides, so an unreadable declaration surfaces as work instead of matching
2039
- * itself.
2040
- *
2041
- * The accepted subset is the one that pattern admits: an exact pin, a tilde
2042
- * range, and a caret range. An exact pin is satisfied by that version alone. A
2043
- * tilde range holds the minor and admits a later patch. A caret range holds the
2044
- * leading nonzero component, which is why `^0.0.5` is an exact pin and `^0.5.3`
2045
- * admits `0.5.4` but not `0.6.0`.
2046
- *
2047
- * Identity is the door a prerelease passes through, and the only one: this
2048
- * package does not order prerelease precedence, so `1.2.3-beta.1` is satisfied
2049
- * by that exact string and by nothing else. The version side needs no separate
2050
- * readability test, because identity already proves it equals a readable
2051
- * declaration and every other branch reads it through {@link extractVersion}.
2052
- *
2053
- * @example
2054
- * ```ts
2055
- * import { matchesRange } from '@orkestrel/scaffold'
2056
- *
2057
- * matchesRange('^0.0.5', '0.0.5') // true
2058
- * matchesRange('^0.0.5', '0.0.7') // false
2059
- * matchesRange('~8.2.0', '8.2.4') // true
2060
- * matchesRange('^7.58.12', '7.60.0') // true
2061
- * matchesRange('not-a-range', 'not-a-range') // false
2062
- * ```
2063
- */
2064
- export declare function matchesRange(range: string, latest: string): boolean;
1897
+ /**
1898
+ * Project a package manifest's text to its own name.
1899
+ *
1900
+ * @param manifest - The `package.json` text.
1901
+ * @returns The declared name, or `undefined` when the text is oversized,
1902
+ * malformed, not an object, or carries no bounded string name.
1903
+ *
1904
+ * @remarks
1905
+ * Never throws: a manifest is a file a target owns, so unreadable is an answer
1906
+ * rather than a fault. The name is bounded by the registry's own package-name
1907
+ * ceiling, because it reaches a path through {@link nameToGuide}.
1908
+ *
1909
+ * @example
1910
+ * ```ts
1911
+ * import { manifestToName } from '@orkestrel/scaffold'
1912
+ *
1913
+ * manifestToName('{"name":"@orkestrel/router"}') // '@orkestrel/router'
1914
+ * manifestToName('{') // undefined
1915
+ * ```
1916
+ */
1917
+ export declare function manifestToName(manifest: string): string | undefined;
2065
1918
 
2066
- /** Maximum bytes accepted for one artifact. */
2067
- export declare const MAX_ARTIFACT_BYTES = 5242880;
1919
+ /**
1920
+ * Test whether {@link inferDrift} could have produced a finding for an ownership.
1921
+ *
1922
+ * @param ownership - What scaffold claims at the planned path.
1923
+ * @param finding - The audit verdict to test.
1924
+ * @returns Whether the ownership and verdict are reachable through {@link inferDrift}.
1925
+ *
1926
+ * @remarks
1927
+ * This predicate keeps the comparison law beside the reachability law it
1928
+ * restates. A mutation uses it so a refusal can distinguish an impossible
1929
+ * verdict from a target that genuinely moved after its audit.
1930
+ */
1931
+ export declare function matchesDriftReachability(ownership: Ownership, finding: Finding): boolean;
2068
1932
 
2069
- /** Maximum length of the hexadecimal string carrying one artifact's bytes. */
2070
- export declare const MAX_ARTIFACT_HEX_LENGTH: number;
1933
+ /**
1934
+ * Test whether a declared engines floor is at or above the supported minimum.
1935
+ *
1936
+ * @param engines - The declared `engines.node` range.
1937
+ * @returns `true` when the range is the accepted syntax and its floor is at or
1938
+ * above `MINIMUM_NODE_VERSION`.
1939
+ *
1940
+ * @remarks
1941
+ * The declaration states a floor, so the comparison is against the oldest Node
1942
+ * the generated toolchain supports rather than against a published version. The
1943
+ * `>=` prefix is read here, so no caller repeats the offset.
1944
+ *
1945
+ * @example
1946
+ * ```ts
1947
+ * import { matchesEngines } from '@orkestrel/scaffold'
1948
+ *
1949
+ * matchesEngines('>=22.12.0') // true
1950
+ * matchesEngines('>=20.0.0') // false
1951
+ * matchesEngines('22.12.0') // false
1952
+ * ```
1953
+ */
1954
+ export declare function matchesEngines(engines: string): boolean;
2071
1955
 
2072
- /** Maximum findings one audit can produce from a bounded plan and snapshot. */
2073
- export declare const MAX_AUDIT_FINDINGS: number;
1956
+ /**
1957
+ * Test whether a path instructs or wires an agent rather than the toolchain.
1958
+ *
1959
+ * @param path - The target-relative path to test.
1960
+ * @returns `true` when the path is beneath a harness directory or is one of the
1961
+ * exact root filenames that wires an agent bench.
1962
+ *
1963
+ * @remarks
1964
+ * The one home of the orchestration membership rule. A vendored path and a
1965
+ * foreign path found in a target are both classified through here, so a new
1966
+ * harness directory is admitted once in `ORCHESTRATION_PATH_PREFIXES` and every
1967
+ * caller follows.
1968
+ *
1969
+ * @example
1970
+ * ```ts
1971
+ * import { matchesOrchestrationPath } from '@orkestrel/scaffold'
1972
+ *
1973
+ * matchesOrchestrationPath('.claude/rules/names.md') // true
1974
+ * matchesOrchestrationPath('.mcp.json') // true
1975
+ * matchesOrchestrationPath('.oxlintrc.json') // false
1976
+ * ```
1977
+ */
1978
+ export declare function matchesOrchestrationPath(path: string): boolean;
2074
1979
 
2075
- /** Maximum items accepted in one public collection. */
2076
- export declare const MAX_COLLECTION_ITEMS = 1000;
1980
+ /**
1981
+ * Test whether one emitted line fits the vendored formatter width.
1982
+ *
1983
+ * @param line - One emitted line, leading tabs included.
1984
+ * @returns `true` when the expanded line fits.
1985
+ *
1986
+ * @remarks
1987
+ * A generator writes source the formatter then reads back, so a line packed
1988
+ * past the vendored width is rewrapped on the next `format` run and the emitted
1989
+ * bytes stop matching the plan the audit compares against. The generator
1990
+ * therefore measures a candidate line and chooses the shape the formatter would
1991
+ * have chosen. Tabs are expanded first because the formatter counts them as
1992
+ * `TAB_WIDTH` columns rather than as one character.
1993
+ *
1994
+ * @example
1995
+ * ```ts
1996
+ * import { matchesPrintWidth } from '@orkestrel/scaffold'
1997
+ *
1998
+ * matchesPrintWidth('\t\tprojects: [core],') // true
1999
+ * ```
2000
+ */
2001
+ export declare function matchesPrintWidth(line: string): boolean;
2077
2002
 
2078
- /** Maximum dependency package name length, scope included, as the registry caps it. */
2079
- export declare const MAX_DEPENDENCY_NAME_LENGTH = 214;
2003
+ /**
2004
+ * Test whether a declared range already admits a published version.
2005
+ *
2006
+ * @param range - The declared dependency range.
2007
+ * @param latest - The version the registry reported as latest.
2008
+ * @returns `true` when the range admits that version.
2009
+ *
2010
+ * @remarks
2011
+ * The one place this comparison is made. A `Release` records the declared range
2012
+ * and the reported version and stores no verdict beside them, because a stored
2013
+ * verdict could only disagree with the fields it sits next to.
2014
+ *
2015
+ * Readability is decided first, and it is `EXTRA_RANGE_PATTERN`: an optional
2016
+ * caret or tilde over `major.minor.patch` and an optional prerelease
2017
+ * suffix. That pattern already covers every `ORKESTREL_RANGE_PATTERN` range and
2018
+ * every `VERSION_PATTERN` version, so the subset is stated once rather than
2019
+ * assembled here. Text outside it is never admitted, including text handed in on
2020
+ * both sides, so an unreadable declaration surfaces as work instead of matching
2021
+ * itself.
2022
+ *
2023
+ * The accepted subset is the one that pattern admits: an exact pin, a tilde
2024
+ * range, and a caret range. An exact pin is satisfied by that version alone. A
2025
+ * tilde range holds the minor and admits a later patch. A caret range holds the
2026
+ * leading nonzero component, which is why `^0.0.5` is an exact pin and `^0.5.3`
2027
+ * admits `0.5.4` but not `0.6.0`.
2028
+ *
2029
+ * Identity is the door a prerelease passes through, and the only one: this
2030
+ * package does not order prerelease precedence, so `1.2.3-beta.1` is satisfied
2031
+ * by that exact string and by nothing else. The version side needs no separate
2032
+ * readability test, because identity already proves it equals a readable
2033
+ * declaration and every other branch reads it through {@link extractVersion}.
2034
+ *
2035
+ * @example
2036
+ * ```ts
2037
+ * import { matchesRange } from '@orkestrel/scaffold'
2038
+ *
2039
+ * matchesRange('^0.0.5', '0.0.5') // true
2040
+ * matchesRange('^0.0.5', '0.0.7') // false
2041
+ * matchesRange('~8.2.0', '8.2.4') // true
2042
+ * matchesRange('^7.58.12', '7.60.0') // true
2043
+ * matchesRange('not-a-range', 'not-a-range') // false
2044
+ * ```
2045
+ */
2046
+ export declare function matchesRange(range: string, latest: string): boolean;
2080
2047
 
2081
- /** Maximum bytes accepted for one package or vendored-host manifest. */
2082
- export declare const MAX_MANIFEST_BYTES = 1048576;
2048
+ /** Maximum bytes accepted for one artifact. */
2049
+ export declare const MAX_ARTIFACT_BYTES = 5242880;
2083
2050
 
2084
- /**
2085
- * Maximum bare workspace name length.
2086
- *
2087
- * @remarks
2088
- * The registry caps a whole package name at 214 characters and the generated
2089
- * scope `@orkestrel/` spends 11 of them.
2090
- */
2091
- export declare const MAX_NAME_LENGTH = 203;
2051
+ /** Maximum length of the hexadecimal string carrying one artifact's bytes. */
2052
+ export declare const MAX_ARTIFACT_HEX_LENGTH: number;
2092
2053
 
2093
- /** Maximum length of one path, matching the longest a supported filesystem accepts. */
2094
- export declare const MAX_PATH_LENGTH = 32767;
2054
+ /** Maximum findings one audit can produce from a bounded plan and snapshot. */
2055
+ export declare const MAX_AUDIT_FINDINGS: number;
2095
2056
 
2096
- /** Maximum length of one declared package range. */
2097
- export declare const MAX_RANGE_LENGTH = 2048;
2057
+ /** Maximum items accepted in one public collection. */
2058
+ export declare const MAX_COLLECTION_ITEMS = 1000;
2098
2059
 
2099
- /** Maximum bytes retained across one whole plan or audit. */
2100
- export declare const MAX_TOTAL_ARTIFACT_BYTES = 104857600;
2060
+ /** Maximum dependency package name length, scope included, as the registry caps it. */
2061
+ export declare const MAX_DEPENDENCY_NAME_LENGTH = 214;
2101
2062
 
2102
- /** The oldest Node version the generated toolchain supports. */
2103
- export declare const MINIMUM_NODE_VERSION = "22.12.0";
2063
+ /** Maximum bytes accepted for one package or vendored-host manifest. */
2064
+ export declare const MAX_MANIFEST_BYTES = 1048576;
2104
2065
 
2105
- /**
2106
- * One dependency guide fetched from upstream, beside the local mirror it answers for.
2107
- *
2108
- * @remarks
2109
- * A found lookup carries the fetched bytes; one that produced no answer carries
2110
- * the cause and no bytes. Either way `observed` is the local mirror's exact
2111
- * bytes as they stood when the fetch was made, and is absent when the mirror
2112
- * was not there; it is the precondition the write is held to, exactly as
2113
- * {@link Finding.observed} is. Whether the mirror is behind is not recorded,
2114
- * because it is `content` against `observed` and a stored answer could only
2115
- * disagree with them. These bytes belong to the catalog verb, which is why a
2116
- * guide mirror is presence-owned: repair restores one that is absent and never
2117
- * replaces one that is present.
2118
- */
2119
- export declare type Mirror = {
2120
- readonly name: string;
2121
- readonly path: string;
2122
- readonly lookup: 'found';
2123
- readonly content: string;
2124
- readonly observed?: string;
2125
- readonly note?: never;
2126
- } | {
2127
- readonly name: string;
2128
- readonly path: string;
2129
- readonly lookup: 'missing' | 'failed';
2130
- readonly note: string;
2131
- readonly observed?: string;
2132
- readonly content?: never;
2133
- };
2134
-
2135
- /** The bare workspace name syntax: lowercase alphanumeric with hyphens, letter first. */
2136
- export declare const NAME_PATTERN: RegExp;
2137
-
2138
- /**
2139
- * Derive the guide mirror path a package name answers for.
2140
- *
2141
- * @param name - A bare or `@orkestrel`-scoped package name.
2142
- * @returns The mirror path, `guides/<bare name>.md`.
2143
- *
2144
- * @remarks
2145
- * The single shape of a mirror path, read by the fetch that fills one and by
2146
- * the plan that leaves the target's own guide out of its host set. Only the
2147
- * segment after the final `/` is used, so the scope never reaches the path.
2148
- * Whether the derived path is safe to write is `isPath`'s answer and the gate's,
2149
- * which is why `DEPENDENCY_NAME_PATTERN` closes the name to a bare scoped one
2150
- * before it ever arrives here.
2151
- *
2152
- * @example
2153
- * ```ts
2154
- * import { nameToGuide } from '@orkestrel/scaffold'
2155
- *
2156
- * nameToGuide('@orkestrel/router') // 'guides/router.md'
2157
- * nameToGuide('scaffold') // 'guides/scaffold.md'
2158
- * ```
2159
- */
2160
- export declare function nameToGuide(name: string): string;
2066
+ /**
2067
+ * Maximum bare workspace name length.
2068
+ *
2069
+ * @remarks
2070
+ * The registry caps a whole package name at 214 characters and the generated
2071
+ * scope `@orkestrel/` spends 11 of them.
2072
+ */
2073
+ export declare const MAX_NAME_LENGTH = 203;
2161
2074
 
2162
- /**
2163
- * Compile the vendored host artifacts a named workspace plans.
2164
- *
2165
- * @param name - The target workspace's own bare package name.
2166
- * @returns One artifact per vendored path, in `HOST_PATHS` order.
2167
- *
2168
- * @remarks
2169
- * Every artifact is claimed by presence, which is the strongest claim a pure
2170
- * compile can make: core cannot read the vendored data root, so it cannot carry
2171
- * the bytes a content claim would have to be checked against. Reading that root
2172
- * is what promotes the ones scaffold owns the bytes of.
2173
- *
2174
- * `source` is left absent because it falls back to `path`, and every vendored
2175
- * path is stored under the name it is written to. The group comes from
2176
- * {@link inferGroup}, so a vendored path and a foreign path found in a target
2177
- * are classified by one rule and a plan never disagrees with the audit beside
2178
- * it.
2179
- *
2180
- * @example
2181
- * ```ts
2182
- * import { nameToHostArtifacts } from '@orkestrel/scaffold'
2183
- *
2184
- * nameToHostArtifacts('router').some((artifact) => artifact.path === 'AGENTS.md') // true
2185
- * nameToHostArtifacts('router').some((artifact) => artifact.path === 'guides/router.md') // false
2186
- * ```
2187
- */
2188
- export declare function nameToHostArtifacts(name: string): readonly Artifact[];
2075
+ /** Maximum length of one path, matching the longest a supported filesystem accepts. */
2076
+ export declare const MAX_PATH_LENGTH = 32767;
2189
2077
 
2190
- /**
2191
- * Derive the declaration rewrite a published face's `beforeWriteFile` applies.
2192
- *
2193
- * @param name - The workspace's own bare package name.
2194
- * @returns The ternary consequent an emitted `vite.{browser,server}.config.ts`
2195
- * fills its `{{replacement}}` span with, indented for that span.
2196
- *
2197
- * @remarks
2198
- * `vite-plugin-dts` rolls a face into one declaration and keeps each source
2199
- * module's own relative depth, so a nested module emits a path that escapes
2200
- * `dist/src` and a flat one resolves only by luck. Both faces rewrite the same
2201
- * relative core path to the package's published root export, so the branch is
2202
- * derived once here. The extension alternation is what the two permitted import
2203
- * spellings produce: an `@src/core` alias resolves to the core source module and
2204
- * prints `.ts`, while a relative import prints the `.js` specifier it was
2205
- * written with. The formatter keeps the call on one line only while the line it
2206
- * prints measures inside the vendored width, and the workspace name is what
2207
- * varies, so the shape is chosen by measuring the candidate: a tab prints as the
2208
- * vendored two columns, and the gate admits a name long enough to push the
2209
- * joined call past 100.
2210
- *
2211
- * @example
2212
- * ```ts
2213
- * import { nameToRewrite } from '@orkestrel/scaffold'
2214
- *
2215
- * nameToRewrite('router').includes("'@orkestrel/router'") // true
2216
- * ```
2217
- */
2218
- export declare function nameToRewrite(name: string): string;
2078
+ /** Maximum length of one declared package range. */
2079
+ export declare const MAX_RANGE_LENGTH = 2048;
2219
2080
 
2220
- /**
2221
- * The exact root filenames that wire an agent bench rather than the toolchain, frozen.
2222
- *
2223
- * @remarks
2224
- * `.mcp.json` registers MCP servers for the harness. It sits among the root
2225
- * dotfiles but governs agents, so it groups with the harness bridges.
2226
- */
2227
- export declare const ORCHESTRATION_PATH_NAMES: readonly string[];
2081
+ /** Maximum bytes retained across one whole plan or audit. */
2082
+ export declare const MAX_TOTAL_ARTIFACT_BYTES = 104857600;
2228
2083
 
2229
- /**
2230
- * The path prefixes whose contents instruct or wire an agent, frozen.
2231
- *
2232
- * @remarks
2233
- * A path is grouped by what it governs rather than by where it sits: anything
2234
- * beneath one of these prefixes is `orchestration`, and everything else that is
2235
- * not source, tests, guides, docs, or a manifest is `configs`. A vendored path
2236
- * and a foreign path found in a target are classified against the same list, so
2237
- * a new harness directory is admitted once.
2238
- */
2239
- export declare const ORCHESTRATION_PATH_PREFIXES: readonly string[];
2084
+ /** The oldest Node version the generated toolchain supports. */
2085
+ export declare const MINIMUM_NODE_VERSION = "22.12.0";
2240
2086
 
2241
- /**
2242
- * How an artifact's content is produced.
2243
- *
2244
- * @remarks
2245
- * `host` is byte-copied from this package's vendored data root. `template` is
2246
- * filled from a frozen template definition. `computed` is derived by this
2247
- * package's own combination logic. Origin says nothing about what scaffold
2248
- * claims at the path; {@link Ownership} says that.
2249
- */
2250
- export declare type Origin = 'host' | 'template' | 'computed';
2087
+ /**
2088
+ * One dependency guide fetched from upstream, beside the local mirror it answers for.
2089
+ *
2090
+ * @remarks
2091
+ * A found lookup carries the fetched bytes; one that produced no answer carries
2092
+ * the cause and no bytes. Either way `observed` is the local mirror's exact
2093
+ * bytes as they stood when the fetch was made, and is absent when the mirror
2094
+ * was not there; it is the precondition the write is held to, exactly as
2095
+ * {@link Finding.observed} is. Whether the mirror is behind is not recorded,
2096
+ * because it is `content` against `observed` and a stored answer could only
2097
+ * disagree with them. These bytes belong to the catalog verb, which is why a
2098
+ * guide mirror is presence-owned: repair restores one that is absent and never
2099
+ * replaces one that is present.
2100
+ */
2101
+ export declare type Mirror = {
2102
+ readonly name: string;
2103
+ readonly path: string;
2104
+ readonly lookup: 'found';
2105
+ readonly content: string;
2106
+ readonly observed?: string;
2107
+ readonly note?: never;
2108
+ } | {
2109
+ readonly name: string;
2110
+ readonly path: string;
2111
+ readonly lookup: 'missing' | 'failed';
2112
+ readonly note: string;
2113
+ readonly observed?: string;
2114
+ readonly content?: never;
2115
+ };
2116
+
2117
+ /** The bare workspace name syntax: lowercase alphanumeric with hyphens, letter first. */
2118
+ export declare const NAME_PATTERN: RegExp;
2251
2119
 
2252
- /**
2253
- * The exact caret-pinned pre-1.0 range accepted for an `@orkestrel/*` runtime dependency.
2254
- *
2255
- * @remarks
2256
- * Pre-1.0 means any `0.x`, not `0.0.x`. The narrower form would refuse the first
2257
- * fleet package to reach `0.1.0`, and `catalog` pins to whatever the registry
2258
- * publishes, so a single minor release would block every later run against a
2259
- * workspace that had already been pinned to it.
2260
- */
2261
- export declare const ORKESTREL_RANGE_PATTERN: RegExp;
2120
+ /**
2121
+ * Derive the guide mirror path a package name answers for.
2122
+ *
2123
+ * @param name - A bare or `@orkestrel`-scoped package name.
2124
+ * @returns The mirror path, `guides/<bare name>.md`.
2125
+ *
2126
+ * @remarks
2127
+ * The single shape of a mirror path, read by the fetch that fills one and by
2128
+ * the plan that leaves the target's own guide out of its host set. Only the
2129
+ * segment after the final `/` is used, so the scope never reaches the path.
2130
+ * Whether the derived path is safe to write is `isPath`'s answer and the gate's,
2131
+ * which is why `DEPENDENCY_NAME_PATTERN` closes the name to a bare scoped one
2132
+ * before it ever arrives here.
2133
+ *
2134
+ * @example
2135
+ * ```ts
2136
+ * import { nameToGuide } from '@orkestrel/scaffold'
2137
+ *
2138
+ * nameToGuide('@orkestrel/router') // 'guides/router.md'
2139
+ * nameToGuide('scaffold') // 'guides/scaffold.md'
2140
+ * ```
2141
+ */
2142
+ export declare function nameToGuide(name: string): string;
2262
2143
 
2263
- /**
2264
- * One artifact override.
2265
- *
2266
- * @remarks
2267
- * `content` replaces the rendered artifact at `path` and never partially
2268
- * merges it. Legality is measured against every artifact the blueprint drafts,
2269
- * before a compile narrows the returned groups. An override that matches none
2270
- * of those artifacts, that targets a host-origin artifact, or that targets the
2271
- * manifest is a blocking question rather than a silent no-op.
2272
- */
2273
- export declare interface Override {
2274
- readonly path: string;
2275
- readonly content: string;
2276
- }
2144
+ /**
2145
+ * Compile the vendored host artifacts a named workspace plans.
2146
+ *
2147
+ * @param name - The target workspace's own bare package name.
2148
+ * @returns One artifact per vendored path, in `HOST_PATHS` order.
2149
+ *
2150
+ * @remarks
2151
+ * Every artifact is claimed by presence, which is the strongest claim a pure
2152
+ * compile can make: core cannot read the vendored data root, so it cannot carry
2153
+ * the bytes a content claim would have to be checked against. Reading that root
2154
+ * is what promotes the ones scaffold owns the bytes of.
2155
+ *
2156
+ * `source` is left absent because it falls back to `path`, and every vendored
2157
+ * path is stored under the name it is written to. The group comes from
2158
+ * {@link inferGroup}, so a vendored path and a foreign path found in a target
2159
+ * are classified by one rule and a plan never disagrees with the audit beside
2160
+ * it.
2161
+ *
2162
+ * @example
2163
+ * ```ts
2164
+ * import { nameToHostArtifacts } from '@orkestrel/scaffold'
2165
+ *
2166
+ * nameToHostArtifacts('router').some((artifact) => artifact.path === 'AGENTS.md') // true
2167
+ * nameToHostArtifacts('router').some((artifact) => artifact.path === 'guides/router.md') // false
2168
+ * ```
2169
+ */
2170
+ export declare function nameToHostArtifacts(name: string): readonly Artifact[];
2277
2171
 
2278
- /**
2279
- * Measure a blueprint's overrides against the artifacts drafted for it.
2280
- *
2281
- * @param overrides - The blueprint's overrides.
2282
- * @param artifacts - The drafted artifacts, before overrides are applied.
2283
- * @returns One blocking question per override the draft cannot accept.
2284
- *
2285
- * @remarks
2286
- * An override that matches no planned artifact is a caller expecting a file that
2287
- * does not exist, and applying nothing would leave that expectation
2288
- * unanswered. An override on a host-origin artifact asks this package to
2289
- * rewrite a file it byte-copies from the vendored data root, which it never
2290
- * does. An override on the manifest asks it to rewrite the one artifact the
2291
- * blueprint's own fields decide, so the fields would no longer describe the
2292
- * workspace they generated. Each is refused rather than dropped.
2293
- *
2294
- * @example
2295
- * ```ts
2296
- * import { blueprintToConfigArtifacts, createBlueprint, overridesToQuestions } from '@orkestrel/scaffold'
2297
- *
2298
- * const artifacts = blueprintToConfigArtifacts(createBlueprint('router', { src: ['core'] }))
2299
- *
2300
- * overridesToQuestions([{ path: 'package.json', content: '{}\n' }], artifacts).length // 1
2301
- * ```
2302
- */
2303
- export declare function overridesToQuestions(overrides: readonly Override[], artifacts: readonly Artifact[]): readonly Question[];
2172
+ /**
2173
+ * Derive the declaration rewrite a published face's `beforeWriteFile` applies.
2174
+ *
2175
+ * @param name - The workspace's own bare package name.
2176
+ * @returns The ternary consequent an emitted `vite.{browser,server}.config.ts`
2177
+ * fills its `{{replacement}}` span with, indented for that span.
2178
+ *
2179
+ * @remarks
2180
+ * `vite-plugin-dts` rolls a face into one declaration and keeps each source
2181
+ * module's own relative depth, so a nested module emits a path that escapes
2182
+ * `dist/src` and a flat one resolves only by luck. Both faces rewrite the same
2183
+ * relative core path to the package's published root export, so the branch is
2184
+ * derived once here. The extension alternation is what the permitted import
2185
+ * spellings produce: an `@src/core` alias resolves to the core source module and
2186
+ * prints `.ts`, while a relative import prints the `.js` specifier it was
2187
+ * written with. The formatter keeps the call on one line only while the line it
2188
+ * prints measures inside the vendored width, and the workspace name is what
2189
+ * varies, so the shape is chosen by measuring the candidate: a tab prints as the
2190
+ * vendored two columns, and the gate admits a name long enough to push the
2191
+ * joined call past 100.
2192
+ *
2193
+ * @example
2194
+ * ```ts
2195
+ * import { nameToRewrite } from '@orkestrel/scaffold'
2196
+ *
2197
+ * nameToRewrite('router').includes("'@orkestrel/router'") // true
2198
+ * ```
2199
+ */
2200
+ export declare function nameToRewrite(name: string): string;
2304
2201
 
2305
- /**
2306
- * What scaffold claims at an artifact's path.
2307
- *
2308
- * @remarks
2309
- * `content` claims the bytes: audit compares them, and a write restores a
2310
- * missing file and replaces a stale one. `presence` claims only that the file
2311
- * exists: audit compares existence, and a write restores an absent file and
2312
- * never touches present bytes. `birth` claims only the file's creation: audit
2313
- * never compares it and always reports it aligned, and a write creates it only
2314
- * while it is absent.
2315
- */
2316
- export declare type Ownership = 'content' | 'presence' | 'birth';
2202
+ /**
2203
+ * The exact root filenames that wire an agent bench rather than the toolchain, frozen.
2204
+ *
2205
+ * @remarks
2206
+ * `.mcp.json` registers MCP servers for the harness. It sits among the root
2207
+ * dotfiles but governs agents, so it groups with the harness bridges.
2208
+ */
2209
+ export declare const ORCHESTRATION_PATH_NAMES: readonly string[];
2317
2210
 
2318
- /**
2319
- * Coerce an untrusted value to a {@link Blueprint}.
2320
- *
2321
- * @param value - The value to parse.
2322
- * @returns The blueprint, or `undefined` when the value is not one.
2323
- *
2324
- * @remarks
2325
- * Derived from {@link isBlueprint}, which is what makes the pair sound in both
2326
- * directions: a guard-valid value is returned unchanged, and every value this
2327
- * returns satisfies that guard. The reference is returned rather than copied,
2328
- * so a caller that means to own the value clones it.
2329
- *
2330
- * @example
2331
- * ```ts
2332
- * import { parseBlueprint } from '@orkestrel/scaffold'
2333
- *
2334
- * parseBlueprint({ name: 'router' }) // undefined
2335
- * ```
2336
- */
2337
- export declare function parseBlueprint(value: unknown): Blueprint | undefined;
2211
+ /**
2212
+ * The path prefixes whose contents instruct or wire an agent, frozen.
2213
+ *
2214
+ * @remarks
2215
+ * A path is grouped by what it governs rather than by where it sits: anything
2216
+ * beneath one of these prefixes is `orchestration`, and everything else that is
2217
+ * not source, tests, guides, docs, or a manifest is `configs`. A vendored path
2218
+ * and a foreign path found in a target are classified against the same list, so
2219
+ * a new harness directory is admitted once.
2220
+ */
2221
+ export declare const ORCHESTRATION_PATH_PREFIXES: readonly string[];
2338
2222
 
2339
- /**
2340
- * Coerce an untrusted value to {@link CompilerOptions}.
2341
- *
2342
- * @param value - The value to parse.
2343
- * @returns The options, or `undefined` when the value is not an option bag.
2344
- *
2345
- * @remarks
2346
- * Derived from {@link isCompilerOptions}. Absence is not an option bag, so
2347
- * `undefined` in returns `undefined` out and a constructor reads that as the
2348
- * defaults rather than as a refusal.
2349
- *
2350
- * @example
2351
- * ```ts
2352
- * import { parseCompilerOptions } from '@orkestrel/scaffold'
2353
- *
2354
- * parseCompilerOptions({ on: { compile: () => {} } }) // the same record
2355
- * parseCompilerOptions({ on: { compiled: () => {} } }) // undefined
2356
- * ```
2357
- */
2358
- export declare function parseCompilerOptions(value: unknown): CompilerOptions | undefined;
2223
+ /**
2224
+ * How an artifact's content is produced.
2225
+ *
2226
+ * @remarks
2227
+ * `host` is byte-copied from this package's vendored data root. `template` is
2228
+ * filled from a frozen template definition. `computed` is derived by this
2229
+ * package's own combination logic. Origin says nothing about what scaffold
2230
+ * claims at the path; {@link Ownership} says that.
2231
+ */
2232
+ export declare type Origin = 'host' | 'template' | 'computed';
2359
2233
 
2360
- /**
2361
- * Coerce an untrusted value to a group selection.
2362
- *
2363
- * @param value - The value to parse.
2364
- * @returns The selection, or `undefined` when the value is not one.
2365
- *
2366
- * @remarks
2367
- * Derived from {@link isGroups}. Order and repetition are preserved: which
2368
- * groups a plan finally covers, and in which order, is the compiler's to decide
2369
- * from `GROUPS`, not this boundary's.
2370
- *
2371
- * @example
2372
- * ```ts
2373
- * import { parseGroups } from '@orkestrel/scaffold'
2374
- *
2375
- * parseGroups(['manifest', 'configs']) // ['manifest', 'configs']
2376
- * parseGroups(['readme']) // undefined
2377
- * ```
2378
- */
2379
- export declare function parseGroups(value: unknown): readonly Group[] | undefined;
2234
+ /**
2235
+ * The exact caret-pinned pre-1.0 range accepted for an `@orkestrel/*` runtime dependency.
2236
+ *
2237
+ * @remarks
2238
+ * Pre-1.0 means any `0.x`, not `0.0.x`. The narrower form would refuse the first
2239
+ * fleet package to reach `0.1.0`, and `catalog` pins to whatever the registry
2240
+ * publishes, so a single minor release would block every later run against a
2241
+ * workspace that had already been pinned to it.
2242
+ */
2243
+ export declare const ORKESTREL_RANGE_PATTERN: RegExp;
2380
2244
 
2381
- /**
2382
- * Coerce an untrusted value to a {@link Snapshot}.
2383
- *
2384
- * @param value - The value to parse.
2385
- * @returns The snapshot, or `undefined` when the value is not one.
2386
- *
2387
- * @remarks
2388
- * Derived from {@link isSnapshot}.
2389
- *
2390
- * @example
2391
- * ```ts
2392
- * import { parseSnapshot } from '@orkestrel/scaffold'
2393
- *
2394
- * parseSnapshot({ 'AGENTS.md': '68690a' }) // { 'AGENTS.md': '68690a' }
2395
- * parseSnapshot({ 'AGENTS.md': null }) // undefined
2396
- * ```
2397
- */
2398
- export declare function parseSnapshot(value: unknown): Snapshot | undefined;
2245
+ /**
2246
+ * One artifact override.
2247
+ *
2248
+ * @remarks
2249
+ * `content` replaces the rendered artifact at `path` and never partially
2250
+ * merges it. Legality is measured against every artifact the blueprint drafts,
2251
+ * before a compile narrows the returned groups. An override that matches none
2252
+ * of those artifacts, that targets a host-origin artifact, or that targets the
2253
+ * manifest is a blocking question rather than a silent no-op.
2254
+ */
2255
+ export declare interface Override {
2256
+ readonly path: string;
2257
+ readonly content: string;
2258
+ }
2399
2259
 
2400
- /**
2401
- * Build one `exports` condition block for a built environment.
2402
- *
2403
- * @param path - The extensionless `dist` path both conditions point at.
2404
- * @param formats - The module formats that environment builds.
2405
- * @returns The condition block: an `import` condition always, and a `require`
2406
- * condition only where a CommonJS build exists.
2407
- *
2408
- * @remarks
2409
- * The formats decide the shape, so no caller repeats the rule. An environment
2410
- * that builds ES only publishes an `import` condition alone rather than a
2411
- * `default` one, because a `default` condition answers `require` too and would
2412
- * hand a CommonJS consumer a module its loader cannot read.
2413
- *
2414
- * @example
2415
- * ```ts
2416
- * import { pathToCondition } from '@orkestrel/scaffold'
2417
- *
2418
- * pathToCondition('./dist/src/browser/index', ['es'])
2419
- * // { import: { types: './dist/src/browser/index.d.ts', default: './dist/src/browser/index.js' } }
2420
- * ```
2421
- */
2422
- export declare function pathToCondition(path: string, formats: readonly BuildFormat[]): Readonly<Record<string, unknown>>;
2260
+ /**
2261
+ * Measure a blueprint's overrides against the artifacts drafted for it.
2262
+ *
2263
+ * @param overrides - The blueprint's overrides.
2264
+ * @param artifacts - The drafted artifacts, before overrides are applied.
2265
+ * @returns One blocking question per override the draft cannot accept.
2266
+ *
2267
+ * @remarks
2268
+ * An override that matches no planned artifact is a caller expecting a file that
2269
+ * does not exist, and applying nothing would leave that expectation
2270
+ * unanswered. An override on a host-origin artifact asks this package to
2271
+ * rewrite a file it byte-copies from the vendored data root, which it never
2272
+ * does. An override on the manifest asks it to rewrite the one artifact the
2273
+ * blueprint's own fields decide, so the fields would no longer describe the
2274
+ * workspace they generated. Each is refused rather than dropped.
2275
+ *
2276
+ * @example
2277
+ * ```ts
2278
+ * import { blueprintToConfigArtifacts, createBlueprint, overridesToQuestions } from '@orkestrel/scaffold'
2279
+ *
2280
+ * const artifacts = blueprintToConfigArtifacts(createBlueprint('router', { src: ['core'] }))
2281
+ *
2282
+ * overridesToQuestions([{ path: 'package.json', content: '{}\n' }], artifacts).length // 1
2283
+ * ```
2284
+ */
2285
+ export declare function overridesToQuestions(overrides: readonly Override[], artifacts: readonly Artifact[]): readonly Question[];
2423
2286
 
2424
- /**
2425
- * The compiled, ordered artifact list and the selection it covers.
2426
- *
2427
- * @remarks
2428
- * `hash` is the plan's content identity and is absent until the pin stage
2429
- * fills it.
2430
- */
2431
- export declare interface Plan {
2432
- readonly blueprint: Blueprint;
2433
- readonly groups: readonly Group[];
2434
- readonly artifacts: readonly Artifact[];
2435
- readonly hash?: string;
2436
- }
2437
-
2438
- /** The tally of one plan by artifact origin. */
2439
- export declare interface PlanSummary {
2440
- readonly name: string;
2441
- readonly src: readonly Environment[];
2442
- readonly app: readonly Environment[];
2443
- readonly groups: readonly Group[];
2444
- readonly host: number;
2445
- readonly template: number;
2446
- readonly computed: number;
2447
- }
2448
-
2449
- /**
2450
- * Compare a plan against a target's current content.
2451
- *
2452
- * @param plan - The compiled plan.
2453
- * @param current - The target's exact bytes, keyed by artifact-relative path.
2454
- * @returns One finding per planned artifact in plan order, then one `foreign`
2455
- * finding per unplanned path in snapshot order.
2456
- *
2457
- * @remarks
2458
- * The sweep is bounded by the plan's own selection: a path the plan does not own
2459
- * is reported as foreign only when its group is one the plan covers, so a
2460
- * compile narrowed to a few groups never reports the rest of the workspace as
2461
- * unowned. Within a covered group the report is deliberately wide, because
2462
- * `foreign` is the set the destructive verb draws from and the narrowing that
2463
- * set needs — the paths no verb may remove, and what git tracks — belongs to
2464
- * that verb rather than to the comparison.
2465
- *
2466
- * @example
2467
- * ```ts
2468
- * import { createBlueprint, createCompiler, planToFindings } from '@orkestrel/scaffold'
2469
- *
2470
- * const { plan } = createCompiler().compile(createBlueprint('router', { src: ['core'] }))
2471
- *
2472
- * plan === undefined ? [] : planToFindings(plan, { 'AGENTS.md': '68690a' })
2473
- * ```
2474
- */
2475
- export declare function planToFindings(plan: Plan, current: Snapshot): readonly Finding[];
2287
+ /**
2288
+ * What scaffold claims at an artifact's path.
2289
+ *
2290
+ * @remarks
2291
+ * `content` claims the bytes: audit compares them, and a write restores a
2292
+ * missing file and replaces a stale one. `presence` claims only that the file
2293
+ * exists: audit compares existence, and a write restores an absent file and
2294
+ * never touches present bytes. `birth` claims only the file's creation: audit
2295
+ * never compares it and always reports it aligned, and a write creates it only
2296
+ * while it is absent.
2297
+ */
2298
+ export declare type Ownership = 'content' | 'presence' | 'birth';
2476
2299
 
2477
- /**
2478
- * Compute a plan's content identity.
2479
- *
2480
- * @param plan - The plan to identify.
2481
- * @returns Sixteen lowercase hexadecimal digits, or `undefined` when the plan
2482
- * carries a value JSON cannot encode.
2483
- *
2484
- * @remarks
2485
- * The identity covers the blueprint the plan was compiled from, the groups it
2486
- * covers, and its ordered artifacts. It deliberately excludes `hash` itself,
2487
- * which is what lets a pinned plan be re-identified and compared without
2488
- * stripping a field first.
2489
- *
2490
- * The projection is canonical, so two plans that differ only in key order
2491
- * answer the same digits. Nothing here reads a clock or randomness, and the
2492
- * refusal is total: a value that cannot be read answers `undefined` rather than
2493
- * escaping as a thrown error, and a caller decides what an unidentifiable plan
2494
- * means.
2495
- *
2496
- * @example
2497
- * ```ts
2498
- * import { createBlueprint, createCompiler, planToHash } from '@orkestrel/scaffold'
2499
- *
2500
- * const { plan } = createCompiler().compile(createBlueprint('router', { src: ['core'] }))
2501
- *
2502
- * plan === undefined ? undefined : planToHash(plan)?.length // 16
2503
- * ```
2504
- */
2505
- export declare function planToHash(plan: Plan): string | undefined;
2300
+ /**
2301
+ * Coerce an untrusted value to a {@link Blueprint}.
2302
+ *
2303
+ * @param value - The value to parse.
2304
+ * @returns The blueprint, or `undefined` when the value is not one.
2305
+ *
2306
+ * @remarks
2307
+ * Derived from {@link isBlueprint}, which is what makes the pair sound in both
2308
+ * directions: a guard-valid value is returned unchanged, and every value this
2309
+ * returns satisfies that guard. The reference is returned rather than copied,
2310
+ * so a caller that means to own the value clones it.
2311
+ *
2312
+ * @example
2313
+ * ```ts
2314
+ * import { parseBlueprint } from '@orkestrel/scaffold'
2315
+ *
2316
+ * parseBlueprint({ name: 'router' }) // undefined
2317
+ * ```
2318
+ */
2319
+ export declare function parseBlueprint(value: unknown): Blueprint | undefined;
2506
2320
 
2507
- /**
2508
- * Project a plan into its tally by artifact origin.
2509
- *
2510
- * @param plan - The plan to summarize.
2511
- * @returns The workspace's name, both environment axes, the covered groups, and
2512
- * one count per origin.
2513
- *
2514
- * @remarks
2515
- * Lossy on purpose: the summary is what a report prints, and it holds nothing a
2516
- * caller could mistake for the plan itself. The counts are derived on each call
2517
- * rather than stored on the plan, so they cannot disagree with the artifacts
2518
- * beside them.
2519
- *
2520
- * @example
2521
- * ```ts
2522
- * import { planToSummary } from '@orkestrel/scaffold'
2523
- *
2524
- * planToSummary(plan).computed // the number of computed artifacts
2525
- * ```
2526
- */
2527
- export declare function planToSummary(plan: Plan): PlanSummary;
2321
+ /**
2322
+ * Coerce an untrusted value to {@link CompilerOptions}.
2323
+ *
2324
+ * @param value - The value to parse.
2325
+ * @returns The options, or `undefined` when the value is not an option bag.
2326
+ *
2327
+ * @remarks
2328
+ * Derived from {@link isCompilerOptions}. Absence is not an option bag, so
2329
+ * `undefined` in returns `undefined` out and a constructor reads that as the
2330
+ * defaults rather than as a refusal.
2331
+ *
2332
+ * @example
2333
+ * ```ts
2334
+ * import { parseCompilerOptions } from '@orkestrel/scaffold'
2335
+ *
2336
+ * parseCompilerOptions({ on: { compile: () => {} } }) // the same record
2337
+ * parseCompilerOptions({ on: { compiled: () => {} } }) // undefined
2338
+ * ```
2339
+ */
2340
+ export declare function parseCompilerOptions(value: unknown): CompilerOptions | undefined;
2528
2341
 
2529
- /** Columns one emitted line may occupy, matching `printWidth` in `.oxfmtrc.json`. */
2530
- export declare const PRINT_WIDTH = 100;
2342
+ /**
2343
+ * Coerce an untrusted value to a group selection.
2344
+ *
2345
+ * @param value - The value to parse.
2346
+ * @returns The selection, or `undefined` when the value is not one.
2347
+ *
2348
+ * @remarks
2349
+ * Derived from {@link isGroups}. Order and repetition are preserved: which
2350
+ * groups a plan finally covers, and in which order, is the compiler's to decide
2351
+ * from `GROUPS`, not this boundary's.
2352
+ *
2353
+ * @example
2354
+ * ```ts
2355
+ * import { parseGroups } from '@orkestrel/scaffold'
2356
+ *
2357
+ * parseGroups(['manifest', 'configs']) // ['manifest', 'configs']
2358
+ * parseGroups(['readme']) // undefined
2359
+ * ```
2360
+ */
2361
+ export declare function parseGroups(value: unknown): readonly Group[] | undefined;
2531
2362
 
2532
- /**
2533
- * One validation issue raised against a blueprint or a plan.
2534
- *
2535
- * @remarks
2536
- * A blocking question fails the gate closed. A non-blocking question is an
2537
- * advisory that rides a complete result. `candidates` names the accepted
2538
- * values when the issue is a rejected choice.
2539
- */
2540
- export declare interface Question {
2541
- readonly field: string;
2542
- readonly message: string;
2543
- readonly blocking: boolean;
2544
- readonly candidates?: readonly string[];
2545
- }
2546
-
2547
- /**
2548
- * One declared dependency range measured against the registry's latest release.
2549
- *
2550
- * @remarks
2551
- * A found lookup carries the version upstream reported; one that produced no
2552
- * answer carries the cause and no version. Whether the declared range already
2553
- * admits that version is not recorded, because it is a deterministic function
2554
- * of `range` and `latest`: a stored answer could only disagree with the two
2555
- * fields beside it. One centralized helper decides it, and every caller reads
2556
- * the same decision.
2557
- */
2558
- export declare type Release = {
2559
- readonly name: string;
2560
- readonly range: string;
2561
- readonly lookup: 'found';
2562
- readonly latest: string;
2563
- readonly note?: never;
2564
- } | {
2565
- readonly name: string;
2566
- readonly range: string;
2567
- readonly lookup: 'missing' | 'failed';
2568
- readonly note: string;
2569
- readonly latest?: never;
2570
- };
2571
-
2572
- /**
2573
- * The one error this package throws, carrying the coded reason it was raised.
2574
- *
2575
- * @remarks
2576
- * Each code names one cause: `INVALID` for off-contract input, `DESTROYED` for
2577
- * any call made after teardown, `TARGET` for a destination that is not what the
2578
- * caller's observation said it was, `WRITE` for a mutation that could not be
2579
- * completed, `FETCH` for an upstream read that produced no answer the caller can
2580
- * be given, and `BLOCKED` for a refused blueprint.
2581
- *
2582
- * `BLOCKED` covers both refusals a blueprint can meet, because they are one fact
2583
- * — this blueprint will not be built — and the questions say which. The compiler
2584
- * answers its refusal rather than throwing it: the gate fails closed, returns the
2585
- * questions that closed it, and records `BLOCKED` on its stage, so a caller reads
2586
- * that refusal from the value it asked for. A verb that creates a workspace
2587
- * throws it, because it chose the shape and has nothing to hand back. A blocking
2588
- * question closed the gate; a non-blocking one is a shape this package can
2589
- * describe and declines to create.
2590
- *
2591
- * `context` carries whatever the raising site can say about the failure. It is
2592
- * `unknown` because nothing narrows it usefully at the catch site; read it for
2593
- * a report, never branch on it.
2594
- *
2595
- * @example
2596
- * ```ts
2597
- * import { ScaffoldError, isScaffoldError } from '@orkestrel/scaffold'
2598
- *
2599
- * try {
2600
- * throw new ScaffoldError('INVALID', 'Blueprint is not an exact record')
2601
- * } catch (error) {
2602
- * if (isScaffoldError(error)) error.code // 'INVALID'
2603
- * }
2604
- * ```
2605
- */
2606
- export declare class ScaffoldError extends Error {
2607
- readonly code: ScaffoldErrorCode;
2608
- readonly context?: unknown;
2609
- /**
2610
- * Construct a coded scaffold error.
2611
- *
2612
- * @param code - The coded reason the error is raised.
2613
- * @param message - What went wrong, in one sentence.
2614
- * @param context - Whatever the raising site can say about the failure.
2615
- */
2616
- constructor(code: ScaffoldErrorCode, message: string, context?: unknown);
2617
- }
2618
-
2619
- /** The coded reasons a scaffold error is raised. */
2620
- export declare type ScaffoldErrorCode = 'INVALID' | 'BLOCKED' | 'DESTROYED' | 'TARGET' | 'WRITE' | 'FETCH';
2621
-
2622
- /**
2623
- * The replayable outcome of one compile.
2624
- *
2625
- * @remarks
2626
- * `plan` is present exactly when the compile completed, so it is also the
2627
- * completeness test, and it carries the blueprint it was compiled from. A
2628
- * gated compile returns the questions that closed the gate and the stage
2629
- * records up to it, and no plan; the caller still holds the blueprint it
2630
- * passed in, so repeating it here would be one fact stored twice and free to
2631
- * disagree with itself.
2632
- */
2633
- export declare interface Scaffolding {
2634
- readonly plan?: Plan;
2635
- readonly questions: readonly Question[];
2636
- readonly stages: readonly CompileRecord[];
2637
- readonly blueprint?: never;
2638
- }
2639
-
2640
- /**
2641
- * Select the groups a compile covers, in plan order.
2642
- *
2643
- * @param groups - The requested selection; every group when absent.
2644
- * @returns The requested groups in `GROUPS` order, without repeats.
2645
- *
2646
- * @remarks
2647
- * A caller's selection is data, so it arrives in whatever order and with
2648
- * whatever repeats the caller wrote. Plan order is this package's, so the
2649
- * selection is read as membership and the order comes from `GROUPS`. An empty
2650
- * selection covers nothing, which is a caller asking for an empty plan rather
2651
- * than a caller asking for everything.
2652
- *
2653
- * @example
2654
- * ```ts
2655
- * import { selectGroups } from '@orkestrel/scaffold'
2656
- *
2657
- * selectGroups(['tests', 'manifest', 'tests']) // ['manifest', 'tests']
2658
- * selectGroups() // every group, in plan order
2659
- * ```
2660
- */
2661
- export declare function selectGroups(groups?: readonly Group[]): readonly Group[];
2363
+ /**
2364
+ * Coerce an untrusted value to a {@link Snapshot}.
2365
+ *
2366
+ * @param value - The value to parse.
2367
+ * @returns The snapshot, or `undefined` when the value is not one.
2368
+ *
2369
+ * @remarks
2370
+ * Derived from {@link isSnapshot}.
2371
+ *
2372
+ * @example
2373
+ * ```ts
2374
+ * import { parseSnapshot } from '@orkestrel/scaffold'
2375
+ *
2376
+ * parseSnapshot({ 'AGENTS.md': '68690a' }) // { 'AGENTS.md': '68690a' }
2377
+ * parseSnapshot({ 'AGENTS.md': null }) // undefined
2378
+ * ```
2379
+ */
2380
+ export declare function parseSnapshot(value: unknown): Snapshot | undefined;
2662
2381
 
2663
- /**
2664
- * Select the host paths a named workspace vendors.
2665
- *
2666
- * @param paths - The candidate host paths, in their declared order.
2667
- * @param name - The target workspace's own bare package name.
2668
- * @returns Every candidate except the workspace's own guide, in input order.
2669
- *
2670
- * @remarks
2671
- * `HOST_PATHS` is a candidate set rather than a plan, because a workspace never
2672
- * mirrors its own guide: that file is the workspace's own product, and vendoring
2673
- * it would have the target overwrite its guide with the copy it published.
2674
- *
2675
- * @example
2676
- * ```ts
2677
- * import { HOST_PATHS, selectHostPaths } from '@orkestrel/scaffold'
2678
- *
2679
- * selectHostPaths(HOST_PATHS, 'scaffold').includes('guides/scaffold.md') // false
2680
- * ```
2681
- */
2682
- export declare function selectHostPaths(paths: readonly string[], name: string): readonly string[];
2382
+ /**
2383
+ * Build one `exports` condition block for a built environment.
2384
+ *
2385
+ * @param path - The extensionless `dist` path both conditions point at.
2386
+ * @param formats - The module formats that environment builds.
2387
+ * @returns The condition block: an `import` condition always, and a `require`
2388
+ * condition only where a CommonJS build exists.
2389
+ *
2390
+ * @remarks
2391
+ * The formats decide the shape, so no caller repeats the rule. An environment
2392
+ * that builds ES only publishes an `import` condition alone rather than a
2393
+ * `default` one, because a `default` condition answers `require` too and would
2394
+ * hand a CommonJS consumer a module its loader cannot read.
2395
+ *
2396
+ * @example
2397
+ * ```ts
2398
+ * import { pathToCondition } from '@orkestrel/scaffold'
2399
+ *
2400
+ * pathToCondition('./dist/src/browser/index', ['es'])
2401
+ * // { import: { types: './dist/src/browser/index.d.ts', default: './dist/src/browser/index.js' } }
2402
+ * ```
2403
+ */
2404
+ export declare function pathToCondition(path: string, formats: readonly BuildFormat[]): Readonly<Record<string, unknown>>;
2683
2405
 
2684
- /**
2685
- * Serialize one string as a single-quoted TypeScript literal.
2686
- *
2687
- * @param value - The string to serialize.
2688
- * @returns A complete single-quoted literal with line-breaking and delimiter
2689
- * characters escaped.
2690
- *
2691
- * @remarks
2692
- * Configuration templates insert blueprint-derived strings into TypeScript.
2693
- * Keeping this one serializer at that boundary prevents a name from becoming
2694
- * syntax and preserves oxfmt's configured single-quote fixed point.
2695
- *
2696
- * @example
2697
- * ```ts
2698
- * import { serializeTypeScriptString } from '@orkestrel/scaffold'
2699
- *
2700
- * serializeTypeScriptString("it's") // `'it\\'s'`
2701
- * ```
2702
- */
2703
- export declare function serializeTypeScriptString(value: string): string;
2406
+ /**
2407
+ * The compiled, ordered artifact list and the selection it covers.
2408
+ *
2409
+ * @remarks
2410
+ * `hash` is the plan's content identity and is absent until the pin stage
2411
+ * fills it.
2412
+ */
2413
+ export declare interface Plan {
2414
+ readonly blueprint: Blueprint;
2415
+ readonly groups: readonly Group[];
2416
+ readonly artifacts: readonly Artifact[];
2417
+ readonly hash?: string;
2418
+ }
2419
+
2420
+ /** The tally of one plan by artifact origin. */
2421
+ export declare interface PlanSummary {
2422
+ readonly name: string;
2423
+ readonly src: readonly Environment[];
2424
+ readonly app: readonly Environment[];
2425
+ readonly groups: readonly Group[];
2426
+ readonly host: number;
2427
+ readonly template: number;
2428
+ readonly computed: number;
2429
+ }
2704
2430
 
2705
- /** The provisioner skeleton a workspace with declared service vendors is given once. */
2706
- export declare const SERVICE_SCRIPT_PATH = "scripts/service.sh";
2431
+ /**
2432
+ * Compare a plan against a target's current content.
2433
+ *
2434
+ * @param plan - The compiled plan.
2435
+ * @param current - The target's exact bytes, keyed by artifact-relative path.
2436
+ * @returns One finding per planned artifact in plan order, then one `foreign`
2437
+ * finding per unplanned path in snapshot order.
2438
+ *
2439
+ * @remarks
2440
+ * The sweep is bounded by the plan's own selection: a path the plan does not own
2441
+ * is reported as foreign only when its group is one the plan covers, so a
2442
+ * compile narrowed to a few groups never reports the rest of the workspace as
2443
+ * unowned. Within a covered group the report is deliberately wide, because
2444
+ * `foreign` is the set the destructive verb draws from and the narrowing that
2445
+ * set needs — the paths no verb may remove, and what git tracks — belongs to
2446
+ * that verb rather than to the comparison.
2447
+ *
2448
+ * @example
2449
+ * ```ts
2450
+ * import { Compiler, createBlueprint, planToFindings } from '@orkestrel/scaffold'
2451
+ *
2452
+ * const { plan } = new Compiler().compile(createBlueprint('router', { src: ['core'] }))
2453
+ *
2454
+ * plan === undefined ? [] : planToFindings(plan, { 'AGENTS.md': '68690a' })
2455
+ * ```
2456
+ */
2457
+ export declare function planToFindings(plan: Plan, current: Snapshot): readonly Finding[];
2707
2458
 
2708
- /** The live-service readiness module whose presence makes a workspace `service`. */
2709
- export declare const SERVICE_SETUP_PATH = "tests/setupService.ts";
2459
+ /**
2460
+ * Compute a plan's content identity.
2461
+ *
2462
+ * @param plan - The plan to identify.
2463
+ * @returns Sixteen lowercase hexadecimal digits, or `undefined` when the plan
2464
+ * carries a value JSON cannot encode.
2465
+ *
2466
+ * @remarks
2467
+ * The identity covers the blueprint the plan was compiled from, the groups it
2468
+ * covers, and its ordered artifacts. It deliberately excludes `hash` itself,
2469
+ * which is what lets a pinned plan be re-identified and compared without
2470
+ * stripping a field first.
2471
+ *
2472
+ * The projection is canonical, so two plans that differ only in key order
2473
+ * answer the same digits. Nothing here reads a clock or randomness, and the
2474
+ * refusal is total: a value that cannot be read answers `undefined` rather than
2475
+ * escaping as a thrown error, and a caller decides what an unidentifiable plan
2476
+ * means.
2477
+ *
2478
+ * @example
2479
+ * ```ts
2480
+ * import { Compiler, createBlueprint, planToHash } from '@orkestrel/scaffold'
2481
+ *
2482
+ * const { plan } = new Compiler().compile(createBlueprint('router', { src: ['core'] }))
2483
+ *
2484
+ * plan === undefined ? undefined : planToHash(plan)?.length // 16
2485
+ * ```
2486
+ */
2487
+ export declare function planToHash(plan: Plan): string | undefined;
2710
2488
 
2711
- /** The include the live-service project covers, which is a directory rather than one proof. */
2712
- export declare const SERVICE_TEST_INCLUDE = "tests/service/**/*.test.ts";
2489
+ /**
2490
+ * Project a plan into its tally by artifact origin.
2491
+ *
2492
+ * @param plan - The plan to summarize.
2493
+ * @returns The workspace's name, both environment axes, the covered groups, and
2494
+ * one count per origin.
2495
+ *
2496
+ * @remarks
2497
+ * Lossy on purpose: the summary is what a report prints, and it holds nothing a
2498
+ * caller could mistake for the plan itself. The counts are derived on each call
2499
+ * rather than stored on the plan, so they cannot disagree with the artifacts
2500
+ * beside them.
2501
+ *
2502
+ * @example
2503
+ * ```ts
2504
+ * import { planToSummary } from '@orkestrel/scaffold'
2505
+ *
2506
+ * planToSummary(plan).computed // the number of computed artifacts
2507
+ * ```
2508
+ */
2509
+ export declare function planToSummary(plan: Plan): PlanSummary;
2713
2510
 
2714
- /** The Vite wrapper whose presence makes a workspace `showcase`. */
2715
- export declare const SHOWCASE_CONFIG_PATH = "configs/app/vite.showcase.config.ts";
2511
+ /** Columns one emitted line may occupy, matching `printWidth` in `.oxfmtrc.json`. */
2512
+ export declare const PRINT_WIDTH = 100;
2716
2513
 
2717
- /**
2718
- * The development dependency used only by the optional single-file showcase build.
2719
- *
2720
- * @example
2721
- * ```ts
2722
- * import { SHOWCASE_DEV_DEPENDENCIES } from '@orkestrel/scaffold'
2723
- *
2724
- * SHOWCASE_DEV_DEPENDENCIES['vite-plugin-singlefile'] // '^2.3.3'
2725
- * ```
2726
- */
2727
- export declare const SHOWCASE_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
2514
+ /**
2515
+ * One validation issue raised against a blueprint or a plan.
2516
+ *
2517
+ * @remarks
2518
+ * A blocking question fails the gate closed. A non-blocking question is an
2519
+ * advisory that rides a complete result. `candidates` names the accepted
2520
+ * values when the issue is a rejected choice.
2521
+ */
2522
+ export declare interface Question {
2523
+ readonly field: string;
2524
+ readonly message: string;
2525
+ readonly blocking: boolean;
2526
+ readonly candidates?: readonly string[];
2527
+ }
2728
2528
 
2729
- /** Exact lowercase hexadecimal target bytes keyed by artifact-relative path. */
2730
- export declare type Snapshot = Readonly<Record<string, string>>;
2529
+ /**
2530
+ * One declared dependency range measured against the registry's latest release.
2531
+ *
2532
+ * @remarks
2533
+ * A found lookup carries the version upstream reported; one that produced no
2534
+ * answer carries the cause and no version. Whether the declared range already
2535
+ * admits that version is not recorded, because it is a deterministic function
2536
+ * of `range` and `latest`: a stored answer could only disagree with the
2537
+ * fields beside it. One centralized helper decides it, and every caller reads
2538
+ * the same decision.
2539
+ */
2540
+ export declare type Release = {
2541
+ readonly name: string;
2542
+ readonly range: string;
2543
+ readonly lookup: 'found';
2544
+ readonly latest: string;
2545
+ readonly note?: never;
2546
+ } | {
2547
+ readonly name: string;
2548
+ readonly range: string;
2549
+ readonly lookup: 'missing' | 'failed';
2550
+ readonly note: string;
2551
+ readonly latest?: never;
2552
+ };
2731
2553
 
2732
- /** The development dependencies a published browser `src` environment adds. */
2733
- export declare const SOURCE_BROWSER_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
2554
+ /**
2555
+ * The one error this package throws, carrying the coded reason it was raised.
2556
+ *
2557
+ * @remarks
2558
+ * Each code names one cause: `INVALID` for off-contract input, `DESTROYED` for
2559
+ * any call made after teardown, `TARGET` for a destination that is not what the
2560
+ * caller's observation said it was, `WRITE` for a mutation that could not be
2561
+ * completed, `FETCH` for an upstream read that produced no answer the caller can
2562
+ * be given, and `BLOCKED` for a refused blueprint.
2563
+ *
2564
+ * `BLOCKED` covers both refusals a blueprint can meet, because they are one fact
2565
+ * — this blueprint will not be built — and the questions say which. The compiler
2566
+ * answers its refusal rather than throwing it: the gate fails closed, returns the
2567
+ * questions that closed it, and records `BLOCKED` on its stage, so a caller reads
2568
+ * that refusal from the value it asked for. A verb that creates a workspace
2569
+ * throws it, because it chose the shape and has nothing to hand back. A blocking
2570
+ * question closed the gate; a non-blocking one is a shape this package can
2571
+ * describe and declines to create.
2572
+ *
2573
+ * `context` carries whatever the raising site can say about the failure. It is
2574
+ * `unknown` because nothing narrows it usefully at the catch site; read it for
2575
+ * a report, never branch on it.
2576
+ *
2577
+ * @example
2578
+ * ```ts
2579
+ * import { ScaffoldError, isScaffoldError } from '@orkestrel/scaffold'
2580
+ *
2581
+ * try {
2582
+ * throw new ScaffoldError('INVALID', 'Blueprint is not an exact record')
2583
+ * } catch (error) {
2584
+ * if (isScaffoldError(error)) error.code // 'INVALID'
2585
+ * }
2586
+ * ```
2587
+ */
2588
+ export declare class ScaffoldError extends Error {
2589
+ readonly code: ScaffoldErrorCode;
2590
+ readonly context?: unknown;
2591
+ /**
2592
+ * Construct a coded scaffold error.
2593
+ *
2594
+ * @param code - The coded reason the error is raised.
2595
+ * @param message - What went wrong, in one sentence.
2596
+ * @param context - Whatever the raising site can say about the failure.
2597
+ */
2598
+ constructor(code: ScaffoldErrorCode, message: string, context?: unknown);
2599
+ }
2734
2600
 
2735
- /**
2736
- * The build and export settings each published `src` environment contributes, frozen.
2737
- *
2738
- * @remarks
2739
- * Per environment: the thin configuration files it adds under `configs/src`,
2740
- * its Vitest project label, its `exports` subpath, and the module formats it
2741
- * builds. Core alone occupies the package root, so it is the only environment
2742
- * whose subpath is `.`; browser ships ES only because no CommonJS consumer
2743
- * reaches it.
2744
- */
2745
- export declare const SRC_MATRIX: Readonly<Record<Environment, SrcDefinition>>;
2746
-
2747
- /** The build and export settings one published `src` environment contributes. */
2748
- export declare interface SrcDefinition {
2749
- readonly configs: readonly string[];
2750
- readonly project: string;
2751
- readonly path: string;
2752
- readonly formats: readonly BuildFormat[];
2753
- }
2754
-
2755
- /**
2756
- * Project a published selection into the manifest's entry fields.
2757
- *
2758
- * @param src - The declared published environments.
2759
- * @returns The `main` and `module` fields, plus `types` when one environment
2760
- * owns the package root.
2761
- *
2762
- * @remarks
2763
- * `main` follows the root environment's own formats, so an environment that
2764
- * builds ES only points both fields at the same file rather than promising a
2765
- * CommonJS build that never runs. A selection with several environments carries
2766
- * no top-level `types`, because each environment declares its own under its
2767
- * subpath and a single top-level field could only name one of them.
2768
- *
2769
- * @example
2770
- * ```ts
2771
- * import { srcToEntry } from '@orkestrel/scaffold'
2772
- *
2773
- * srcToEntry(['core']).main // './dist/src/core/index.cjs'
2774
- * srcToEntry(['browser']).main // './dist/src/browser/index.js'
2775
- * ```
2776
- */
2777
- export declare function srcToEntry(src: readonly Environment[]): {
2778
- readonly main: string;
2779
- readonly module: string;
2780
- readonly types?: string;
2781
- };
2782
-
2783
- /**
2784
- * Project a published selection into the manifest's `exports` map.
2785
- *
2786
- * @param src - The declared published environments.
2787
- * @returns The map, keyed by subpath in `ENVIRONMENTS` order.
2788
- *
2789
- * @remarks
2790
- * One environment owns the package root and every other declared environment
2791
- * takes the subpath its `SRC_MATRIX` row names, so the map never invents a
2792
- * subpath. `./package.json` is published alongside, which is what lets a
2793
- * consumer's tooling read the manifest of a package whose exports are otherwise
2794
- * closed. A selection publishing nothing answers an empty map rather than a
2795
- * core-rooted one, because a workspace with no published environment declares
2796
- * no exports at all.
2797
- *
2798
- * @example
2799
- * ```ts
2800
- * import { srcToExports } from '@orkestrel/scaffold'
2801
- *
2802
- * Object.keys(srcToExports(['core', 'server'])) // ['.', './server', './package.json']
2803
- * srcToExports([]) // {}
2804
- * ```
2805
- */
2806
- export declare function srcToExports(src: readonly Environment[]): Readonly<Record<string, unknown>>;
2601
+ /** The coded reasons a scaffold error is raised. */
2602
+ export declare type ScaffoldErrorCode = 'INVALID' | 'BLOCKED' | 'DESTROYED' | 'TARGET' | 'WRITE' | 'FETCH';
2807
2603
 
2808
- /**
2809
- * Select the single published environment a package root points at.
2810
- *
2811
- * @param src - The declared published environments.
2812
- * @returns That environment, or `undefined` when the selection declares none or
2813
- * several.
2814
- *
2815
- * @remarks
2816
- * A workspace publishing exactly one environment puts it at the package root,
2817
- * so its entry fields and its `'.'` export condition both name that
2818
- * environment's build. A workspace publishing several puts core at the root and
2819
- * gives every other environment a subpath, so there is no single root to name.
2820
- * Both callers read the same answer, which is why the branch is decided once
2821
- * here rather than twice.
2822
- *
2823
- * @example
2824
- * ```ts
2825
- * import { srcToRoot } from '@orkestrel/scaffold'
2826
- *
2827
- * srcToRoot(['browser']) // 'browser'
2828
- * srcToRoot(['core', 'server']) // undefined
2829
- * ```
2830
- */
2831
- export declare function srcToRoot(src: readonly Environment[]): Environment | undefined;
2604
+ /**
2605
+ * The replayable outcome of one compile.
2606
+ *
2607
+ * @remarks
2608
+ * `plan` is present exactly when the compile completed, so it is also the
2609
+ * completeness test, and `scaffolding.plan.blueprint` carries the blueprint it
2610
+ * was compiled from. A gated compile returns the questions that closed the gate
2611
+ * and the stage records up to it, and no plan; the caller still holds the
2612
+ * blueprint it passed in, so repeating it at this level would be one fact stored
2613
+ * twice and free to disagree with itself.
2614
+ */
2615
+ export declare interface Scaffolding {
2616
+ readonly plan?: Plan;
2617
+ readonly questions: readonly Question[];
2618
+ readonly stages: readonly CompileRecord[];
2619
+ }
2832
2620
 
2833
- /** Columns one tab occupies when the formatter measures a line, matching `tabWidth`. */
2834
- export declare const TAB_WIDTH = 2;
2621
+ /**
2622
+ * Select the groups a compile covers, in plan order.
2623
+ *
2624
+ * @param groups - The requested selection; every group when absent.
2625
+ * @returns The requested groups in `GROUPS` order, without repeats.
2626
+ *
2627
+ * @remarks
2628
+ * A caller's selection is data, so it arrives in whatever order and with
2629
+ * whatever repeats the caller wrote. Plan order is this package's, so the
2630
+ * selection is read as membership and the order comes from `GROUPS`. An empty
2631
+ * selection covers nothing, which is a caller asking for an empty plan rather
2632
+ * than a caller asking for everything.
2633
+ *
2634
+ * @example
2635
+ * ```ts
2636
+ * import { selectGroups } from '@orkestrel/scaffold'
2637
+ *
2638
+ * selectGroups(['tests', 'manifest', 'tests']) // ['manifest', 'tests']
2639
+ * selectGroups() // every group, in plan order
2640
+ * ```
2641
+ */
2642
+ export declare function selectGroups(groups?: readonly Group[]): readonly Group[];
2835
2643
 
2836
- /** The exact three-component version syntax a blueprint declares. */
2837
- export declare const VERSION_PATTERN: RegExp;
2644
+ /**
2645
+ * Select the host paths a named workspace vendors.
2646
+ *
2647
+ * @param paths - The candidate host paths, in their declared order.
2648
+ * @param name - The target workspace's own bare package name.
2649
+ * @returns Every candidate except the workspace's own guide, in input order.
2650
+ *
2651
+ * @remarks
2652
+ * `HOST_PATHS` is a candidate set rather than a plan, because a workspace never
2653
+ * mirrors its own guide: that file is the workspace's own product, and vendoring
2654
+ * it would have the target overwrite its guide with the copy it published.
2655
+ *
2656
+ * @example
2657
+ * ```ts
2658
+ * import { HOST_PATHS, selectHostPaths } from '@orkestrel/scaffold'
2659
+ *
2660
+ * selectHostPaths(HOST_PATHS, 'scaffold').includes('guides/scaffold.md') // false
2661
+ * ```
2662
+ */
2663
+ export declare function selectHostPaths(paths: readonly string[], name: string): readonly string[];
2838
2664
 
2839
- /**
2840
- * Which host-specific pipelines a generated root Vite configuration carries.
2841
- *
2842
- * @remarks
2843
- * Boundary guarantees never vary by blueprint, so they are not selected here:
2844
- * every generated configuration emits the environment-boundary plugin, its
2845
- * module-graph audit, and stylesheet rejection. `browser` selects the shared
2846
- * root CSS analysis and real-browser test machinery. `vue` selects the
2847
- * single-file-component, HTML, and development-server machinery an application
2848
- * browser environment needs. `output` selects build-output containment.
2849
- * `showcase` selects the optional single-file application-browser projection.
2850
- */
2851
- export declare interface ViteMachinery {
2852
- readonly browser: boolean;
2853
- readonly vue: boolean;
2854
- readonly output: boolean;
2855
- readonly showcase: boolean;
2856
- }
2857
-
2858
- /**
2859
- * The vendored paths whose present bytes belong to each workspace, frozen.
2860
- *
2861
- * @remarks
2862
- * These paths are copied into a workspace when absent and are never compared
2863
- * or replaced while present. A workspace therefore stops receiving later
2864
- * canonical updates to them. `.gitignore` takes that trade because its correct
2865
- * rules differ by workspace, so scaffold cannot own its bytes.
2866
- */
2867
- export declare const WORKSPACE_OWNED_PATHS: readonly string[];
2665
+ /**
2666
+ * Serialize one string as a single-quoted TypeScript literal.
2667
+ *
2668
+ * @param value - The string to serialize.
2669
+ * @returns A complete single-quoted literal with line-breaking and delimiter
2670
+ * characters escaped.
2671
+ *
2672
+ * @remarks
2673
+ * Configuration templates insert blueprint-derived strings into TypeScript.
2674
+ * Keeping this one serializer at that boundary prevents a name from becoming
2675
+ * syntax and preserves oxfmt's configured single-quote fixed point.
2676
+ *
2677
+ * @example
2678
+ * ```ts
2679
+ * import { serializeTypeScriptString } from '@orkestrel/scaffold'
2680
+ *
2681
+ * serializeTypeScriptString("it's") // `'it\\'s'`
2682
+ * ```
2683
+ */
2684
+ export declare function serializeTypeScriptString(value: string): string;
2685
+
2686
+ /** The provisioner skeleton a workspace with declared service vendors is given once. */
2687
+ export declare const SERVICE_SCRIPT_PATH = "scripts/service.sh";
2688
+
2689
+ /** The live-service readiness module whose presence makes a workspace `service`. */
2690
+ export declare const SERVICE_SETUP_PATH = "tests/setupService.ts";
2691
+
2692
+ /** The include the live-service project covers, which is a directory rather than one proof. */
2693
+ export declare const SERVICE_TEST_INCLUDE = "tests/service/**/*.test.ts";
2694
+
2695
+ /** The Vite wrapper whose presence makes a workspace `showcase`. */
2696
+ export declare const SHOWCASE_CONFIG_PATH = "configs/app/vite.showcase.config.ts";
2697
+
2698
+ /**
2699
+ * The development dependency used only by the optional single-file showcase build.
2700
+ *
2701
+ * @example
2702
+ * ```ts
2703
+ * import { SHOWCASE_DEV_DEPENDENCIES } from '@orkestrel/scaffold'
2704
+ *
2705
+ * SHOWCASE_DEV_DEPENDENCIES['vite-plugin-singlefile'] // '^2.3.3'
2706
+ * ```
2707
+ */
2708
+ export declare const SHOWCASE_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
2709
+
2710
+ /** Exact lowercase hexadecimal target bytes keyed by artifact-relative path. */
2711
+ export declare type Snapshot = Readonly<Record<string, string>>;
2712
+
2713
+ /** The development dependencies a published browser `src` environment adds. */
2714
+ export declare const SOURCE_BROWSER_DEV_DEPENDENCIES: Readonly<Record<string, string>>;
2715
+
2716
+ /**
2717
+ * The build and export settings each published `src` environment contributes, frozen.
2718
+ *
2719
+ * @remarks
2720
+ * Per environment: the thin configuration files it adds under `configs/src`,
2721
+ * its Vitest project label, its `exports` subpath, and the module formats it
2722
+ * builds. Core alone occupies the package root, so it is the only environment
2723
+ * whose subpath is `.`; browser ships ES only because no CommonJS consumer
2724
+ * reaches it.
2725
+ */
2726
+ export declare const SRC_MATRIX: Readonly<Record<Environment, SrcDefinition>>;
2727
+
2728
+ /** The build and export settings one published `src` environment contributes. */
2729
+ export declare interface SrcDefinition {
2730
+ readonly configs: readonly string[];
2731
+ readonly project: string;
2732
+ readonly path: string;
2733
+ readonly formats: readonly BuildFormat[];
2734
+ }
2735
+
2736
+ /**
2737
+ * Project a published selection into the manifest's entry fields.
2738
+ *
2739
+ * @param src - The declared published environments.
2740
+ * @returns The `main` and `module` fields, plus `types` when one environment
2741
+ * owns the package root.
2742
+ *
2743
+ * @remarks
2744
+ * `main` follows the root environment's own formats, so an environment that
2745
+ * builds ES only points both fields at the same file rather than promising a
2746
+ * CommonJS build that never runs. A selection with several environments carries
2747
+ * no top-level `types`, because each environment declares its own under its
2748
+ * subpath and a single top-level field could only name one of them.
2749
+ *
2750
+ * @example
2751
+ * ```ts
2752
+ * import { srcToEntry } from '@orkestrel/scaffold'
2753
+ *
2754
+ * srcToEntry(['core']).main // './dist/src/core/index.cjs'
2755
+ * srcToEntry(['browser']).main // './dist/src/browser/index.js'
2756
+ * ```
2757
+ */
2758
+ export declare function srcToEntry(src: readonly Environment[]): {
2759
+ readonly main: string;
2760
+ readonly module: string;
2761
+ readonly types?: string;
2762
+ };
2763
+
2764
+ /**
2765
+ * Project a published selection into the manifest's `exports` map.
2766
+ *
2767
+ * @param src - The declared published environments.
2768
+ * @returns The map, keyed by subpath in `ENVIRONMENTS` order.
2769
+ *
2770
+ * @remarks
2771
+ * One environment owns the package root and every other declared environment
2772
+ * takes the subpath its `SRC_MATRIX` row names, so the map never invents a
2773
+ * subpath. `./package.json` is published alongside, which is what lets a
2774
+ * consumer's tooling read the manifest of a package whose exports are otherwise
2775
+ * closed. A selection publishing nothing answers an empty map rather than a
2776
+ * core-rooted one, because a workspace with no published environment declares
2777
+ * no exports at all.
2778
+ *
2779
+ * @example
2780
+ * ```ts
2781
+ * import { srcToExports } from '@orkestrel/scaffold'
2782
+ *
2783
+ * Object.keys(srcToExports(['core', 'server'])) // ['.', './server', './package.json']
2784
+ * srcToExports([]) // {}
2785
+ * ```
2786
+ */
2787
+ export declare function srcToExports(src: readonly Environment[]): Readonly<Record<string, unknown>>;
2788
+
2789
+ /**
2790
+ * Select the single published environment a package root points at.
2791
+ *
2792
+ * @param src - The declared published environments.
2793
+ * @returns That environment, or `undefined` when the selection declares none or
2794
+ * several.
2795
+ *
2796
+ * @remarks
2797
+ * A workspace publishing exactly one environment puts it at the package root,
2798
+ * so its entry fields and its `'.'` export condition both name that
2799
+ * environment's build. A workspace publishing several puts core at the root and
2800
+ * gives every other environment a subpath, so there is no single root to name.
2801
+ * Both callers read the same answer, which is why the branch is decided once
2802
+ * here rather than twice.
2803
+ *
2804
+ * @example
2805
+ * ```ts
2806
+ * import { srcToRoot } from '@orkestrel/scaffold'
2807
+ *
2808
+ * srcToRoot(['browser']) // 'browser'
2809
+ * srcToRoot(['core', 'server']) // undefined
2810
+ * ```
2811
+ */
2812
+ export declare function srcToRoot(src: readonly Environment[]): Environment | undefined;
2813
+
2814
+ /** Columns one tab occupies when the formatter measures a line, matching `tabWidth`. */
2815
+ export declare const TAB_WIDTH = 2;
2816
+
2817
+ /** The exact `major.minor.patch` version syntax a blueprint declares. */
2818
+ export declare const VERSION_PATTERN: RegExp;
2819
+
2820
+ /**
2821
+ * Which host-specific pipelines a generated root Vite configuration carries.
2822
+ *
2823
+ * @remarks
2824
+ * Boundary guarantees never vary by blueprint, so they are not selected here:
2825
+ * every generated configuration emits the environment-boundary plugin, its
2826
+ * module-graph audit, and stylesheet rejection. `browser` selects the shared
2827
+ * root CSS analysis and real-browser test machinery. `vue` selects the
2828
+ * single-file-component, HTML, and development-server machinery an application
2829
+ * browser environment needs. `output` selects build-output containment.
2830
+ * `showcase` selects the optional single-file application-browser projection.
2831
+ */
2832
+ export declare interface ViteMachinery {
2833
+ readonly browser: boolean;
2834
+ readonly vue: boolean;
2835
+ readonly output: boolean;
2836
+ readonly showcase: boolean;
2837
+ }
2838
+
2839
+ /**
2840
+ * The vendored paths whose present bytes belong to each workspace, frozen.
2841
+ *
2842
+ * @remarks
2843
+ * These paths are copied into a workspace when absent and are never compared
2844
+ * or replaced while present. A workspace therefore stops receiving later
2845
+ * canonical updates to them. `.gitignore` takes that trade because its correct
2846
+ * rules differ by workspace, so scaffold cannot own its bytes.
2847
+ */
2848
+ export declare const WORKSPACE_OWNED_PATHS: readonly string[];
2868
2849
 
2869
- export { }
2850
+ export { }