@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,213 @@
1
+ # Implement a flow in an application
2
+
3
+ Use an approved flow model as input. Validate it against `model.schema.json` before connecting live data. The demo page shows the intended interaction. Keep this guide with the skill when copying files into an application.
4
+
5
+ In the commands, `<skill>` is the path to the flow-studio skill folder in your checkout. Your app has no copy of the skill's scripts, so commands that run them always name the `<skill>` path.
6
+
7
+ ## 1. Pick the route
8
+
9
+ | Requirement | Route | Implementation |
10
+ |---|---|---|
11
+ | The app may use the flow engine's own styling and controls | Fast wrapper / library embed | `templates/flow-studio.lib.mjs` provides the full self-contained engine. |
12
+ | The app must use its own design tokens, buttons, and drawer | Native components & hooks | Pure modules (`src/`) supply geometry, layout, and trace logic to app components. |
13
+
14
+ The files are copyable source, not a published npm package. Both routes need a caller that supplies a valid `Model`; neither fetches app data for you.
15
+
16
+ ---
17
+
18
+ ## 2. Lock the model and data contract
19
+
20
+ 1. Save the approved model as JSON. Run `node --experimental-strip-types <skill>/scripts/validate.mjs <model.json>`. Fix any reported errors.
21
+ 2. Define a typed state for the app's data source: `loading`, `ready` with a `Model`, `empty`, or `error` with a user-facing message and retry action.
22
+ 3. Map node IDs from immutable business keys (e.g. `order-service-uuid`). Map link IDs from immutable relation keys. Never use array indices or volatile titles.
23
+ 4. Keep absent values absent. Do not turn a missing measurement into `0`. Use state words and timestamps (`asOf`) to distinguish measured data from declarations.
24
+ 5. On refresh error, keep the last valid model visible and show a notification banner. On a valid refresh, pass the new model with stable IDs so `engine.update(model)` preserves pan, zoom, and active selection.
25
+ 6. Render all model strings as plain text. Never use raw `innerHTML` for titles, subtitles, or labels.
26
+
27
+ ---
28
+
29
+ ## 3. Test-first workflow in the target app
30
+
31
+ 1. **Verify skill tests:** Run the test suite:
32
+ ```bash
33
+ node --experimental-strip-types --no-warnings --test "src/*.test.ts" "scripts/lib/*.test.mjs"
34
+ ```
35
+ 2. **Author mapping test:** Write an app test verifying the data-to-model mapper produces stable IDs, preserves absent fields, and handles loading/error states. See the test fail before implementing the mapper.
36
+ 3. **Author component test:** Write a test verifying component mount, node selection callback, and update behavior.
37
+ 4. **Implement iteratively:** Implement mapping and component wiring, re-running the focused test after each step until green.
38
+
39
+ ---
40
+
41
+ ## 4. Integration guides by framework
42
+
43
+ ### A. React
44
+
45
+ Copy `templates/flow-studio.lib.mjs`, `templates/flow-studio.lib.d.mts`, `templates/flow-studio.css`, `react/`, and `src/` into the app, keeping the relative layout.
46
+
47
+ ```tsx
48
+ import React, { useEffect, useRef } from "react";
49
+ import { FlowStudioView, FlowLoading, FlowError } from "./react/index";
50
+ import type { Model } from "./src/model";
51
+ import "./templates/flow-studio.css";
52
+
53
+ export function FlowContainer({ model, status, error, onRetry }: { model?: Model; status: string; error?: string; onRetry: () => void }) {
54
+ const [selectedId, setSelectedId] = React.useState<string | null>(null);
55
+
56
+ if (status === "loading") return <FlowLoading />;
57
+ if (status === "error") return <FlowError message={error!} onRetry={onRetry} />;
58
+ if (!model || model.nodes.length === 0) return <div>No data available</div>;
59
+
60
+ return (
61
+ <div style={{ height: "80vh", width: "100%" }}>
62
+ <FlowStudioView
63
+ model={model}
64
+ onSelect={(id) => setSelectedId(id)}
65
+ />
66
+ </div>
67
+ );
68
+ }
69
+ ```
70
+
71
+ ### B. Vue (Vue 3 / Vite)
72
+
73
+ Copy `templates/flow-studio.lib.mjs`, `templates/flow-studio.lib.d.mts`, and `templates/flow-studio.css` to your application assets directory.
74
+
75
+ ```vue
76
+ <script setup lang="ts">
77
+ import { ref, onMounted, onBeforeUnmount, watch } from "vue";
78
+ import { mount } from "../assets/flow-studio.lib.mjs";
79
+ import "../assets/flow-studio.css";
80
+
81
+ const props = defineProps<{ model: any }>();
82
+ const emit = defineEmits(["select"]);
83
+ const containerRef = ref<HTMLDivElement | null>(null);
84
+ let engineInstance: any = null;
85
+
86
+ onMounted(() => {
87
+ if (containerRef.value && props.model) {
88
+ engineInstance = mount(containerRef.value, props.model, {
89
+ onSelect: (id: string | null) => emit("select", id)
90
+ });
91
+ }
92
+ });
93
+
94
+ watch(() => props.model, (newModel) => {
95
+ if (engineInstance && newModel) {
96
+ engineInstance.update(newModel);
97
+ }
98
+ });
99
+
100
+ onBeforeUnmount(() => {
101
+ if (engineInstance) {
102
+ engineInstance.destroy();
103
+ engineInstance = null;
104
+ }
105
+ });
106
+ </script>
107
+
108
+ <template>
109
+ <div ref="containerRef" class="flow-container"></div>
110
+ </template>
111
+
112
+ <style scoped>
113
+ .flow-container { width: 100%; height: 80vh; position: relative; }
114
+ </style>
115
+ ```
116
+
117
+ ### C. Svelte (Svelte 4 / 5)
118
+
119
+ Copy the same library files as for Vue. The import is aliased because Svelte 5 exports its own `mount`.
120
+
121
+ ```svelte
122
+ <script lang="ts">
123
+ import { onMount, onDestroy } from "svelte";
124
+ import { mount as mountFlow } from "./flow-studio.lib.mjs";
125
+ import "./flow-studio.css";
126
+
127
+ export let model: any;
128
+ export let onSelect: (id: string | null) => void = () => {};
129
+
130
+ let container: HTMLDivElement;
131
+ let engine: any;
132
+
133
+ onMount(() => {
134
+ engine = mountFlow(container, model, { onSelect });
135
+ });
136
+
137
+ $: if (engine && model) {
138
+ engine.update(model);
139
+ }
140
+
141
+ onDestroy(() => {
142
+ if (engine) engine.destroy();
143
+ });
144
+ </script>
145
+
146
+ <div bind:this={container} class="flow-container"></div>
147
+
148
+ <style>
149
+ .flow-container { width: 100%; height: 80vh; position: relative; }
150
+ </style>
151
+ ```
152
+
153
+ ### D. Vanilla HTML & JavaScript
154
+
155
+ ```html
156
+ <!DOCTYPE html>
157
+ <html lang="en">
158
+ <head>
159
+ <meta charset="utf-8">
160
+ <link rel="stylesheet" href="./templates/flow-studio.css">
161
+ <style>
162
+ #flowHost { width: 100vw; height: 100vh; }
163
+ </style>
164
+ </head>
165
+ <body>
166
+ <div id="flowHost"></div>
167
+ <script type="module">
168
+ import { mount } from "./templates/flow-studio.lib.mjs";
169
+
170
+ const response = await fetch("/api/lineage-model");
171
+ const model = await response.json();
172
+
173
+ const engine = mount(document.getElementById("flowHost"), model, {
174
+ onSelect: (selectedId) => {
175
+ console.log("Selected node:", selectedId);
176
+ }
177
+ });
178
+
179
+ // Expose for external live event updates
180
+ window.updateFlow = (newModel) => engine.update(newModel);
181
+ </script>
182
+ </body>
183
+ </html>
184
+ ```
185
+
186
+ ---
187
+
188
+ ## 5. State synchronization and host events
189
+
190
+ `mount()` returns the engine API, and every mount also publishes the same API object on `window.FlowStudio`. Hold the value that `mount()` returns in a variable named `engine` in the examples below:
191
+
192
+ 1. **Selection listening:** pass `onSelect(nodeId: string | null)` as a mount option to receive updates when a user selects a card or chip, or clears the selection.
193
+ 2. **Programmatic selection:** call `engine.select(nodeId)` (or `window.FlowStudio.select(nodeId)`) to focus and inspect a node from outside the canvas, for example from an app table row.
194
+ 3. **Reading active state:** call `engine.selected()`, `engine.view()`, or `engine.mode()` to read the current canvas state.
195
+ 4. **Live topology updates:** call `engine.update(newModel)` to refresh node metrics, statuses, or structure without losing the user's pan and zoom. A model that fails validation is rejected, the last good graph stays, and the engine shows a banner.
196
+
197
+ In the React wrapper route the engine instance is private to `FlowStudioView`: use the `onSelect` prop for selection changes and drive new data through the `model` prop (the wrapper calls `update` itself). Use `window.FlowStudio` for a direct call only while exactly one view is mounted.
198
+
199
+ ---
200
+
201
+ ## 6. Verifying the app in browser check harness
202
+
203
+ Run the skill's check harness against your local dev server:
204
+
205
+ ```bash
206
+ node <skill>/scripts/check.mjs http://127.0.0.1:5173/flow --out shots --channel msedge --skip hostile
207
+ ```
208
+
209
+ The harness is not copied into your app, so the command always names the `<skill>` path. Run it from a folder whose `node_modules` resolves `playwright`.
210
+
211
+ - When tests fail, inspect the screenshot in `shots/` and read the error detail.
212
+ - Ensure the app container provides at least 540px height for desktop viewports.
213
+ - `--skip` takes a comma-separated list of groups (`notes`, `playback`, `update`, `hostile`). Skip a group only when the app intentionally omits that feature. Record every skipped group in your verification notes. Never treat a skipped check as a pass.
@@ -0,0 +1,254 @@
1
+ # Model schema reference
2
+
3
+ This reference documents the structure and properties of the flow-studio model specified in `model.schema.json`. Every property is listed with its type, requirement status, default value, and semantic description.
4
+
5
+ ## 1. Top-level model object
6
+
7
+ A valid model requires the core structural properties.
8
+
9
+ | Property | Type | Required? | Default | Description |
10
+ |---|---|---|---|---|
11
+ | `version` | `integer` | Required | `1` | Schema version (must equal 1). |
12
+ | `meta` | `object` | Required | - | Metadata describing the scenario, authorship, and timestamp. |
13
+ | `layout` | `string` | Required | - | Layout mode: `"columns-lanes"`, `"free"`, or `"auto"`. |
14
+ | `columns` | `array` | Required | - | Array of vertical column definitions. |
15
+ | `lanes` | `array` | Required | - | Array of horizontal swimlane definitions. |
16
+ | `states` | `object` | Required | - | Dictionary of visual state definitions keyed by state ID. |
17
+ | `nodes` | `array` | Required | - | Array of node and card elements. |
18
+ | `links` | `array` | Required | - | Array of directional connections between nodes. |
19
+ | `views` | `array` | Optional | - | Optional filtered or perspective views. |
20
+ | `inspector` | `object` | Optional | - | Inspector relationships and what-if simulation actions. |
21
+ | `scenarios` | `array` | Optional | - | Playback scenarios for animated token traces. |
22
+ | `notes` | `array` | Optional | - | Implementation and design callout notes. |
23
+
24
+ ---
25
+
26
+ ## 2. `meta` object
27
+
28
+ Contains descriptive and provenance metadata for the diagram.
29
+
30
+ | Property | Type | Required? | Default | Description |
31
+ |---|---|---|---|---|
32
+ | `title` | `string` | Required | - | Header title of the flow. |
33
+ | `help` | `string` | Required | - | Subtitle explaining the scenario purpose and context. |
34
+ | `asOf` | `string` | Required | - | Date or timestamp representing data freshness (e.g. `"2026-10-06"`). |
35
+ | `source` | `string` | Required | - | Provenance, API, database, or owning team. |
36
+ | `labels` | `object` | Optional | - | Key-value dictionary of auxiliary string labels. |
37
+
38
+ ---
39
+
40
+ ## 3. `columns` array items
41
+
42
+ Columns provide vertical partitioning for time, stages, or architecture tiers.
43
+
44
+ | Property | Type | Required? | Default | Description |
45
+ |---|---|---|---|---|
46
+ | `id` | `string` | Required | - | Unique column identifier. |
47
+ | `title` | `string` | Required | - | Display title shown in the column header. |
48
+ | `short` | `string` | Optional | - | Compact abbreviated title for tight viewports. |
49
+ | `width` | `number` | Required | - | Column width in pixels (minimum 60). |
50
+ | `phase` | `string` | Optional | - | Super-category phase grouping shown in top banner. |
51
+
52
+ ---
53
+
54
+ ## 4. `lanes` array items
55
+
56
+ Swimlanes partition the canvas horizontally by owning team, system, or tier.
57
+
58
+ | Property | Type | Required? | Default | Description |
59
+ |---|---|---|---|---|
60
+ | `id` | `string` | Required | - | Unique lane identifier. |
61
+ | `title` | `string` | Required | - | Display title of the swimlane. |
62
+ | `badge` | `string` | Optional | - | Small uppercase pill badge text (e.g. `"PROD"`). |
63
+ | `badgeTone` | `string` | Optional | `"muted"` | Badge color tone: `"ok"`, `"warn"`, or `"muted"`. |
64
+ | `note` | `string` | Optional | - | Explanatory note rendered beside the lane title. |
65
+ | `gap` | `boolean` | Optional | `false` | When true, renders the lane with an amber dashed border indicating unverified systems. |
66
+
67
+ ---
68
+
69
+ ## 5. `states` dictionary items
70
+
71
+ States define semantic styling, borders, and animations for nodes and edges.
72
+
73
+ | Property | Type | Required? | Default | Description |
74
+ |---|---|---|---|---|
75
+ | `word` | `string` | Required | - | Human-readable state badge text (e.g. `"HEALTHY"`, `"FAILED"`). |
76
+ | `tone` | `string` | Required | - | Semantic color: `"ok"`, `"warn"`, `"crit"`, `"info"`, `"muted"`, or `"accent"`. |
77
+ | `border` | `string` | Required | - | Card border style: `"solid"`, `"dashed"`, or `"dotted"`. |
78
+ | `edge` | `string` | Required | - | Link stroke dash pattern: `"flow"`, `"dash"`, `"dash-short"`, or `"dot"`. |
79
+ | `motion` | `string` | Required | - | State animation: `"flow"`, `"flow-slow"`, `"breathe"`, or `"none"`. |
80
+ | `bucket` | `string` | Required | - | Health bucket category: `"good"`, `"neutral"`, or `"gap"`. |
81
+
82
+ ---
83
+
84
+ ## 6. `nodes` array items
85
+
86
+ Nodes represent processing units, cards, gateways, groups, and milestones.
87
+
88
+ | Property | Type | Required? | Default | Description |
89
+ |---|---|---|---|---|
90
+ | `id` | `string` | Required | - | Unique node identifier. |
91
+ | `kind` | `string` | Required | - | Node kind: `"card"`, `"group"`, `"chip"`, `"gateway"`, `"start"`, `"end"`, `"event"`, `"placeholder"`, or `"note"`. |
92
+ | `state` | `string` | Required | - | Key referencing a valid state in `states`. |
93
+ | `title` | `string` | Required | - | Primary label or name of the node. |
94
+ | `subtitle` | `string` | Optional | - | Secondary description line. |
95
+ | `lane` | `string` | Required (grid/free) | - | ID of the lane containing this node. |
96
+ | `col` | `integer` | Required (grid) | - | Zero-based column index where the node begins. |
97
+ | `colSpan` | `integer` | Optional | `1` | Number of columns spanned (minimum 1). |
98
+ | `row` | `number` | Required (grid) | - | Vertical slot index within the lane. |
99
+ | `x` | `number` | Required (free) | - | Absolute horizontal coordinate in pixels. |
100
+ | `y` | `number` | Required (free) | - | Absolute vertical coordinate in pixels. |
101
+ | `w` | `number` | Optional | auto | Explicit node width in pixels. |
102
+ | `h` | `number` | Optional | auto | Explicit node height in pixels. |
103
+ | `stateIn` | `object` | Optional | - | View-specific state overrides keyed by view ID. |
104
+ | `visibleIn` | `array` | Optional | all | Array of view IDs where this node appears. |
105
+ | `members` | `array` | Optional | - | Array of member entity names or IDs. |
106
+ | `weight` | `number` | Optional | - | Numerical priority or item count (minimum 0). |
107
+ | `ref` | `string` | Optional | - | Target node ID if this node acts as a portal chip. |
108
+ | `upgrade` | `string` | Optional | - | Recommended action or upgrade path. |
109
+ | `allowIsolated` | `boolean` | Optional | `false` | Explicitly permits node to have zero links without failing validation. |
110
+ | `contains` | `array` | Optional | - | Child node IDs contained in this group. |
111
+ | `collapsed` | `boolean` | Optional | `true` | When true, group starts collapsed hiding child nodes. |
112
+ | `sla` | `number` | Optional | - | Target service level agreement threshold in seconds. |
113
+ | `metrics` | `object` | Optional | - | Telemetry payload (see metrics table). |
114
+
115
+ ---
116
+
117
+ ## 7. `metrics` object (under `nodes`)
118
+
119
+ Provides numerical telemetry, health bars, and sparklines.
120
+
121
+ | Property | Type | Required? | Default | Description |
122
+ |---|---|---|---|---|
123
+ | `value` | `number` | Required | - | Primary current numerical metric value. |
124
+ | `max` | `number` or `null` | Optional | `null` | Upper bound for the badge fill bar (null for unbounded). |
125
+ | `unit` | `string` | Optional | - | Display unit (e.g. `"ms"`, `"rps"`, `"MB"`). |
126
+ | `label` | `string` | Optional | - | Descriptive metric label. |
127
+ | `series` | `array` | Optional | - | Historical series array (up to 60 numbers) rendered as sparkline. |
128
+
129
+ ---
130
+
131
+ ## 8. `links` array items
132
+
133
+ Links represent directional data transit, dependencies, or workflow transitions.
134
+
135
+ | Property | Type | Required? | Default | Description |
136
+ |---|---|---|---|---|
137
+ | `id` | `string` | Required | - | Unique link identifier. |
138
+ | `from` | `string` | Required | - | Source node ID. |
139
+ | `to` | `string` | Required | - | Target node ID. |
140
+ | `state` | `string` | Required | - | Key referencing a valid state in `states`. |
141
+ | `stateIn` | `object` | Optional | - | View-specific state overrides keyed by view ID. |
142
+ | `visibleIn` | `array` | Optional | all | Array of view IDs where this link appears. |
143
+ | `label` | `string` | Optional | - | Text label displayed on the link path. |
144
+ | `kind` | `string` | Optional | `"forward"` | Link direction: `"forward"` or `"loop-back"`. |
145
+
146
+ ---
147
+
148
+ ## 9. `views` array items
149
+
150
+ Permit switching between different operational layers or time slices.
151
+
152
+ | Property | Type | Required? | Default | Description |
153
+ |---|---|---|---|---|
154
+ | `id` | `string` | Required | - | Unique view identifier. |
155
+ | `label` | `string` | Required | - | Button label shown in the mode switch toolbar. |
156
+ | `default` | `boolean` | Optional | `false` | Marks the initial default active view. |
157
+
158
+ ---
159
+
160
+ ## 10. `inspector` object
161
+
162
+ Configures the drill-down side panel and simulation capabilities.
163
+
164
+ | Property | Type | Required? | Default | Description |
165
+ |---|---|---|---|---|
166
+ | `relations` | `array` | Optional | - | Array of up to 3 relation queries (see below). |
167
+ | `actions` | `array` | Optional | - | Array of what-if simulation buttons. |
168
+
169
+ ### `relations` array items
170
+ - `id` (`string`, required): Unique relation query identifier.
171
+ - `label` (`string`, required): Button text displayed in the inspector header.
172
+ - `dir` (`string`, required): Traversal direction: `"up"` (upstream) or `"down"` (downstream).
173
+ - `filter` (`object`, optional): Criteria to filter matched nodes:
174
+ - `col` (`integer`, optional): Restrict matches to a specific column index.
175
+ - `kind` (`string`, optional): Restrict matches to a specific node kind.
176
+
177
+ ### `actions` array items
178
+ - `id` (`string`, required): Unique action identifier.
179
+ - `label` (`string`, required): Button label (e.g. `"Simulate: DB Failure"`).
180
+ - `dir` (`string`, required): Must be `"down"`.
181
+ - `staleText` (`string`, required): Description of the simulated failure consequence.
182
+
183
+ ---
184
+
185
+ ## 11. `scenarios` array items
186
+
187
+ Configures interactive playback traces through the diagram.
188
+
189
+ | Property | Type | Required? | Default | Description |
190
+ |---|---|---|---|---|
191
+ | `id` | `string` | Required | - | Unique scenario identifier. |
192
+ | `label` | `string` | Required | - | Scenario name shown in the playbar dropdown. |
193
+ | `steps` | `array` | Required | - | Ordered sequence of animation steps. |
194
+
195
+ ### `steps` array items
196
+ - `link` (`string`, required): ID of the link traversed during this step.
197
+ - `at` (`number`, optional): Relative elapsed offset from scenario start in seconds.
198
+ - `dur` (`number`, optional): Token travel duration in milliseconds (default: 900).
199
+ - `caption` (`string`, optional): Narrative text displayed in the playbar during this step.
200
+
201
+ ---
202
+
203
+ ## 12. `notes` array items
204
+
205
+ Callouts anchored to HTML elements or nodes.
206
+
207
+ | Property | Type | Required? | Default | Description |
208
+ |---|---|---|---|---|
209
+ | `anchor` | `string` | Required | - | CSS selector resolved with `document.querySelector` at runtime. Target a shell element by id (`#viewport`) or a node card by its generated element id (`#n_` + node id). A bare node id is not a selector. A selector that matches nothing shows no marker. |
210
+ | `title` | `string` | Required | - | Short title of the implementation note. |
211
+ | `text` | `string` | Required | - | Guidance text for building this section. |
212
+
213
+ ---
214
+
215
+ ## 13. Minimal valid model example
216
+
217
+ A complete valid model under 20 lines:
218
+
219
+ ```json
220
+ {
221
+ "version": 1,
222
+ "meta": { "title": "Minimal Flow", "help": "Minimal valid model", "asOf": "2026-10-06", "source": "Core API" },
223
+ "layout": "auto",
224
+ "columns": [{ "id": "c1", "title": "Step 1", "width": 160 }, { "id": "c2", "title": "Step 2", "width": 160 }],
225
+ "lanes": [{ "id": "main", "title": "Primary Lane" }],
226
+ "states": {
227
+ "ok": { "word": "OK", "tone": "ok", "border": "solid", "edge": "flow", "motion": "flow", "bucket": "good" }
228
+ },
229
+ "nodes": [
230
+ { "id": "n1", "kind": "card", "lane": "main", "state": "ok", "title": "Input Node" },
231
+ { "id": "n2", "kind": "card", "lane": "main", "state": "ok", "title": "Output Node" }
232
+ ],
233
+ "links": [{ "id": "l1", "from": "n1", "to": "n2", "state": "ok" }]
234
+ }
235
+ ```
236
+
237
+ ---
238
+
239
+ ## 14. Common validation errors and fixes
240
+
241
+ ### Error 1: Missing required column/lane position in grid layout
242
+ - **Schema error message:** `must have required property 'col'`, `must have required property 'row'`
243
+ - **Cause:** When `layout` is `"columns-lanes"`, all nodes must specify `lane`, `col`, and `row`.
244
+ - **Fix:** Add `"col": 0, "row": 0` to each node, or change `layout` to `"auto"`.
245
+
246
+ ### Error 2: Missing state reference
247
+ - **Validator error message:** `node 'n1' references unknown state 'active'`
248
+ - **Cause:** A node or link sets `state: "active"`, but `"active"` is not defined in the top-level `states` dictionary.
249
+ - **Fix:** Define `"active"` under `states` with valid `word`, `tone`, `border`, `edge`, `motion`, and `bucket`.
250
+
251
+ ### Error 3: Backward link without loop-back marker
252
+ - **Validator warning:** `link 'l2' goes right-to-left from column 2 to column 0; mark as loop-back`
253
+ - **Cause:** A link connects a later column back to an earlier column without declaring `kind: "loop-back"`.
254
+ - **Fix:** Add `"kind": "loop-back"` to the link definition.
@@ -0,0 +1,74 @@
1
+ # Implementation notes and model specification
2
+
3
+ This document explains how implementation notes and the model reference specification work in flow-studio.
4
+
5
+ ## 1. How implementation notes work
6
+
7
+ Implementation notes provide inline architectural, design, and engineering guidance for someone building the production page in an app.
8
+ - Notes are defined as an array of objects: `{ "anchor": "<css-selector>", "title": "...", "text": "..." }`.
9
+ - In `scripts/build.mjs`, all active notes are collected, assigned sequential numeric IDs (`1..n`), and embedded into the HTML via `/*__NOTES__*/[]`.
10
+ - In the browser, `src/notes.ts` renders a slide-out drawer on the left (`#drawer`) and creates circular purple numbered markers (`.marker`) positioned over the anchored target elements.
11
+ - Notes mode toggles via the "Implementation notes (N)" header button (`#notesBtn`) or by pressing the `N` key.
12
+
13
+ ---
14
+
15
+ ## 2. Shell anchors and valid IDs
16
+
17
+ A note can anchor to any node card (for example `#n_crm-db`: the engine gives each node element the id `n_` followed by the node id) or to any of the 13 canonical shell elements defined in `templates/shell-body.html`:
18
+
19
+ | Anchor ID | Shell element | Description |
20
+ |---|---|---|
21
+ | `#title` | Main title | Scenario headline and timestamp metadata |
22
+ | `#layerstrip` | Completeness strip | Column completeness health indicators |
23
+ | `#kpis` | Summary tiles | Summary metrics and gap count indicators |
24
+ | `#modeSwitch` | View switch | Perspective and layer selector buttons |
25
+ | `#q` | Search input | Text search and N-hop filter controls |
26
+ | `#viewport` | Canvas viewport | Pan, zoom, and interactive graph container |
27
+ | `#tools` | Toolbar | Zoom in/out, fit, and pan hand tool buttons |
28
+ | `#inspector` | Side inspector | Slide-in panel for relations, telemetry, and actions |
29
+ | `#legend` | Legend | State color keyline and evidence definitions |
30
+ | `#ledger` | Change ledger | Historical changes or audit log entries |
31
+ | `#tbl` | Table view | Accessible tabular rendering of all graph nodes |
32
+ | `#playbar` | Playback bar | Scenario controls, step scrubber, and branch options |
33
+ | `#notesBtn` | Notes toggle button | Header button to toggle the implementation notes drawer |
34
+
35
+ ---
36
+
37
+ ## 3. The demo pack mechanism
38
+
39
+ The `--demo-pack` option provides comprehensive out-of-the-box guidance:
40
+ - When running `scripts/build.mjs` with `--demo-pack`:
41
+ 1. Default shell notes are loaded from `scripts/default-notes.json`.
42
+ 2. Conditional shell notes are filtered based on the model:
43
+ - `#playbar` note is dropped if the model has no scenarios (`!model.scenarios?.length`).
44
+ - `#modeSwitch` note is dropped if the model has fewer than two views (`(model.views?.length ?? 0) < 2`).
45
+ - A default note whose anchor starts with `#` is dropped when no element with that id exists in the generated shell. Class and attribute selectors are not checked at build time.
46
+ 3. Per-model notes from `model.notes` are appended.
47
+ 4. Additional external notes from `--notes <file.json>` are appended.
48
+ 5. The combined list is re-indexed from 1 to `N`.
49
+
50
+ Model and extra notes are never filtered at build time. At runtime, a note whose selector matches nothing simply gets no marker (see section 4).
51
+
52
+ ---
53
+
54
+ ## 4. Notes panel interaction
55
+
56
+ `src/notes.ts` manages bidirectional interaction between cards and markers:
57
+ - **Marker placement:** Markers calculate bounding client rectangles and hover beside anchored elements.
58
+ - **Hovering a card:** Adds `.anchored` outline to the target element on the canvas and highlights the card in the drawer.
59
+ - **Hovering a marker:** Highlights the target element and scrolls the corresponding card into view in the drawer.
60
+ - **Clicking a marker:** Focuses and scrolls directly to the note text.
61
+ - **Safety:** Hostile selectors in notes (e.g. malformed CSS or script strings) are safely evaluated inside a `try ... catch` block using `document.querySelector`, returning null without throwing errors.
62
+
63
+ ---
64
+
65
+ ## 5. Model reference and specification tab
66
+
67
+ At the bottom of the page, the collapsible `<details class="spec" id="spec">` element renders the complete scenario model specification:
68
+ - **Summary information:** Model title, description, provenance source, and generation date.
69
+ - **States list:** Every state with its word, tone, border style, and edge animation.
70
+ - **Columns & Lanes:** Ordered sequence of columns with phases, and lanes with ownership badges.
71
+ - **Graph size:** Total node count and link count.
72
+ - **Copy buttons:**
73
+ - `Copy as Markdown`: Copies a clean markdown summary formatted with tables and lists.
74
+ - `Copy model JSON`: Copies the formatted JSON payload directly to the clipboard for reuse in an app.