@orkestrel/scaffold 0.0.80 → 0.0.82

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 (36) hide show
  1. package/dist/bin/main.js +167 -13
  2. package/dist/bin/main.js.map +1 -1
  3. package/dist/host/AGENTS.md +5 -3
  4. package/dist/host/agents/skills/orkestrel-harden/references/centralization.md +2 -2
  5. package/dist/host/agents/skills/orkestrel-journey/SKILL.md +41 -38
  6. package/dist/host/agents/skills/orkestrel-journey/references/captures.md +3 -3
  7. package/dist/host/agents/skills/orkestrel-journey/references/decide.md +3 -3
  8. package/dist/host/agents/skills/orkestrel-journey/references/layer.md +5 -5
  9. package/dist/host/agents/skills/orkestrel-journey/references/statechart.md +3 -3
  10. package/dist/host/agents/skills/orkestrel-journey/references/styles.md +9 -9
  11. package/dist/host/claude/agents/orkestrel.md +52 -52
  12. package/dist/host/claude/rules/application.md +20 -6
  13. package/dist/host/claude/rules/architecture.md +2 -2
  14. package/dist/host/claude/rules/browser.md +9 -0
  15. package/dist/host/claude/rules/documentation.md +2 -1
  16. package/dist/host/claude/rules/styles.md +35 -12
  17. package/dist/host/claude/rules/tests.md +15 -10
  18. package/dist/host/claude/rules/workspace.md +128 -98
  19. package/dist/host/claude/skills/orkestrel-journey/SKILL.md +1 -1
  20. package/dist/host/configs/helpers.ts +157 -9
  21. package/dist/host/configs/policy.ts +64 -61
  22. package/dist/host/dotfiles/oxlintrc.json +132 -16
  23. package/dist/host/dotfiles/prettierignore +1 -1
  24. package/dist/host/guides/README.md +9 -5
  25. package/dist/host/guides/scaffold.md +475 -115
  26. package/dist/host/manifest.json +27 -27
  27. package/dist/host/tests/config.test.ts +996 -39
  28. package/dist/host/tests/policy.test.ts +11 -0
  29. package/dist/host/tests/setupPolicy.ts +396 -22
  30. package/dist/src/core/index.cjs +1263 -161
  31. package/dist/src/core/index.cjs.map +1 -1
  32. package/dist/src/core/index.d.cts +297 -35
  33. package/dist/src/core/index.d.ts +297 -35
  34. package/dist/src/core/index.js +1243 -162
  35. package/dist/src/core/index.js.map +1 -1
  36. package/package.json +3 -3
