@caddis/cli 0.4.45 → 0.4.47

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 (193) hide show
  1. package/bundles/antigravity-plugin/plugin.json +1 -1
  2. package/bundles/antigravity-plugin/skills/flow-studio/README.md +46 -7
  3. package/bundles/antigravity-plugin/skills/flow-studio/SKILL.md +81 -81
  4. package/bundles/antigravity-plugin/skills/flow-studio/examples/README.md +8 -0
  5. package/bundles/antigravity-plugin/skills/flow-studio/examples/agent-journey.json +103 -0
  6. package/bundles/antigravity-plugin/skills/flow-studio/examples/approval-flow.json +134 -0
  7. package/bundles/antigravity-plugin/skills/flow-studio/examples/ci-cd-pipeline.json +132 -0
  8. package/bundles/antigravity-plugin/skills/flow-studio/examples/incident-timeline.json +109 -0
  9. package/bundles/antigravity-plugin/skills/flow-studio/examples/order-desk.json +99 -0
  10. package/bundles/antigravity-plugin/skills/flow-studio/examples/service-map.json +133 -0
  11. package/bundles/antigravity-plugin/skills/flow-studio/examples/state-machine.json +106 -0
  12. package/bundles/antigravity-plugin/skills/flow-studio/examples/ticket-lifecycle.json +107 -0
  13. package/bundles/antigravity-plugin/skills/flow-studio/model.schema.json +27 -8
  14. package/bundles/antigravity-plugin/skills/flow-studio/react/FlowStudioView.test.tsx +82 -0
  15. package/bundles/antigravity-plugin/skills/flow-studio/react/FlowStudioView.tsx +67 -0
  16. package/bundles/antigravity-plugin/skills/flow-studio/react/README.md +37 -0
  17. package/bundles/antigravity-plugin/skills/flow-studio/react/index.ts +3 -0
  18. package/bundles/antigravity-plugin/skills/flow-studio/react/native/FlowCanvas.tsx +58 -0
  19. package/bundles/antigravity-plugin/skills/flow-studio/react/native/Inspector.tsx +27 -0
  20. package/bundles/antigravity-plugin/skills/flow-studio/react/native/index.ts +6 -0
  21. package/bundles/antigravity-plugin/skills/flow-studio/react/native/native.css +18 -0
  22. package/bundles/antigravity-plugin/skills/flow-studio/react/native/native.test.tsx +161 -0
  23. package/bundles/antigravity-plugin/skills/flow-studio/react/native/useFlowGeometry.ts +22 -0
  24. package/bundles/antigravity-plugin/skills/flow-studio/react/native/useTrace.ts +29 -0
  25. package/bundles/antigravity-plugin/skills/flow-studio/react/native/useViewport.ts +75 -0
  26. package/bundles/antigravity-plugin/skills/flow-studio/react/states.tsx +13 -0
  27. package/bundles/antigravity-plugin/skills/flow-studio/references/design-language.md +177 -0
  28. package/bundles/antigravity-plugin/skills/flow-studio/references/implement-in-app.md +213 -0
  29. package/bundles/antigravity-plugin/skills/flow-studio/references/model-schema.md +254 -0
  30. package/bundles/antigravity-plugin/skills/flow-studio/references/notes-and-spec.md +74 -0
  31. package/bundles/antigravity-plugin/skills/flow-studio/references/patterns.md +385 -49
  32. package/bundles/antigravity-plugin/skills/flow-studio/references/ux-rules.md +91 -0
  33. package/bundles/antigravity-plugin/skills/flow-studio/references/verify.md +124 -0
  34. package/bundles/antigravity-plugin/skills/flow-studio/scripts/build-engine.mjs +54 -14
  35. package/bundles/antigravity-plugin/skills/flow-studio/scripts/build.mjs +21 -4
  36. package/bundles/antigravity-plugin/skills/flow-studio/scripts/check.mjs +320 -28
  37. package/bundles/antigravity-plugin/skills/flow-studio/scripts/default-notes.json +67 -0
  38. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/auto-entry-guard.mjs +5 -0
  39. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/auto-entry-guard.test.mjs +14 -0
  40. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/auto-entry.test.mjs +31 -0
  41. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/build-engine.test.mjs +30 -2
  42. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/check-args.mjs +18 -0
  43. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/check-args.test.mjs +15 -0
  44. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/check-selftest.test.mjs +64 -4
  45. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/cli.test.mjs +115 -0
  46. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/demo-pack.mjs +23 -0
  47. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/demo-pack.test.mjs +34 -0
  48. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/docs.test.mjs +128 -0
  49. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/library.test.mjs +26 -0
  50. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/react-proof-cleanup.mjs +12 -0
  51. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/react-proof-cleanup.test.mjs +45 -0
  52. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/react-proof-page.mjs +3 -0
  53. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/react-proof-page.test.mjs +9 -0
  54. package/bundles/antigravity-plugin/skills/flow-studio/scripts/lib/styles.test.mjs +37 -0
  55. package/bundles/antigravity-plugin/skills/flow-studio/scripts/react-proof.mjs +92 -0
  56. package/bundles/antigravity-plugin/skills/flow-studio/scripts/validate.mjs +5 -1
  57. package/bundles/antigravity-plugin/skills/flow-studio/src/autolayout.test.ts +78 -0
  58. package/bundles/antigravity-plugin/skills/flow-studio/src/autolayout.ts +132 -0
  59. package/bundles/antigravity-plugin/skills/flow-studio/src/chrome.ts +11 -7
  60. package/bundles/antigravity-plugin/skills/flow-studio/src/controls.ts +8 -0
  61. package/bundles/antigravity-plugin/skills/flow-studio/src/ctx.ts +16 -0
  62. package/bundles/antigravity-plugin/skills/flow-studio/src/diff.test.ts +31 -0
  63. package/bundles/antigravity-plugin/skills/flow-studio/src/diff.ts +34 -0
  64. package/bundles/antigravity-plugin/skills/flow-studio/src/engine.ts +83 -32
  65. package/bundles/antigravity-plugin/skills/flow-studio/src/inspector.ts +23 -5
  66. package/bundles/antigravity-plugin/skills/flow-studio/src/interact.ts +21 -8
  67. package/bundles/antigravity-plugin/skills/flow-studio/src/layout.test.ts +75 -0
  68. package/bundles/antigravity-plugin/skills/flow-studio/src/layout.ts +49 -11
  69. package/bundles/antigravity-plugin/skills/flow-studio/src/lib.ts +20 -0
  70. package/bundles/antigravity-plugin/skills/flow-studio/src/metrics.test.ts +57 -0
  71. package/bundles/antigravity-plugin/skills/flow-studio/src/metrics.ts +29 -0
  72. package/bundles/antigravity-plugin/skills/flow-studio/src/model.test.ts +62 -2
  73. package/bundles/antigravity-plugin/skills/flow-studio/src/model.ts +101 -16
  74. package/bundles/antigravity-plugin/skills/flow-studio/src/notes.ts +8 -0
  75. package/bundles/antigravity-plugin/skills/flow-studio/src/page.ts +21 -0
  76. package/bundles/antigravity-plugin/skills/flow-studio/src/playback.test.ts +104 -0
  77. package/bundles/antigravity-plugin/skills/flow-studio/src/playback.ts +106 -0
  78. package/bundles/antigravity-plugin/skills/flow-studio/src/playbackui.ts +193 -0
  79. package/bundles/antigravity-plugin/skills/flow-studio/src/render.ts +66 -18
  80. package/bundles/antigravity-plugin/skills/flow-studio/src/routing.test.ts +21 -1
  81. package/bundles/antigravity-plugin/skills/flow-studio/src/routing.ts +32 -0
  82. package/bundles/antigravity-plugin/skills/flow-studio/src/rules.test.ts +48 -1
  83. package/bundles/antigravity-plugin/skills/flow-studio/src/rules.ts +52 -1
  84. package/bundles/antigravity-plugin/skills/flow-studio/src/selection.test.ts +12 -0
  85. package/bundles/antigravity-plugin/skills/flow-studio/src/selection.ts +4 -0
  86. package/bundles/antigravity-plugin/skills/flow-studio/src/stateCss.test.ts +7 -0
  87. package/bundles/antigravity-plugin/skills/flow-studio/src/stateCss.ts +1 -0
  88. package/bundles/antigravity-plugin/skills/flow-studio/src/styles.css +44 -10
  89. package/bundles/antigravity-plugin/skills/flow-studio/src/trace.test.ts +21 -1
  90. package/bundles/antigravity-plugin/skills/flow-studio/src/trace.ts +18 -0
  91. package/bundles/antigravity-plugin/skills/flow-studio/templates/engine.html +995 -82
  92. package/bundles/antigravity-plugin/skills/flow-studio/templates/engine.shell.html +1 -39
  93. package/bundles/antigravity-plugin/skills/flow-studio/templates/flow-studio.css +191 -0
  94. package/bundles/antigravity-plugin/skills/flow-studio/templates/flow-studio.lib.d.mts +1 -0
  95. package/bundles/antigravity-plugin/skills/flow-studio/templates/flow-studio.lib.mjs +2492 -0
  96. package/bundles/antigravity-plugin/skills/flow-studio/templates/shell-body.html +50 -0
  97. package/bundles/codex/.codex/skills/frontend/flow-studio/README.md +46 -7
  98. package/bundles/codex/.codex/skills/frontend/flow-studio/SKILL.md +81 -81
  99. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/README.md +8 -0
  100. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/agent-journey.json +103 -0
  101. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/approval-flow.json +134 -0
  102. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/ci-cd-pipeline.json +132 -0
  103. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/incident-timeline.json +109 -0
  104. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/order-desk.json +99 -0
  105. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/service-map.json +133 -0
  106. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/state-machine.json +106 -0
  107. package/bundles/codex/.codex/skills/frontend/flow-studio/examples/ticket-lifecycle.json +107 -0
  108. package/bundles/codex/.codex/skills/frontend/flow-studio/model.schema.json +27 -8
  109. package/bundles/codex/.codex/skills/frontend/flow-studio/react/FlowStudioView.test.tsx +82 -0
  110. package/bundles/codex/.codex/skills/frontend/flow-studio/react/FlowStudioView.tsx +67 -0
  111. package/bundles/codex/.codex/skills/frontend/flow-studio/react/README.md +37 -0
  112. package/bundles/codex/.codex/skills/frontend/flow-studio/react/index.ts +3 -0
  113. package/bundles/codex/.codex/skills/frontend/flow-studio/react/native/FlowCanvas.tsx +58 -0
  114. package/bundles/codex/.codex/skills/frontend/flow-studio/react/native/Inspector.tsx +27 -0
  115. package/bundles/codex/.codex/skills/frontend/flow-studio/react/native/index.ts +6 -0
  116. package/bundles/codex/.codex/skills/frontend/flow-studio/react/native/native.css +18 -0
  117. package/bundles/codex/.codex/skills/frontend/flow-studio/react/native/native.test.tsx +161 -0
  118. package/bundles/codex/.codex/skills/frontend/flow-studio/react/native/useFlowGeometry.ts +22 -0
  119. package/bundles/codex/.codex/skills/frontend/flow-studio/react/native/useTrace.ts +29 -0
  120. package/bundles/codex/.codex/skills/frontend/flow-studio/react/native/useViewport.ts +75 -0
  121. package/bundles/codex/.codex/skills/frontend/flow-studio/react/states.tsx +13 -0
  122. package/bundles/codex/.codex/skills/frontend/flow-studio/references/design-language.md +177 -0
  123. package/bundles/codex/.codex/skills/frontend/flow-studio/references/implement-in-app.md +213 -0
  124. package/bundles/codex/.codex/skills/frontend/flow-studio/references/model-schema.md +254 -0
  125. package/bundles/codex/.codex/skills/frontend/flow-studio/references/notes-and-spec.md +74 -0
  126. package/bundles/codex/.codex/skills/frontend/flow-studio/references/patterns.md +385 -49
  127. package/bundles/codex/.codex/skills/frontend/flow-studio/references/ux-rules.md +91 -0
  128. package/bundles/codex/.codex/skills/frontend/flow-studio/references/verify.md +124 -0
  129. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/build-engine.mjs +54 -14
  130. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/build.mjs +21 -4
  131. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/check.mjs +320 -28
  132. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/default-notes.json +67 -0
  133. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/auto-entry-guard.mjs +5 -0
  134. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/auto-entry-guard.test.mjs +14 -0
  135. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/auto-entry.test.mjs +31 -0
  136. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/build-engine.test.mjs +30 -2
  137. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/check-args.mjs +18 -0
  138. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/check-args.test.mjs +15 -0
  139. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/check-selftest.test.mjs +64 -4
  140. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/cli.test.mjs +115 -0
  141. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/demo-pack.mjs +23 -0
  142. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/demo-pack.test.mjs +34 -0
  143. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/docs.test.mjs +128 -0
  144. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/library.test.mjs +26 -0
  145. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/react-proof-cleanup.mjs +12 -0
  146. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/react-proof-cleanup.test.mjs +45 -0
  147. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/react-proof-page.mjs +3 -0
  148. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/react-proof-page.test.mjs +9 -0
  149. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/lib/styles.test.mjs +37 -0
  150. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/react-proof.mjs +92 -0
  151. package/bundles/codex/.codex/skills/frontend/flow-studio/scripts/validate.mjs +5 -1
  152. package/bundles/codex/.codex/skills/frontend/flow-studio/src/autolayout.test.ts +78 -0
  153. package/bundles/codex/.codex/skills/frontend/flow-studio/src/autolayout.ts +132 -0
  154. package/bundles/codex/.codex/skills/frontend/flow-studio/src/chrome.ts +11 -7
  155. package/bundles/codex/.codex/skills/frontend/flow-studio/src/controls.ts +8 -0
  156. package/bundles/codex/.codex/skills/frontend/flow-studio/src/ctx.ts +16 -0
  157. package/bundles/codex/.codex/skills/frontend/flow-studio/src/diff.test.ts +31 -0
  158. package/bundles/codex/.codex/skills/frontend/flow-studio/src/diff.ts +34 -0
  159. package/bundles/codex/.codex/skills/frontend/flow-studio/src/engine.ts +83 -32
  160. package/bundles/codex/.codex/skills/frontend/flow-studio/src/inspector.ts +23 -5
  161. package/bundles/codex/.codex/skills/frontend/flow-studio/src/interact.ts +21 -8
  162. package/bundles/codex/.codex/skills/frontend/flow-studio/src/layout.test.ts +75 -0
  163. package/bundles/codex/.codex/skills/frontend/flow-studio/src/layout.ts +49 -11
  164. package/bundles/codex/.codex/skills/frontend/flow-studio/src/lib.ts +20 -0
  165. package/bundles/codex/.codex/skills/frontend/flow-studio/src/metrics.test.ts +57 -0
  166. package/bundles/codex/.codex/skills/frontend/flow-studio/src/metrics.ts +29 -0
  167. package/bundles/codex/.codex/skills/frontend/flow-studio/src/model.test.ts +62 -2
  168. package/bundles/codex/.codex/skills/frontend/flow-studio/src/model.ts +101 -16
  169. package/bundles/codex/.codex/skills/frontend/flow-studio/src/notes.ts +8 -0
  170. package/bundles/codex/.codex/skills/frontend/flow-studio/src/page.ts +21 -0
  171. package/bundles/codex/.codex/skills/frontend/flow-studio/src/playback.test.ts +104 -0
  172. package/bundles/codex/.codex/skills/frontend/flow-studio/src/playback.ts +106 -0
  173. package/bundles/codex/.codex/skills/frontend/flow-studio/src/playbackui.ts +193 -0
  174. package/bundles/codex/.codex/skills/frontend/flow-studio/src/render.ts +66 -18
  175. package/bundles/codex/.codex/skills/frontend/flow-studio/src/routing.test.ts +21 -1
  176. package/bundles/codex/.codex/skills/frontend/flow-studio/src/routing.ts +32 -0
  177. package/bundles/codex/.codex/skills/frontend/flow-studio/src/rules.test.ts +48 -1
  178. package/bundles/codex/.codex/skills/frontend/flow-studio/src/rules.ts +52 -1
  179. package/bundles/codex/.codex/skills/frontend/flow-studio/src/selection.test.ts +12 -0
  180. package/bundles/codex/.codex/skills/frontend/flow-studio/src/selection.ts +4 -0
  181. package/bundles/codex/.codex/skills/frontend/flow-studio/src/stateCss.test.ts +7 -0
  182. package/bundles/codex/.codex/skills/frontend/flow-studio/src/stateCss.ts +1 -0
  183. package/bundles/codex/.codex/skills/frontend/flow-studio/src/styles.css +44 -10
  184. package/bundles/codex/.codex/skills/frontend/flow-studio/src/trace.test.ts +21 -1
  185. package/bundles/codex/.codex/skills/frontend/flow-studio/src/trace.ts +18 -0
  186. package/bundles/codex/.codex/skills/frontend/flow-studio/templates/engine.html +995 -82
  187. package/bundles/codex/.codex/skills/frontend/flow-studio/templates/engine.shell.html +1 -39
  188. package/bundles/codex/.codex/skills/frontend/flow-studio/templates/flow-studio.css +191 -0
  189. package/bundles/codex/.codex/skills/frontend/flow-studio/templates/flow-studio.lib.d.mts +1 -0
  190. package/bundles/codex/.codex/skills/frontend/flow-studio/templates/flow-studio.lib.mjs +2492 -0
  191. package/bundles/codex/.codex/skills/frontend/flow-studio/templates/shell-body.html +50 -0
  192. package/bundles/manifest.json +2 -2
  193. package/package.json +1 -1