@@ -20,6 +20,7 @@ export interface PolicyExpression extends PolicyNode {
20
20
  readonly callee?: PolicyExpression
21
21
  readonly argument?: PolicyExpression | null
22
22
  readonly arguments?: readonly PolicyExpression[]
23
+ readonly elements?: ReadonlyArray<PolicyExpression | null>
23
24
  readonly body?: PolicyExpression | readonly PolicyExpression[]
24
25
  readonly quasis?: readonly PolicyExpression[]
25
26
  readonly expressions?: readonly PolicyExpression[]
@@ -514,39 +515,65 @@ export function isPolicyDeclaredFunction(node: PolicyExpression): boolean {
514
515
  return node.type === 'FunctionDeclaration' || node.type === 'TSDeclareFunction'
515
516
  }
516
517
 
517
- /** Reports whether a policy function is anonymous. */
518
- export function isPolicyAnonymous(node: PolicyExpression): boolean {
519
- return node.type === 'ArrowFunctionExpression' || node.id === null
520
- }
521
-
522
- /** Returns the outermost parenthesized expression holding a policy function. */
518
+ /** Returns a policy function's position through parentheses, object values, and array elements. */
523
519
  export function functionToPolicyPosition(node: PolicyExpression): PolicyExpression {
524
520
  let position = node
525
- while (position.parent?.type === 'ParenthesizedExpression') {
526
- position = position.parent
521
+ while (position.parent !== undefined && position.parent !== null) {
522
+ const parent = position.parent
523
+ if (parent.type === 'ParenthesizedExpression') {
524
+ position = parent
525
+ continue
526
+ }
527
+ if (
528
+ parent.type === 'Property' &&
529
+ parent.kind === 'init' &&
530
+ parent.method === false &&
531
+ parent.computed !== true &&
532
+ parent.value === position &&
533
+ parent.parent?.type === 'ObjectExpression'
534
+ ) {
535
+ position = parent.parent
536
+ continue
537
+ }
538
+ if (parent.type === 'ArrayExpression' && parent.elements?.includes(position) === true) {
539
+ position = parent
540
+ continue
541
+ }
542
+ break
527
543
  }
528
544
  return position
529
545
  }
530
546
 
531
- /** Reports whether a policy function is an anonymous callback passed directly as an argument. */
547
+ /** Reports whether a literal position is admitted as an argument, return, or arrow body. */
548
+ export function isPolicyPosition(position: PolicyExpression): boolean {
549
+ const parent = position.parent
550
+ return (
551
+ ((parent?.type === 'CallExpression' || parent?.type === 'NewExpression') &&
552
+ parent.arguments?.includes(position) === true) ||
553
+ (parent?.type === 'ReturnStatement' && parent.argument === position) ||
554
+ (parent?.type === 'ArrowFunctionExpression' && parent.body === position)
555
+ )
556
+ }
557
+
558
+ /** Reports whether a function expression is passed as an argument through its literal position. */
532
559
  export function isPolicyCallback(node: PolicyExpression): boolean {
533
- if (!isPolicyAnonymous(node)) return false
560
+ if (node.type !== 'ArrowFunctionExpression' && node.type !== 'FunctionExpression') return false
534
561
  const position = functionToPolicyPosition(node)
535
562
  const parent = position.parent
536
563
  return (
537
564
  (parent?.type === 'CallExpression' || parent?.type === 'NewExpression') &&
538
- parent.arguments?.includes(position) === true
565
+ isPolicyPosition(position)
539
566
  )
540
567
  }
541
568
 
542
- /** Reports whether a policy function is an anonymous function returned directly as a result. */
569
+ /** Reports whether a function expression is returned as a result through its literal position. */
543
570
  export function isPolicyResult(node: PolicyExpression): boolean {
544
- if (!isPolicyAnonymous(node)) return false
571
+ if (node.type !== 'ArrowFunctionExpression' && node.type !== 'FunctionExpression') return false
545
572
  const position = functionToPolicyPosition(node)
546
573
  const parent = position.parent
547
574
  return (
548
- (parent?.type === 'ReturnStatement' && parent.argument === position) ||
549
- (parent?.type === 'ArrowFunctionExpression' && parent.body === position)
575
+ (parent?.type === 'ReturnStatement' || parent?.type === 'ArrowFunctionExpression') &&
576
+ isPolicyPosition(position)
550
577
  )
551
578
  }
552
579
 
@@ -581,42 +608,6 @@ export function hasPolicyFunctionAncestor(node: PolicyExpression): boolean {
581
608
  return method
582
609
  }
583
610
 
584
- /** Reports whether an arrow is the policy plugin's sanctioned visitor-table delegation. */
585
- export function isPolicyVisitor(node: PolicyExpression): boolean {
586
- const body = expressionToPolicyBody(node)
587
- if (
588
- node.type !== 'ArrowFunctionExpression' ||
589
- node.expression !== true ||
590
- body?.type !== 'CallExpression' ||
591
- body.callee?.type !== 'Identifier' ||
592
- typeof body.callee.name !== 'string' ||
593
- !body.callee.name.startsWith('report')
594
- ) {
595
- return false
596
- }
597
- const property = node.parent
598
- const object = property?.parent
599
- const returned = object?.parent
600
- const block = returned?.parent
601
- const create = block?.parent
602
- const definition = create?.parent
603
- return (
604
- property?.type === 'Property' &&
605
- property.method === false &&
606
- property.value === node &&
607
- object?.type === 'ObjectExpression' &&
608
- returned?.type === 'ReturnStatement' &&
609
- returned.argument === object &&
610
- block?.type === 'BlockStatement' &&
611
- create?.type === 'FunctionExpression' &&
612
- definition?.type === 'Property' &&
613
- definition.method === true &&
614
- definition.value === create &&
615
- definition.key?.type === 'Identifier' &&
616
- definition.key.name === 'create'
617
- )
618
- }
619
-
620
611
  /**
621
612
  * Returns the module-scope statement that owns a policy function, or `undefined` when none does.
622
613
  *
@@ -849,15 +840,17 @@ export function reportComments(context: PolicyContext): void {
849
840
 
850
841
  /** Reports function syntax nested inside another function body. */
851
842
  export function reportNested(context: PolicyContext, node: PolicyExpression): void {
852
- if (
853
- !hasPolicyFunctionAncestor(node) ||
854
- isPolicyMethod(node) ||
855
- isPolicyCallback(node) ||
856
- isPolicyResult(node) ||
857
- isPolicyVisitor(node)
858
- ) {
843
+ if (!hasPolicyFunctionAncestor(node) || isPolicyCallback(node) || isPolicyResult(node)) {
859
844
  return
860
845
  }
846
+ if (isPolicyMethod(node)) {
847
+ if (node.parent?.type === 'MethodDefinition') return
848
+ const object = node.parent?.parent
849
+ if (object?.type === 'ObjectExpression') {
850
+ const position = functionToPolicyPosition(object)
851
+ if (isPolicyPosition(position)) return
852
+ }
853
+ }
861
854
  context.report({ node, messageId: 'nested' })
862
855
  }
863
856
 
@@ -969,7 +962,16 @@ export function reportFunction(context: PolicyContext, node: PolicyExpression):
969
962
  return
970
963
  }
971
964
  if (region.type !== 'VariableDeclaration') return
972
- if (isPolicyCallback(node) || isPolicyResult(node)) return
965
+ let position = node
966
+ while (position.parent?.type === 'ParenthesizedExpression') position = position.parent
967
+ // Module placement admits direct callbacks; a literal member still belongs in a function file.
968
+ if (
969
+ (node.type === 'ArrowFunctionExpression' || node.id === null) &&
970
+ functionToPolicyPosition(node) === position &&
971
+ (isPolicyCallback(node) || isPolicyResult(node))
972
+ ) {
973
+ return
974
+ }
973
975
  context.report({ node, messageId: 'function' })
974
976
  }
975
977
 
@@ -1059,11 +1061,12 @@ export const NESTED_RULE: PolicyRuleInterface = {
1059
1061
  meta: {
1060
1062
  type: 'problem',
1061
1063
  docs: {
1062
- description: 'Disallow function declarations and assignments inside another function body.',
1064
+ description:
1065
+ 'Disallow nested functions except callbacks passed as arguments or returned as results, directly or as members of object or array literals in those positions.',
1063
1066
  },
1064
1067
  messages: {
1065
1068
  nested:
1066
- 'Extract the function to module scope or make instance-bound work a method; only direct anonymous callbacks and returned anonymous functions may stay in a function body.',
1069
+ 'Extract the function to module scope or make instance-bound work a method; only a callback passed as an argument or returned as the result, directly or as a member of an object or array literal in that position, may stay in a function body.',
1067
1070
  },
1068
1071
  },
1069
1072
  create(context) {
@@ -132,16 +132,20 @@
132
132
  "error",
133
133
  {
134
134
  "patterns": [
135
+ {
136
+ "regex": "^(?:@(?:src|app)/vue(?:[/?#]|$)|@orkestrel/[^/]+/vue(?:[/?#]|$)|(?:\\.\\./)+(?:vue|src/vue|app/vue)(?:[/?#]|$))",
137
+ "message": "src/core must not depend on Vue extension modules"
138
+ },
135
139
  {
136
140
  "regex": "^(?:/|[A-Za-z]:[\\\\/])",
137
141
  "message": "imports must not use machine-specific absolute paths"
138
142
  },
139
143
  {
140
- "regex": "^(?!node:)[A-Za-z][A-Za-z0-9+.-]*:",
144
+ "regex": "^(?:[A-Za-mo-z][A-Za-z0-9+.-]*|n(?:[A-Za-np-z0-9+.-][A-Za-z0-9+.-]*)?|no(?:[A-Za-ce-z0-9+.-][A-Za-z0-9+.-]*)?|nod(?:[A-Za-df-z0-9+.-][A-Za-z0-9+.-]*)?|node[A-Za-z0-9+.-]+):",
141
145
  "message": "imports must not use non-Node URL schemes"
142
146
  },
143
147
  {
144
- "regex": "(?:^|/)(?!\\.{1,2}(?:/|$))[^/]+/\\.{1,2}(?:/|$)",
148
+ "regex": "(?:^|/)(?:[^/.][^/]*|\\.[^/.][^/]*|\\.\\.[^/]+)/\\.{1,2}(?:/|$)",
145
149
  "message": "internal import paths must not contain noncanonical dot segments"
146
150
  },
147
151
  {
@@ -180,16 +184,20 @@
180
184
  "error",
181
185
  {
182
186
  "patterns": [
187
+ {
188
+ "regex": "^(?:(?:vue|vite)(?:[/?#]|$)|@(?:vue|vitejs)/|@(?:src|app)/vue(?:[/?#]|$)|@orkestrel/[^/]+/vue(?:[/?#]|$)|(?:\\.\\./)+(?:vue|src/vue|app/vue)(?:[/?#]|$))",
189
+ "message": "src/browser must not depend on Vue extension modules"
190
+ },
183
191
  {
184
192
  "regex": "^(?:/|[A-Za-z]:[\\\\/])",
185
193
  "message": "imports must not use machine-specific absolute paths"
186
194
  },
187
195
  {
188
- "regex": "^(?!node:)[A-Za-z][A-Za-z0-9+.-]*:",
196
+ "regex": "^(?:[A-Za-mo-z][A-Za-z0-9+.-]*|n(?:[A-Za-np-z0-9+.-][A-Za-z0-9+.-]*)?|no(?:[A-Za-ce-z0-9+.-][A-Za-z0-9+.-]*)?|nod(?:[A-Za-df-z0-9+.-][A-Za-z0-9+.-]*)?|node[A-Za-z0-9+.-]+):",
189
197
  "message": "imports must not use non-Node URL schemes"
190
198
  },
191
199
  {
192
- "regex": "(?:^|/)(?!\\.{1,2}(?:/|$))[^/]+/\\.{1,2}(?:/|$)",
200
+ "regex": "(?:^|/)(?:[^/.][^/]*|\\.[^/.][^/]*|\\.\\.[^/]+)/\\.{1,2}(?:/|$)",
193
201
  "message": "internal import paths must not contain noncanonical dot segments"
194
202
  },
195
203
  {
@@ -217,6 +225,54 @@
217
225
  ]
218
226
  }
219
227
  },
228
+ {
229
+ "files": ["src/vue/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}"],
230
+ "rules": {
231
+ "no-restricted-imports": [
232
+ "error",
233
+ {
234
+ "patterns": [
235
+ {
236
+ "regex": "^(?:@src/(?:|[^/?#cbv][^/?#]*|c|c[^/?#o][^/?#]*|co|co[^/?#r][^/?#]*|cor|cor[^/?#e][^/?#]*|core[^/?#][^/?#]*|b|b[^/?#r][^/?#]*|br|br[^/?#o][^/?#]*|bro|bro[^/?#w][^/?#]*|brow|brow[^/?#s][^/?#]*|brows|brows[^/?#e][^/?#]*|browse|browse[^/?#r][^/?#]*|browser[^/?#][^/?#]*|v|v[^/?#u][^/?#]*|vu|vu[^/?#e][^/?#]*|vue[^/?#][^/?#]*)(?:[/?#]|$)|(?:\\.\\./)+(?:src/)?(?:[^/.scbv][^/.]*|s|s[^/.r][^/.]*|sr|sr[^/.c][^/.]*|src[^/.][^/.]*|c|c[^/.o][^/.]*|co|co[^/.r][^/.]*|cor|cor[^/.e][^/.]*|core[^/.][^/.]*|b|b[^/.r][^/.]*|br|br[^/.o][^/.]*|bro|bro[^/.w][^/.]*|brow|brow[^/.s][^/.]*|brows|brows[^/.e][^/.]*|browse|browse[^/.r][^/.]*|browser[^/.][^/.]*|v|v[^/.u][^/.]*|vu|vu[^/.e][^/.]*|vue[^/.][^/.]*)(?:/|$))",
237
+ "message": "Vue faces must not depend on sibling sheet faces"
238
+ },
239
+ {
240
+ "regex": "^(?:/|[A-Za-z]:[\\\\/])",
241
+ "message": "imports must not use machine-specific absolute paths"
242
+ },
243
+ {
244
+ "regex": "^(?:[A-Za-mo-z][A-Za-z0-9+.-]*|n(?:[A-Za-np-z0-9+.-][A-Za-z0-9+.-]*)?|no(?:[A-Za-ce-z0-9+.-][A-Za-z0-9+.-]*)?|nod(?:[A-Za-df-z0-9+.-][A-Za-z0-9+.-]*)?|node[A-Za-z0-9+.-]+):",
245
+ "message": "imports must not use non-Node URL schemes"
246
+ },
247
+ {
248
+ "regex": "(?:^|/)(?:[^/.][^/]*|\\.[^/.][^/]*|\\.\\.[^/]+)/\\.{1,2}(?:/|$)",
249
+ "message": "internal import paths must not contain noncanonical dot segments"
250
+ },
251
+ {
252
+ "regex": "^(?:(?:\\./)+\\.\\.(?:/|$)|(?:\\.\\./)+(?:\\./)+)|//",
253
+ "message": "relative import paths must not contain normalized traversal segments"
254
+ },
255
+ {
256
+ "regex": "\\\\",
257
+ "message": "import paths must use portable forward slashes"
258
+ },
259
+ {
260
+ "regex": "^typescript(?:[/?#]|$)",
261
+ "message": "the in-process compiler API is not a surface the fleet uses"
262
+ },
263
+ {
264
+ "regex": "^(?:@app(?:[/?#]|$)|(?:\\.\\./)+app(?:[/?#]|$))",
265
+ "message": "src environments must not depend on private app modules"
266
+ },
267
+ {
268
+ "regex": "^(?:node:|(?:_(?:http_(?:agent|client|common|incoming|outgoing|server)|stream_(?:duplex|passthrough|readable|transform|wrap|writable)|tls_(?:common|wrap))|assert(?:/strict)?|async_hooks|buffer|child_process|cluster|console|constants|crypto|dgram|diagnostics_channel|dns(?:/promises)?|domain|events|fs(?:/promises)?|http|http2|https|inspector(?:/promises)?|module|net|os|path(?:/(?:posix|win32))?|perf_hooks|process|punycode|querystring|readline(?:/promises)?|repl|sqlite|stream(?:/(?:consumers|promises|web))?|string_decoder|sys|timers(?:/promises)?|tls|trace_events|tty|url|util(?:/types)?|v8|vm|wasi|worker_threads|zlib)(?:[/?#]|$)|@src/server(?:[/?#]|$)|@orkestrel/[^/]+/server(?:[/?#]|$)|(?:\\.\\./)+(?:server|src/server|app/server)(?:[/?#]|$))",
269
+ "message": "src/vue must not depend on Node or server-only modules"
270
+ }
271
+ ]
272
+ }
273
+ ]
274
+ }
275
+ },
220
276
  {
221
277
  "files": ["src/server/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}"],
222
278
  "rules": {
@@ -224,16 +280,20 @@
224
280
  "error",
225
281
  {
226
282
  "patterns": [
283
+ {
284
+ "regex": "^(?:@(?:src|app)/vue(?:[/?#]|$)|@orkestrel/[^/]+/vue(?:[/?#]|$)|(?:\\.\\./)+(?:vue|src/vue|app/vue)(?:[/?#]|$))",
285
+ "message": "src/server must not depend on Vue extension modules"
286
+ },
227
287
  {
228
288
  "regex": "^(?:/|[A-Za-z]:[\\\\/])",
229
289
  "message": "imports must not use machine-specific absolute paths"
230
290
  },
231
291
  {
232
- "regex": "^(?!node:)[A-Za-z][A-Za-z0-9+.-]*:",
292
+ "regex": "^(?:[A-Za-mo-z][A-Za-z0-9+.-]*|n(?:[A-Za-np-z0-9+.-][A-Za-z0-9+.-]*)?|no(?:[A-Za-ce-z0-9+.-][A-Za-z0-9+.-]*)?|nod(?:[A-Za-df-z0-9+.-][A-Za-z0-9+.-]*)?|node[A-Za-z0-9+.-]+):",
233
293
  "message": "imports must not use non-Node URL schemes"
234
294
  },
235
295
  {
236
- "regex": "(?:^|/)(?!\\.{1,2}(?:/|$))[^/]+/\\.{1,2}(?:/|$)",
296
+ "regex": "(?:^|/)(?:[^/.][^/]*|\\.[^/.][^/]*|\\.\\.[^/]+)/\\.{1,2}(?:/|$)",
237
297
  "message": "internal import paths must not contain noncanonical dot segments"
238
298
  },
239
299
  {
@@ -272,16 +332,20 @@
272
332
  "error",
273
333
  {
274
334
  "patterns": [
335
+ {
336
+ "regex": "^(?:@(?:src|app)/vue(?:[/?#]|$)|@orkestrel/[^/]+/vue(?:[/?#]|$)|(?:\\.\\./)+(?:vue|src/vue|app/vue)(?:[/?#]|$))",
337
+ "message": "app/core must not depend on Vue extension modules"
338
+ },
275
339
  {
276
340
  "regex": "^(?:/|[A-Za-z]:[\\\\/])",
277
341
  "message": "imports must not use machine-specific absolute paths"
278
342
  },
279
343
  {
280
- "regex": "^(?!node:)[A-Za-z][A-Za-z0-9+.-]*:",
344
+ "regex": "^(?:[A-Za-mo-z][A-Za-z0-9+.-]*|n(?:[A-Za-np-z0-9+.-][A-Za-z0-9+.-]*)?|no(?:[A-Za-ce-z0-9+.-][A-Za-z0-9+.-]*)?|nod(?:[A-Za-df-z0-9+.-][A-Za-z0-9+.-]*)?|node[A-Za-z0-9+.-]+):",
281
345
  "message": "imports must not use non-Node URL schemes"
282
346
  },
283
347
  {
284
- "regex": "(?:^|/)(?!\\.{1,2}(?:/|$))[^/]+/\\.{1,2}(?:/|$)",
348
+ "regex": "(?:^|/)(?:[^/.][^/]*|\\.[^/.][^/]*|\\.\\.[^/]+)/\\.{1,2}(?:/|$)",
285
349
  "message": "internal import paths must not contain noncanonical dot segments"
286
350
  },
287
351
  {
@@ -316,16 +380,20 @@
316
380
  "error",
317
381
  {
318
382
  "patterns": [
383
+ {
384
+ "regex": "^(?:(?:vue|vite)(?:[/?#]|$)|@(?:vue|vitejs)/|@(?:src|app)/vue(?:[/?#]|$)|@orkestrel/[^/]+/vue(?:[/?#]|$)|(?:\\.\\./)+(?:vue|src/vue|app/vue)(?:[/?#]|$))",
385
+ "message": "app/browser must not depend on Vue extension modules"
386
+ },
319
387
  {
320
388
  "regex": "^(?:/|[A-Za-z]:[\\\\/])",
321
389
  "message": "imports must not use machine-specific absolute paths"
322
390
  },
323
391
  {
324
- "regex": "^(?!node:)[A-Za-z][A-Za-z0-9+.-]*:",
392
+ "regex": "^(?:[A-Za-mo-z][A-Za-z0-9+.-]*|n(?:[A-Za-np-z0-9+.-][A-Za-z0-9+.-]*)?|no(?:[A-Za-ce-z0-9+.-][A-Za-z0-9+.-]*)?|nod(?:[A-Za-df-z0-9+.-][A-Za-z0-9+.-]*)?|node[A-Za-z0-9+.-]+):",
325
393
  "message": "imports must not use non-Node URL schemes"
326
394
  },
327
395
  {
328
- "regex": "(?:^|/)(?!\\.{1,2}(?:/|$))[^/]+/\\.{1,2}(?:/|$)",
396
+ "regex": "(?:^|/)(?:[^/.][^/]*|\\.[^/.][^/]*|\\.\\.[^/]+)/\\.{1,2}(?:/|$)",
329
397
  "message": "internal import paths must not contain noncanonical dot segments"
330
398
  },
331
399
  {
@@ -349,6 +417,50 @@
349
417
  ]
350
418
  }
351
419
  },
420
+ {
421
+ "files": ["app/vue/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}"],
422
+ "rules": {
423
+ "no-restricted-imports": [
424
+ "error",
425
+ {
426
+ "patterns": [
427
+ {
428
+ "regex": "^(?:@(?:src|app)/(?:|[^/?#cbv][^/?#]*|c|c[^/?#o][^/?#]*|co|co[^/?#r][^/?#]*|cor|cor[^/?#e][^/?#]*|core[^/?#][^/?#]*|b|b[^/?#r][^/?#]*|br|br[^/?#o][^/?#]*|bro|bro[^/?#w][^/?#]*|brow|brow[^/?#s][^/?#]*|brows|brows[^/?#e][^/?#]*|browse|browse[^/?#r][^/?#]*|browser[^/?#][^/?#]*|v|v[^/?#u][^/?#]*|vu|vu[^/?#e][^/?#]*|vue[^/?#][^/?#]*)(?:[/?#]|$)|(?:\\.\\./)+(?:src/|app/)?(?:[^/.sacbv][^/.]*|s|s[^/.r][^/.]*|sr|sr[^/.c][^/.]*|src[^/.][^/.]*|a|a[^/.p][^/.]*|ap|ap[^/.p][^/.]*|app[^/.][^/.]*|c|c[^/.o][^/.]*|co|co[^/.r][^/.]*|cor|cor[^/.e][^/.]*|core[^/.][^/.]*|b|b[^/.r][^/.]*|br|br[^/.o][^/.]*|bro|bro[^/.w][^/.]*|brow|brow[^/.s][^/.]*|brows|brows[^/.e][^/.]*|browse|browse[^/.r][^/.]*|browser[^/.][^/.]*|v|v[^/.u][^/.]*|vu|vu[^/.e][^/.]*|vue[^/.][^/.]*)(?:/|$))",
429
+ "message": "Vue faces must not depend on sibling sheet faces"
430
+ },
431
+ {
432
+ "regex": "^(?:/|[A-Za-z]:[\\\\/])",
433
+ "message": "imports must not use machine-specific absolute paths"
434
+ },
435
+ {
436
+ "regex": "^(?:[A-Za-mo-z][A-Za-z0-9+.-]*|n(?:[A-Za-np-z0-9+.-][A-Za-z0-9+.-]*)?|no(?:[A-Za-ce-z0-9+.-][A-Za-z0-9+.-]*)?|nod(?:[A-Za-df-z0-9+.-][A-Za-z0-9+.-]*)?|node[A-Za-z0-9+.-]+):",
437
+ "message": "imports must not use non-Node URL schemes"
438
+ },
439
+ {
440
+ "regex": "(?:^|/)(?:[^/.][^/]*|\\.[^/.][^/]*|\\.\\.[^/]+)/\\.{1,2}(?:/|$)",
441
+ "message": "internal import paths must not contain noncanonical dot segments"
442
+ },
443
+ {
444
+ "regex": "^(?:(?:\\./)+\\.\\.(?:/|$)|(?:\\.\\./)+(?:\\./)+)|//",
445
+ "message": "relative import paths must not contain normalized traversal segments"
446
+ },
447
+ {
448
+ "regex": "\\\\",
449
+ "message": "import paths must use portable forward slashes"
450
+ },
451
+ {
452
+ "regex": "^typescript(?:[/?#]|$)",
453
+ "message": "the in-process compiler API is not a surface the fleet uses"
454
+ },
455
+ {
456
+ "regex": "^(?:node:|(?:_(?:http_(?:agent|client|common|incoming|outgoing|server)|stream_(?:duplex|passthrough|readable|transform|wrap|writable)|tls_(?:common|wrap))|assert(?:/strict)?|async_hooks|buffer|child_process|cluster|console|constants|crypto|dgram|diagnostics_channel|dns(?:/promises)?|domain|events|fs(?:/promises)?|http|http2|https|inspector(?:/promises)?|module|net|os|path(?:/(?:posix|win32))?|perf_hooks|process|punycode|querystring|readline(?:/promises)?|repl|sqlite|stream(?:/(?:consumers|promises|web))?|string_decoder|sys|timers(?:/promises)?|tls|trace_events|tty|url|util(?:/types)?|v8|vm|wasi|worker_threads|zlib)(?:[/?#]|$)|@app/server(?:[/?#]|$)|@src/server(?:[/?#]|$)|@orkestrel/[^/]+/server(?:[/?#]|$)|(?:\\.\\./)+(?:server|src/server|app/server)(?:[/?#]|$))",
457
+ "message": "app/vue must not depend on Node or server-only modules"
458
+ }
459
+ ]
460
+ }
461
+ ]
462
+ }
463
+ },
352
464
  {
353
465
  "files": ["app/server/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}"],
354
466
  "rules": {
@@ -356,16 +468,20 @@
356
468
  "error",
357
469
  {
358
470
  "patterns": [
471
+ {
472
+ "regex": "^(?:@(?:src|app)/vue(?:[/?#]|$)|@orkestrel/[^/]+/vue(?:[/?#]|$)|(?:\\.\\./)+(?:vue|src/vue|app/vue)(?:[/?#]|$))",
473
+ "message": "app/server must not depend on Vue extension modules"
474
+ },
359
475
  {
360
476
  "regex": "^(?:/|[A-Za-z]:[\\\\/])",
361
477
  "message": "imports must not use machine-specific absolute paths"
362
478
  },
363
479
  {
364
- "regex": "^(?!node:)[A-Za-z][A-Za-z0-9+.-]*:",
480
+ "regex": "^(?:[A-Za-mo-z][A-Za-z0-9+.-]*|n(?:[A-Za-np-z0-9+.-][A-Za-z0-9+.-]*)?|no(?:[A-Za-ce-z0-9+.-][A-Za-z0-9+.-]*)?|nod(?:[A-Za-df-z0-9+.-][A-Za-z0-9+.-]*)?|node[A-Za-z0-9+.-]+):",
365
481
  "message": "imports must not use non-Node URL schemes"
366
482
  },
367
483
  {
368
- "regex": "(?:^|/)(?!\\.{1,2}(?:/|$))[^/]+/\\.{1,2}(?:/|$)",
484
+ "regex": "(?:^|/)(?:[^/.][^/]*|\\.[^/.][^/]*|\\.\\.[^/]+)/\\.{1,2}(?:/|$)",
369
485
  "message": "internal import paths must not contain noncanonical dot segments"
370
486
  },
371
487
  {
@@ -405,11 +521,11 @@
405
521
  "message": "imports must not use machine-specific absolute paths"
406
522
  },
407
523
  {
408
- "regex": "^(?!node:)[A-Za-z][A-Za-z0-9+.-]*:",
524
+ "regex": "^(?:[A-Za-mo-z][A-Za-z0-9+.-]*|n(?:[A-Za-np-z0-9+.-][A-Za-z0-9+.-]*)?|no(?:[A-Za-ce-z0-9+.-][A-Za-z0-9+.-]*)?|nod(?:[A-Za-df-z0-9+.-][A-Za-z0-9+.-]*)?|node[A-Za-z0-9+.-]+):",
409
525
  "message": "imports must not use non-Node URL schemes"
410
526
  },
411
527
  {
412
- "regex": "(?:^|/)(?!\\.{1,2}(?:/|$))[^/]+/\\.{1,2}(?:/|$)",
528
+ "regex": "(?:^|/)(?:[^/.][^/]*|\\.[^/.][^/]*|\\.\\.[^/]+)/\\.{1,2}(?:/|$)",
413
529
  "message": "internal import paths must not contain noncanonical dot segments"
414
530
  },
415
531
  {
@@ -433,7 +549,7 @@
433
549
  "message": "src/bin must not depend on private app modules"
434
550
  },
435
551
  {
436
- "regex": "^(?:(?:vue|vite)(?:[/?#]|$)|@(?:vue|vitejs)/|@src/browser(?:[/?#]|$)|@orkestrel/[^/]+/browser(?:[/?#]|$)|(?:\\.\\./)+(?:browser|src/browser|app/browser)(?:[/?#]|$))",
552
+ "regex": "^(?:(?:vue|vite)(?:[/?#]|$)|@(?:vue|vitejs)/|@src/(?:browser|vue)(?:[/?#]|$)|@orkestrel/[^/]+/(?:browser|vue)(?:[/?#]|$)|(?:\\.\\./)+(?:browser|vue|src/(?:browser|vue)|app/browser)(?:[/?#]|$))",
437
553
  "message": "src/bin must not depend on browser-only modules"
438
554
  }
439
555
  ]
@@ -446,7 +562,7 @@
446
562
  "tests/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}",
447
563
  "configs/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}",
448
564
  "scripts/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}",
449
- "*.{cjs,cts,js,mjs,mts,ts}"
565
+ "./*.{cjs,cts,js,mjs,mts,ts}"
450
566
  ],
451
567
  "rules": {
452
568
  "no-restricted-imports": [
@@ -5,7 +5,7 @@
5
5
  dist/
6
6
 
7
7
  # Generated single-file application showcase.
8
- demo/showcase.html
8
+ showcase/
9
9
 
10
10
  # Campaign records under the orchestration folder are verbatim evidence, not formatted source.
11
11
  .orkestrel/
@@ -16,15 +16,19 @@ guide mirrors, and the `WriteTransaction` those writes stage through. The `scaff
16
16
  ([`src/bin`](../src/bin)) publishes no barrel, so it is documented in prose and sits outside the
17
17
  surface bijection.
18
18
 
19
- The [Blueprint reference](scaffold.md#blueprint) describes the journey axis and its adopter-edited
20
- `configs/app/vite.journey.config.ts` wrapper, the `SetupRuntime` list, and the `setup:browser`
21
- project. The generated workspace runs journey variants and browser setup proofs through its
22
- `test` chain.
23
-
24
19
  That bijection is the row's contract, and [`tests/guides.test.ts`](../tests/guides.test.ts)
25
20
  enforces it: every symbol the guide documents exists in the core barrel or the server barrel, and
26
21
  every symbol either barrel exports is documented.
27
22
 
23
+ The [Blueprint reference](scaffold.md#blueprint) describes the structural facts, the
24
+ `SetupRuntime` list, and the `setup:browser` project. The
25
+ [surfaces and extensions reference](scaffold.md#surfaces-and-extensions) describes the browser
26
+ surface with its journey and showcase modes, the styles surface with its themes target, the `vue`
27
+ faces and named-sheet extensions, the creation options, their refusals, and the advisories on a
28
+ declared selection, the marker each fact is read from, the ownership of every path a sheet face
29
+ emits, the page stamp, the external resolution of each published face, the vendored proofs, the
30
+ root setup mirror, and the migration guard. Both sections belong to the `scaffold.md` row.
31
+
28
32
  ## By directory
29
33
 
30
34
  | Directory | Guide |