@@ -0,0 +1,124 @@
1
+ # Verification harness reference
2
+
3
+ `scripts/check.mjs` is the automated headless browser testing harness for flow-studio pages. It drives Chromium via Playwright, interrogates page geometry and accessibility trees, tests user interactions, and asserts visual and behavioral invariants.
4
+
5
+ ## 1. CLI usage and options
6
+
7
+ ```bash
8
+ # Run against a local build
9
+ node scripts/check.mjs out/lineage.html
10
+
11
+ # Run with custom output directory and viewports
12
+ node scripts/check.mjs out/lineage.html --out check-out --viewports 1600x900,1280x720,820x900
13
+
14
+ # Specify browser channel and skip optional groups
15
+ node scripts/check.mjs out/lineage.html --channel msedge --skip notes,playback,update,hostile
16
+
17
+ # Test hostile payload safety
18
+ node scripts/check.mjs out/lineage.html --hostile out/hostile.html --json report.json
19
+
20
+ # Skip performance benchmarking
21
+ node scripts/check.mjs out/lineage.html --skip-perf
22
+ ```
23
+
24
+ ### CLI options
25
+ - `<page.html | URL>`: File path or HTTP URL to the target page.
26
+ - `--out <dir>`: Directory where screenshots and failure captures are saved (default: `check-out`).
27
+ - `--viewports <WxH,...>`: Comma-separated list of test viewports (default: `1600x900,1280x720,820x900`).
28
+ - `--channel <name>`: Browser executable channel (e.g. `msedge`, `chrome`).
29
+ - `--skip <groups>`: Comma-separated list of test groups to skip (`notes`, `playback`, `update`, `hostile`). A skipped group is not a pass: skip one only when the page intentionally omits that feature, and record every skip in your verification notes.
30
+ - `--hostile <file.html>`: Path to hostile test page to assert XSS and injection resistance.
31
+ - `--json <file.json>`: Output machine-readable test results to a JSON report.
32
+ - `--skip-perf`: Omit render and zoom frame timing performance assertions.
33
+
34
+ ---
35
+
36
+ ## 2. Check groups and assertions
37
+
38
+ `scripts/check.mjs` organizes assertions into distinct groups:
39
+
40
+ ### 1. `structural`
41
+ Validates that layout geometry satisfies visual clarity:
42
+ - **Zero node-box intersections:** No two node bounding boxes overlap or intersect.
43
+ - **Node inside lane:** Every node sits completely within its designated swimlane boundaries.
44
+ - **No lane overlap:** Swimlanes do not collide vertically.
45
+ - **Links touch node boxes:** Link start points and end points connect cleanly to their endpoint boxes without gaps or overshoots.
46
+ - **Fit shows every node:** Canvas auto-fit places all nodes fully within the visible viewport bounds.
47
+ - **Hidden nodes not painted:** Collapsed or view-filtered nodes have `visibility: hidden` and `opacity: 0`.
48
+
49
+ ### 2. `interaction`
50
+ Validates user input, navigation, and state machines:
51
+ - **Wheel zoom & pan:** Canvas zooms centered on the mouse position; zoom is clamped between 0.25x and 2.5x.
52
+ - **Key navigation:** `0` fits canvas, arrow keys pan the viewport, `Escape` clears selection and closes the inspector.
53
+ - **Group expansion / collapse:** Clicking expand/collapse buttons toggles child visibility and updates visible node counts.
54
+ - **Node selection:** Clicking a node selects it, highlights connected edges, and slides open `#inspector`.
55
+ - **N-hop search:** Searching terms highlights matches and dims non-matching nodes; increasing hops expands the lit radius.
56
+ - **What-if simulation:** Clicking what-if buttons cascades simulated failure states through downstream dependents.
57
+ - **View switcher:** Toggling views displays only elements specified in `visibleIn`.
58
+
59
+ ### 3. `a11y` (Accessibility)
60
+ Validates compliance with WCAG 2.2 AA standards:
61
+ - **Accessible names:** Every button, input, tab, and control has a non-empty `aria-label` or accessible text name.
62
+ - **Keyboard navigation:** Visible nodes receive focus via Tab; hidden nodes are not reachable via Tab. No keyboard traps.
63
+ - **Focus visibility:** Focused nodes display an active outline.
64
+ - **Contrast ratios:** Normal text has at least 4.5:1 contrast against surface backgrounds in both light and dark themes.
65
+ - **Table view:** An accessible `<table class="sources" id="tbl">` lists all visible graph nodes for screen reader users.
66
+ - **Reduced motion:** `prefers-reduced-motion` disables all transitions and animations, setting edge dashes static.
67
+
68
+ ### 4. `safety`
69
+ Validates security against hostile content and network isolation:
70
+ - **No unhandled page errors:** Zero unhandled JavaScript exceptions in the console.
71
+ - **No external network requests:** Zero external HTTP/HTTPS requests (local and standalone execution).
72
+ - **Hostile string escaping:** Malicious HTML (`</script>`, `<img onerror=...>`, Unicode separators) is rendered as plain text without script execution or DOM injection.
73
+
74
+ ### 5. `performance`
75
+ Validates responsiveness and frame timing:
76
+ - **First render time:** Initial canvas render of all nodes must complete in under 500 ms.
77
+ - **Frame budget:** 95th percentile wheel event handling time must be under 16 ms (60 fps threshold).
78
+ - **Self-contained bundle size:** Generated single-file HTML pages must stay under 300 KB.
79
+
80
+ ### 6. `notes`
81
+ Validates the implementation notes drawer:
82
+ - In notes mode, numbered markers appear for every valid anchor.
83
+ - Hovering and clicking markers coordinates with drawer cards.
84
+
85
+ ### 7. `playback`
86
+ Validates scenario animation:
87
+ - Stepping moves the active token along specified links in sequence.
88
+ - Decision gateways show branch choice buttons in step mode.
89
+ - SLA breaches trigger red counter highlighting and failure flash styling.
90
+
91
+ ### 8. `update`
92
+ Validates live graph data updates:
93
+ - Calling `FlowStudio.update(newModel)` updates topology while preserving pan, zoom, and active selection.
94
+ - Invalid model updates preserve the previous valid DOM state and render `#banner` error notice.
95
+
96
+ ### 9. `hostile`
97
+ Validates adversarial resistance:
98
+ - Ingests `examples/hostile.json` to prove the parser and inspector remain inert against script injection.
99
+
100
+ ---
101
+
102
+ ## 3. Thresholds and limits
103
+
104
+ | Metric / Check | Threshold | Rationale |
105
+ |---|---|---|
106
+ | First render duration | `< 500 ms` | Fast initial load experience |
107
+ | Wheel event latency (p95) | `< 16 ms` | Smooth 60fps pan and zoom interaction |
108
+ | Zoom range | `0.25` to `2.5` | Prevents disorientation from extreme zoom levels |
109
+ | Contrast ratio | `>= 4.5:1` | WCAG AA readability in light and dark modes |
110
+ | Node count guideline | `8` to `30` initial visible | Avoids visual clutter in default fit |
111
+ | HTML bundle size | `< 300 KB` | Portable, zero-dependency self-contained distribution |
112
+
113
+ ---
114
+
115
+ ## 4. Reading failures and fixing issues
116
+
117
+ When a check fails, the harness logs:
118
+ ```text
119
+ FAIL [structural] 1600x900 zero node-box intersections crm-db/cdc-pipe
120
+ ```
121
+ 1. **Identify the group and viewport:** Here, `structural` at `1600x900`.
122
+ 2. **Read the detail payload:** The detail string lists the offending nodes (`crm-db` and `cdc-pipe`).
123
+ 3. **Inspect the screenshot:** Open `check-out/01-fit-1600x900.png` to inspect the visual collision.
124
+ 4. **Apply the fix:** Adjust the column width or node row/slot positions in `model.json`, then re-run `node scripts/check.mjs`.
@@ -1,8 +1,8 @@
1
1
  // Build templates/engine.html from the TypeScript sources.
2
2
  // node --experimental-strip-types scripts/build-engine.mjs write templates/engine.html
3
3
  // node --experimental-strip-types scripts/build-engine.mjs --check exit 1 if the committed file is stale
4
- // esbuild is a dev-time tool. It is looked up in this folder, in the caddis cli folder, then in the
5
- // current directory. The built page has no dependency on it.
4
+ // esbuild is a dev-time tool. It is looked up in FLOW_STUDIO_NODE_MODULES first, then this folder,
5
+ // the caddis cli folder, and the current directory. The built page has no dependency on it.
6
6
 
7
7
  import { createRequire } from "node:module";
8
8
  import { readFileSync, writeFileSync, existsSync } from "node:fs";
@@ -12,13 +12,23 @@ import { fileURLToPath } from "node:url";
12
12
  const here = dirname(fileURLToPath(import.meta.url));
13
13
  const root = resolve(here, "..");
14
14
 
15
- function loadEsbuild() {
16
- const candidates = [join(root, "package.json"), resolve(root, "../../../../cli/package.json"), join(process.cwd(), "package.json")];
17
- for (const c of candidates) {
18
- if (!existsSync(dirname(c))) continue;
19
- try { return createRequire(c)("esbuild"); } catch { /* try the next place */ }
15
+ export function resolveEsbuild(candidateDirs, requireFactory = createRequire) {
16
+ for (const dir of candidateDirs) {
17
+ if (!existsSync(dir)) continue;
18
+ try { return requireFactory(join(dir, "package.json"))("esbuild"); } catch { /* try the next place */ }
20
19
  }
21
- throw new Error("esbuild was not found. Run `npm i -D esbuild` in the skill folder, or run this from the caddis checkout (cli/node_modules has it).");
20
+ throw new Error("esbuild was not found. Check FLOW_STUDIO_NODE_MODULES or run `npm i -D esbuild` in the skill folder.");
21
+ }
22
+
23
+ function loadEsbuild() {
24
+ const configured = process.env.FLOW_STUDIO_NODE_MODULES;
25
+ const candidates = [
26
+ ...(configured ? [configured] : []),
27
+ root,
28
+ resolve(root, "../../../../cli"),
29
+ process.cwd(),
30
+ ];
31
+ return resolveEsbuild(candidates);
22
32
  }
23
33
 
24
34
  const normalise = text => text.replace(/\r\n/g, "\n");
@@ -26,7 +36,7 @@ const normalise = text => text.replace(/\r\n/g, "\n");
26
36
  export async function buildEngine() {
27
37
  const esbuild = loadEsbuild();
28
38
  const out = await esbuild.build({
29
- entryPoints: [join(root, "src/engine.ts")],
39
+ entryPoints: [join(root, "src/page.ts")],
30
40
  bundle: true, format: "iife", target: "es2022", minify: false, write: false, legalComments: "none", logLevel: "silent",
31
41
  });
32
42
  const js = out.outputFiles[0].text;
@@ -35,20 +45,50 @@ export async function buildEngine() {
35
45
  if (/<\/style/i.test(css)) throw new Error("The stylesheet contains a closing style tag; it would end the page style early.");
36
46
  if (css.includes("/*__ENGINE__*/") || css.includes("/*__MODEL__*/") || css.includes("/*__NOTES__*/")) throw new Error("The stylesheet contains a template slot marker.");
37
47
  const shell = readFileSync(join(root, "templates/engine.shell.html"), "utf8");
38
- for (const slot of ["/*__CSS__*/", "/*__ENGINE__*/", "/*__MODEL__*/{}", "/*__NOTES__*/[]"]) {
48
+ for (const slot of ["/*__CSS__*/", "/*__SHELL_BODY__*/", "/*__ENGINE__*/", "/*__MODEL__*/{}", "/*__NOTES__*/[]"]) {
39
49
  if (!shell.includes(slot)) throw new Error(`The shell is missing the slot ${slot}`);
40
50
  }
41
- return normalise(shell.replace("/*__CSS__*/", () => css).replace("/*__ENGINE__*/", () => js));
51
+ const body = readFileSync(join(root, "templates/shell-body.html"), "utf8");
52
+ return normalise(shell.replace("/*__CSS__*/", () => css).replace("/*__SHELL_BODY__*/", () => body).replace("/*__ENGINE__*/", () => js));
53
+ }
54
+
55
+ export async function buildLibrary() {
56
+ const esbuild = loadEsbuild();
57
+ const out = await esbuild.build({
58
+ entryPoints: [join(root, "src/lib.ts")], bundle: true, format: "esm", target: "es2022",
59
+ minify: false, write: false, legalComments: "none", logLevel: "silent",
60
+ });
61
+ const marker = JSON.stringify("/*__SHELL_BODY__*/");
62
+ const bundled = out.outputFiles[0].text;
63
+ if (bundled.split(marker).length !== 2) throw new Error("The library must contain one shell body slot.");
64
+ const body = readFileSync(join(root, "templates/shell-body.html"), "utf8");
65
+ const js = bundled.replace(marker, () => JSON.stringify(body));
66
+ const css = readFileSync(join(root, "src/styles.css"), "utf8");
67
+ return { js: normalise(js), css };
42
68
  }
43
69
 
44
70
  if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
45
71
  const target = join(root, "templates/engine.html");
46
- const html = await buildEngine();
72
+ const libTarget = join(root, "templates/flow-studio.lib.mjs");
73
+ const cssTarget = join(root, "templates/flow-studio.css");
47
74
  if (process.argv.includes("--check")) {
75
+ const html = await buildEngine();
76
+ const { js, css } = await buildLibrary();
48
77
  const have = existsSync(target) ? normalise(readFileSync(target, "utf8")) : "";
49
- if (have !== html) { console.error("templates/engine.html is stale. Run: node --experimental-strip-types scripts/build-engine.mjs"); process.exit(1); }
50
- console.log("templates/engine.html is up to date");
78
+ const haveLib = existsSync(libTarget) ? normalise(readFileSync(libTarget, "utf8")) : "";
79
+ const haveCss = existsSync(cssTarget) ? readFileSync(cssTarget, "utf8") : "";
80
+ if (have !== html || haveLib !== js || haveCss !== css) {
81
+ console.error("Generated engine or library files are stale. Run `node --experimental-strip-types scripts/build-engine.mjs` and `node --experimental-strip-types scripts/build-engine.mjs --lib`.");
82
+ process.exit(1);
83
+ }
84
+ console.log("engine and library files are up to date");
85
+ } else if (process.argv.includes("--lib")) {
86
+ const { js, css } = await buildLibrary();
87
+ writeFileSync(libTarget, js, "utf8");
88
+ writeFileSync(cssTarget, css, "utf8");
89
+ console.log(`wrote templates/flow-studio.lib.mjs and templates/flow-studio.css`);
51
90
  } else {
91
+ const html = await buildEngine();
52
92
  writeFileSync(target, html, "utf8");
53
93
  console.log(`wrote templates/engine.html (${html.length} bytes)`);
54
94
  }
@@ -1,25 +1,32 @@
1
1
  // Build one self-contained HTML page from a model.
2
- // node --experimental-strip-types scripts/build.mjs <model.json> <out.html> [--notes notes.json]
2
+ // node --experimental-strip-types scripts/build.mjs <model.json> <out.html> [--notes notes.json] [--demo-pack]
3
3
  // The model is validated first. Any error stops the build (exit 1). Warnings are printed.
4
4
 
5
5
  import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
6
6
  import { dirname, resolve } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { validateModel } from "../src/model.ts";
9
+ import { autoLayout } from "../src/autolayout.ts";
9
10
  import { embedModel } from "./lib/embed.mjs";
11
+ import { prepareModel } from "./lib/auto-entry-guard.mjs";
12
+ import { keepDefaultNotes } from "./lib/demo-pack.mjs";
10
13
  import { loadModel, report } from "./validate.mjs";
11
14
 
12
15
  const here = dirname(fileURLToPath(import.meta.url));
13
16
  const args = process.argv.slice(2);
17
+ const demoPackIndex = args.indexOf("--demo-pack");
18
+ const demoPack = demoPackIndex >= 0;
19
+ if (demoPack) args.splice(demoPackIndex, 1);
14
20
  const notesAt = args.indexOf("--notes");
15
21
  const notesPath = notesAt >= 0 ? args.splice(notesAt, 2)[1] : null;
16
22
  const [modelPath, outPath] = args;
17
23
  if (!modelPath || !outPath) {
18
- console.error("usage: node --experimental-strip-types scripts/build.mjs <model.json> <out.html> [--notes notes.json]");
24
+ console.error("usage: node --experimental-strip-types scripts/build.mjs <model.json> <out.html> [--notes notes.json] [--demo-pack]");
19
25
  process.exit(2);
20
26
  }
21
27
 
22
- const model = loadModel(modelPath);
28
+ const input = loadModel(modelPath);
29
+ const model = prepareModel(input, autoLayout);
23
30
  const errors = report(validateModel(model));
24
31
  if (errors) {
25
32
  console.error(`${errors} error(s): not building.`);
@@ -30,8 +37,18 @@ if (notesPath && !Array.isArray(extra)) {
30
37
  console.error(`${notesPath} must be a JSON array of notes.`);
31
38
  process.exit(2);
32
39
  }
33
- const notes = [...(Array.isArray(model.notes) ? model.notes : []), ...(Array.isArray(extra) ? extra : [])].map((n, i) => ({ ...n, id: i + 1 }));
40
+
34
41
  const shell = readFileSync(resolve(here, "../templates/engine.html"), "utf8");
42
+
43
+ let defaultNotes = [];
44
+ if (demoPack) {
45
+ const defaults = loadModel(resolve(here, "default-notes.json"));
46
+ if (Array.isArray(defaults)) {
47
+ defaultNotes = keepDefaultNotes(defaults, model, shell);
48
+ }
49
+ }
50
+
51
+ const notes = [...defaultNotes, ...(Array.isArray(model.notes) ? model.notes : []), ...(Array.isArray(extra) ? extra : [])].map((n, i) => ({ ...n, id: i + 1 }));
35
52
  mkdirSync(dirname(resolve(outPath)), { recursive: true });
36
53
  writeFileSync(outPath, embedModel(shell, model, notes), "utf8");
37
54
  console.log(`wrote ${outPath}`);