@xenonbyte/da-vinci-workflow 0.1.24 → 0.1.26

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 (62) hide show
  1. package/CHANGELOG.md +35 -1
  2. package/README.md +41 -10
  3. package/README.zh-CN.md +30 -10
  4. package/SKILL.md +4 -0
  5. package/commands/claude/dv/design.md +2 -1
  6. package/commands/codex/prompts/dv-design.md +2 -1
  7. package/commands/gemini/dv/design.toml +2 -1
  8. package/docs/constraint-files.md +1 -0
  9. package/docs/dv-command-reference.md +14 -2
  10. package/docs/pencil-rendering-workflow.md +9 -7
  11. package/docs/prompt-presets/README.md +4 -0
  12. package/docs/visual-assist-presets/README.md +4 -0
  13. package/docs/workflow-examples.md +13 -11
  14. package/docs/workflow-overview.md +2 -0
  15. package/docs/zh-CN/constraint-files.md +1 -0
  16. package/docs/zh-CN/dv-command-reference.md +14 -2
  17. package/docs/zh-CN/pencil-rendering-workflow.md +9 -7
  18. package/docs/zh-CN/prompt-presets/README.md +5 -1
  19. package/docs/zh-CN/visual-assist-presets/README.md +5 -1
  20. package/docs/zh-CN/workflow-examples.md +13 -11
  21. package/docs/zh-CN/workflow-overview.md +2 -0
  22. package/examples/greenfield-spec-markupflow/README.md +6 -1
  23. package/lib/async-offload-worker.js +26 -0
  24. package/lib/async-offload.js +82 -0
  25. package/lib/audit-parsers.js +223 -51
  26. package/lib/audit.js +91 -23
  27. package/lib/cli.js +749 -433
  28. package/lib/fs-safety.js +1 -4
  29. package/lib/icon-aliases.js +7 -7
  30. package/lib/icon-search.js +21 -14
  31. package/lib/icon-sync.js +220 -41
  32. package/lib/install.js +128 -60
  33. package/lib/mcp-runtime-gate.js +4 -7
  34. package/lib/pen-persistence.js +365 -46
  35. package/lib/pencil-lock.js +237 -25
  36. package/lib/pencil-preflight.js +233 -12
  37. package/lib/pencil-session.js +216 -36
  38. package/lib/supervisor-review.js +56 -34
  39. package/lib/utils.js +121 -0
  40. package/lib/workflow-bootstrap.js +255 -0
  41. package/package.json +13 -3
  42. package/references/artifact-templates.md +1 -0
  43. package/references/checkpoints.md +2 -0
  44. package/references/design-inputs.md +2 -0
  45. package/references/pencil-design-to-code.md +2 -0
  46. package/scripts/fixtures/complex-sample.pen +0 -295
  47. package/scripts/fixtures/mock-pencil.js +0 -49
  48. package/scripts/test-audit-context-delta.js +0 -446
  49. package/scripts/test-audit-design-supervisor.js +0 -537
  50. package/scripts/test-audit-safety.js +0 -92
  51. package/scripts/test-icon-aliases.js +0 -96
  52. package/scripts/test-icon-search.js +0 -77
  53. package/scripts/test-icon-sync.js +0 -178
  54. package/scripts/test-mcp-runtime-gate.js +0 -287
  55. package/scripts/test-mode-consistency.js +0 -339
  56. package/scripts/test-pen-persistence.js +0 -254
  57. package/scripts/test-pencil-lock.js +0 -130
  58. package/scripts/test-pencil-preflight.js +0 -169
  59. package/scripts/test-pencil-session.js +0 -192
  60. package/scripts/test-persistence-flows.js +0 -345
  61. package/scripts/test-supervisor-review-cli.js +0 -619
  62. package/scripts/test-supervisor-review-integration.js +0 -115
@@ -0,0 +1,255 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ const { isPathInside } = require("./fs-safety");
5
+ const { ensurePenFile } = require("./pen-persistence");
6
+ const { pathExists, readTextIfExists, writeFileAtomic } = require("./utils");
7
+
8
+ const DEFAULT_PROJECT_PEN_RELATIVE_PATH = ".da-vinci/designs/project-baseline.pen";
9
+
10
+ function validateChangeId(changeId) {
11
+ const normalized = String(changeId || "").trim();
12
+ if (!normalized) {
13
+ return "";
14
+ }
15
+ if (!/^[A-Za-z0-9._-]+$/.test(normalized)) {
16
+ throw new Error(
17
+ `Invalid change id: ${normalized}. Use letters, numbers, dot, underscore, or dash.`
18
+ );
19
+ }
20
+ return normalized;
21
+ }
22
+
23
+ function extractRegisteredPenPath(projectRoot, registryText) {
24
+ const matches = String(registryText || "").match(/\.da-vinci\/designs\/[^\s`]+\.pen/g) || [];
25
+ for (const relativePath of matches) {
26
+ const resolvedPath = path.resolve(projectRoot, relativePath);
27
+ if (isPathInside(projectRoot, resolvedPath)) {
28
+ return resolvedPath;
29
+ }
30
+ }
31
+ return "";
32
+ }
33
+
34
+ function buildProjectTemplates(preferredPenRelativePath) {
35
+ return {
36
+ "DA-VINCI.md": [
37
+ "# DA-VINCI",
38
+ "",
39
+ "## Product Surface",
40
+ "- TODO",
41
+ "",
42
+ "## Visual Direction",
43
+ "- TODO",
44
+ "",
45
+ "## Visual Assist",
46
+ "- Preferred adapters:",
47
+ "- Fallback:",
48
+ " - native",
49
+ "",
50
+ "## Source Of Truth",
51
+ "- requirements",
52
+ "- project-local Pencil source",
53
+ ""
54
+ ].join("\n"),
55
+ ".da-vinci/project-inventory.md": [
56
+ "# Project Inventory",
57
+ "",
58
+ "## Current Product",
59
+ "- TODO",
60
+ "",
61
+ "## Routes And Pages",
62
+ "- TODO",
63
+ ""
64
+ ].join("\n"),
65
+ ".da-vinci/page-map.md": [
66
+ "# Page Map",
67
+ "",
68
+ "## Canonical Pages",
69
+ "- TODO",
70
+ "",
71
+ "## Shared Regions",
72
+ "- TODO",
73
+ ""
74
+ ].join("\n"),
75
+ ".da-vinci/design-registry.md": [
76
+ "# Design Registry",
77
+ "",
78
+ "## Preferred Project-local Source",
79
+ `- Preferred .pen: ${preferredPenRelativePath}`,
80
+ ""
81
+ ].join("\n")
82
+ };
83
+ }
84
+
85
+ function buildChangeTemplates(changeId) {
86
+ const changePrefix = `.da-vinci/changes/${changeId}`;
87
+ return {
88
+ [`${changePrefix}/design-brief.md`]: [
89
+ "# Design Brief",
90
+ "",
91
+ "## Product Form Factor",
92
+ "- TODO",
93
+ "",
94
+ "## Visual Direction",
95
+ "- TODO",
96
+ ""
97
+ ].join("\n"),
98
+ [`${changePrefix}/design.md`]: [
99
+ "# Design",
100
+ "",
101
+ "## Goals",
102
+ "- TODO",
103
+ "",
104
+ "## Anchor Surfaces",
105
+ "- TODO",
106
+ ""
107
+ ].join("\n"),
108
+ [`${changePrefix}/pencil-design.md`]: [
109
+ "# Pencil Design",
110
+ "",
111
+ "## Screenshot Review Records",
112
+ "- TODO",
113
+ "",
114
+ "## Checkpoint Status",
115
+ "- Design checkpoint: `WARN`",
116
+ ""
117
+ ].join("\n"),
118
+ [`${changePrefix}/pencil-bindings.md`]: [
119
+ "# Pencil Bindings",
120
+ "",
121
+ "## Bound Pages",
122
+ "- TODO",
123
+ ""
124
+ ].join("\n")
125
+ };
126
+ }
127
+
128
+ function writeScaffoldFile(projectRoot, relativePath, content, force, result) {
129
+ const absolutePath = path.join(projectRoot, relativePath);
130
+ if (pathExists(absolutePath)) {
131
+ if (!force) {
132
+ result.skipped.push(relativePath);
133
+ return;
134
+ }
135
+ writeFileAtomic(absolutePath, content);
136
+ result.overwritten.push(relativePath);
137
+ return;
138
+ }
139
+
140
+ writeFileAtomic(absolutePath, content);
141
+ result.created.push(relativePath);
142
+ }
143
+
144
+ function bootstrapProjectArtifacts(projectPathInput, options = {}) {
145
+ const projectRoot = path.resolve(projectPathInput || process.cwd());
146
+ const changeId = validateChangeId(options.changeId);
147
+ const force = options.force === true;
148
+
149
+ fs.mkdirSync(projectRoot, { recursive: true });
150
+ fs.mkdirSync(path.join(projectRoot, ".da-vinci", "designs"), { recursive: true });
151
+ fs.mkdirSync(path.join(projectRoot, ".da-vinci", "changes"), { recursive: true });
152
+
153
+ const existingRegistryText = readTextIfExists(path.join(projectRoot, ".da-vinci", "design-registry.md"));
154
+ const registeredPenPath = extractRegisteredPenPath(projectRoot, existingRegistryText);
155
+ const preferredPenPath = registeredPenPath || path.join(projectRoot, DEFAULT_PROJECT_PEN_RELATIVE_PATH);
156
+ const preferredPenRelativePath = path.relative(projectRoot, preferredPenPath) || DEFAULT_PROJECT_PEN_RELATIVE_PATH;
157
+
158
+ const result = {
159
+ projectRoot,
160
+ changeId: changeId || null,
161
+ created: [],
162
+ overwritten: [],
163
+ skipped: [],
164
+ penPath: preferredPenPath,
165
+ penRelativePath: preferredPenRelativePath,
166
+ changeArtifactsCreated: Boolean(changeId),
167
+ registryUpdatedToPreferredPen: Boolean(registeredPenPath) || !pathExists(path.join(projectRoot, ".da-vinci", "design-registry.md"))
168
+ };
169
+
170
+ const projectTemplates = buildProjectTemplates(preferredPenRelativePath);
171
+ for (const [relativePath, content] of Object.entries(projectTemplates)) {
172
+ writeScaffoldFile(projectRoot, relativePath, content, force, result);
173
+ }
174
+
175
+ if (changeId) {
176
+ const changeTemplates = buildChangeTemplates(changeId);
177
+ for (const [relativePath, content] of Object.entries(changeTemplates)) {
178
+ writeScaffoldFile(projectRoot, relativePath, content, force, result);
179
+ }
180
+ }
181
+
182
+ const penResult = ensurePenFile({
183
+ outputPath: preferredPenPath
184
+ });
185
+ result.pen = {
186
+ path: penResult.outputPath,
187
+ relativePath: preferredPenRelativePath,
188
+ created: penResult.created,
189
+ statePath: penResult.statePath,
190
+ snapshotHash: penResult.state.snapshotHash
191
+ };
192
+
193
+ if (!registeredPenPath && pathExists(path.join(projectRoot, ".da-vinci", "design-registry.md")) && !force) {
194
+ const currentRegistryText = readTextIfExists(path.join(projectRoot, ".da-vinci", "design-registry.md"));
195
+ result.registryUpdatedToPreferredPen = currentRegistryText.includes(preferredPenRelativePath);
196
+ }
197
+
198
+ return result;
199
+ }
200
+
201
+ function formatBootstrapProjectReport(result) {
202
+ const lines = [
203
+ "Da Vinci bootstrap-project",
204
+ `Project: ${result.projectRoot}`,
205
+ `Project-local .pen: ${result.pen.relativePath} (${result.pen.created ? "created" : "verified existing"})`,
206
+ `State file: ${result.pen.statePath}`
207
+ ];
208
+
209
+ if (result.changeId) {
210
+ lines.push(`Change scaffold: .da-vinci/changes/${result.changeId}/`);
211
+ } else {
212
+ lines.push("Change scaffold: skipped (pass `--change <change-id>` to create change-level artifacts)");
213
+ }
214
+
215
+ if (result.created.length > 0) {
216
+ lines.push("", "Created:");
217
+ for (const relativePath of result.created) {
218
+ lines.push(`- ${relativePath}`);
219
+ }
220
+ }
221
+
222
+ if (result.overwritten.length > 0) {
223
+ lines.push("", "Overwritten:");
224
+ for (const relativePath of result.overwritten) {
225
+ lines.push(`- ${relativePath}`);
226
+ }
227
+ }
228
+
229
+ if (result.skipped.length > 0) {
230
+ lines.push("", "Preserved existing:");
231
+ for (const relativePath of result.skipped) {
232
+ lines.push(`- ${relativePath}`);
233
+ }
234
+ }
235
+
236
+ lines.push("", "Next steps:");
237
+ if (!result.changeId) {
238
+ lines.push(`- scaffold change artifacts with: da-vinci bootstrap-project --project ${result.projectRoot} --change <change-id>`);
239
+ }
240
+ lines.push(`- begin a Pencil session with: da-vinci pencil-session begin --project ${result.projectRoot} --pen ${result.pen.path}`);
241
+ lines.push(`- after the first persisted write, run: da-vinci audit --mode integrity ${result.projectRoot}`);
242
+ if (!result.registryUpdatedToPreferredPen) {
243
+ lines.push(
244
+ `- update .da-vinci/design-registry.md to reference ${result.pen.relativePath}, or rerun bootstrap-project with --force`
245
+ );
246
+ }
247
+
248
+ return lines.join("\n");
249
+ }
250
+
251
+ module.exports = {
252
+ DEFAULT_PROJECT_PEN_RELATIVE_PATH,
253
+ bootstrapProjectArtifacts,
254
+ formatBootstrapProjectReport
255
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenonbyte/da-vinci-workflow",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "Requirement-to-design-to-code workflow skill for Codex, Claude, and Gemini",
5
5
  "bin": {
6
6
  "da-vinci": "bin/da-vinci.js",
@@ -18,14 +18,19 @@
18
18
  "examples",
19
19
  "bin",
20
20
  "lib",
21
- "scripts"
21
+ "scripts/postinstall.js",
22
+ "scripts/validate-assets.js"
22
23
  ],
23
24
  "scripts": {
24
25
  "postinstall": "node scripts/postinstall.js",
25
26
  "validate-assets": "node scripts/validate-assets.js",
27
+ "test": "node scripts/run-tests.js",
28
+ "test:audit-parsers": "node scripts/test-audit-parsers.js",
26
29
  "test:audit-safety": "node scripts/test-audit-safety.js",
27
30
  "test:audit-context-delta": "node scripts/test-audit-context-delta.js",
28
31
  "test:audit-design-supervisor": "node scripts/test-audit-design-supervisor.js",
32
+ "test:bootstrap-project": "node scripts/test-bootstrap-project.js",
33
+ "test:doc-release-alignment": "node scripts/test-doc-release-alignment.js",
29
34
  "test:pencil-lock": "node scripts/test-pencil-lock.js",
30
35
  "test:mode-consistency": "node scripts/test-mode-consistency.js",
31
36
  "test:mcp-runtime-gate": "node scripts/test-mcp-runtime-gate.js",
@@ -37,7 +42,12 @@
37
42
  "test:icon-sync": "node scripts/test-icon-sync.js",
38
43
  "test:icon-aliases": "node scripts/test-icon-aliases.js",
39
44
  "test:supervisor-review-cli": "node scripts/test-supervisor-review-cli.js",
40
- "test:supervisor-review-integration": "node scripts/test-supervisor-review-integration.js"
45
+ "test:supervisor-review-integration": "node scripts/test-supervisor-review-integration.js",
46
+ "test:install": "node scripts/test-install.js",
47
+ "test:package-contents": "node scripts/test-package-contents.js",
48
+ "quality:ci:core": "npm run test",
49
+ "quality:reviewer-bridge-smoke": "npm run test:supervisor-review-integration",
50
+ "quality:ci": "npm run quality:ci:core"
41
51
  },
42
52
  "engines": {
43
53
  "node": ">=18"
@@ -569,6 +569,7 @@ Use this structure:
569
569
  - Revision outcome
570
570
  - Whether broad expansion is approved
571
571
  - Whether implementation-task handoff is approved
572
+ - Keep one canonical structured supervisor-review section; avoid ad-hoc headings such as `## Design-Supervisor Review (Round X Attempt)`
572
573
 
573
574
  ## Anchor Surfaces
574
575
  - Which 1-3 anchor screens were designed first
@@ -199,6 +199,7 @@ Check:
199
199
  - the active editor is not still an unnamed live document such as `new`
200
200
  - the workflow did not keep using an empty `filePath` after a registered `.pen` existed
201
201
  - the active editor, registered project-local `.pen` path, and shell-visible `.pen` file are converged strongly enough to trust the runtime source
202
+ - when external or secondary `.pen` sources exist, their hashes have been reconciled against the project-local `.pen` baseline before new edits continue
202
203
  - completion-stage runtime evidence includes an explicit live-to-disk sync verification for the registered `.pen`
203
204
  - claimed anchor ids exist in the active live editor
204
205
  - claimed reviewed screens and screenshot targets exist in the active live editor
@@ -231,6 +232,7 @@ Check:
231
232
  - the preferred `.pen` path in `design-registry.md` is workflow-owned and specific, not hand-wavy
232
233
  - the active Pencil editor path matches the preferred project-local `.pen` path, or the project-local file has been reconstructed explicitly
233
234
  - the preferred project-local `.pen` file exists as a shell-visible file when the workflow created or edited Pencil work
235
+ - if external or secondary `.pen` files exist, the workflow records baseline hash alignment and explicit source-priority confirmation before the next edit round
234
236
  - exported screenshots are treated only as review artifacts, not as page-to-design source of truth
235
237
  - each implementation page has a Pencil page or an explicit exception
236
238
  - shared layouts and shared regions are bound clearly enough to implement from
@@ -20,6 +20,7 @@ The preferred `.pen` path in `design-registry.md` is workflow-owned state.
20
20
  - it should be generated and maintained by Da Vinci
21
21
  - it should not rely on the user manually typing a path into `design-registry.md`
22
22
  - external references may influence source priority, but the project-local path should still be resolved explicitly
23
+ - in multi-source scenarios, a successful `pencil-session persist` confirms only live-vs-project sync for that run; verify cross-source hash alignment before treating the project file as globally latest
23
24
 
24
25
  Use these defaults:
25
26
 
@@ -32,6 +33,7 @@ Before broad Pencil work begins:
32
33
  - resolve the exact project-local path
33
34
  - record that path in `design-registry.md`
34
35
  - treat that path as the required target for the active design pass
36
+ - if external or secondary `.pen` sources exist, run `da-vinci check-pen-baseline --pen <project-pen> --baseline <other-pen>`; when hashes diverge, confirm source priority and sync the chosen source into `<project-pen>` before new edits
35
37
 
36
38
  If Pencil MCP is currently pointing at a different active editor:
37
39
 
@@ -75,6 +75,7 @@ When generating or editing Pencil data:
75
75
  - if unsupported-property rollbacks repeat on the same anchor surface, stop treating that pass as stable forward progress until the schema usage is corrected
76
76
  - after any rolled-back batch or structure-changing edit, refresh the live node structure before descendant-targeted follow-up operations
77
77
  - before the first Pencil edit on a redesign pass, require `da-vinci pencil-session begin --project <project-path> --pen <path>` whenever the session wrapper is available so the registered project-local `.pen` exists before editing and the global lock is held; use `da-vinci ensure-pen --output <path> --verify-open` only as a lower-level fallback when the session wrapper truly cannot be used
78
+ - if external or secondary `.pen` files exist, run `da-vinci check-pen-baseline --pen <project-pen> --baseline <other-pen>` before the new write phase; if hashes diverge, confirm source priority and sync the chosen source into `<project-pen>` before new edits
78
79
  - acquire the global Pencil lock before MCP write operations when multiple redesign sessions could overlap on the same machine
79
80
  - on autonomous runs, require the higher-level wrapper:
80
81
  `da-vinci pencil-session begin`
@@ -85,6 +86,7 @@ When generating or editing Pencil data:
85
86
  - if a registered project-local `.pen` already existed, reopen it for continuity, but after material live edits persist a fresh MCP snapshot back to that same path instead of assuming live edits were flushed automatically
86
87
  - use `da-vinci write-pen --output <path> --nodes-file <batch-get-json> --variables-file <get-variables-json> --version <version> --verify-open` when you already have MCP-readable snapshot payloads and need an atomic project-local `.pen` write
87
88
  - run `da-vinci check-pen-sync --pen <path> --nodes-file <batch-get-json> --variables-file <get-variables-json> --version <version>` after material live edits and before completion claims to prove the live snapshot matches the persisted `.pen`
89
+ - when an external source is selected as latest, use `da-vinci sync-pen-source --from <preferred-source.pen> --to <project-pen>` to materialize it into the project-local baseline before reopening a write session
88
90
  - use `da-vinci snapshot-pen --input <path> --output <path> --verify-open` only as a disk-to-disk utility when you need to re-canonicalize an existing `.pen`; it is not a substitute for persisting the current live editor
89
91
  - completion audit expects `.da-vinci/state/pencil-session.json` to exist and reflect the latest persisted `.pen` hash
90
92
  - keep workflow markdown out of `.da-vinci/designs/`; reserve that directory for `.pen` files only
@@ -1,295 +0,0 @@
1
- {
2
- "version": "2.9",
3
- "variables": {
4
- "surface-bg": {
5
- "type": "color",
6
- "value": "#F4EFE7"
7
- },
8
- "surface-panel": {
9
- "type": "color",
10
- "value": "#FFFDF8"
11
- },
12
- "surface-border": {
13
- "type": "color",
14
- "value": "#D7CCBC"
15
- },
16
- "deck-fill": {
17
- "type": "color",
18
- "value": "#17304A"
19
- },
20
- "deck-fill-deep": {
21
- "type": "color",
22
- "value": "#10263A"
23
- },
24
- "surface-ink": {
25
- "type": "color",
26
- "value": "#132033"
27
- },
28
- "surface-muted": {
29
- "type": "color",
30
- "value": "#5D6673"
31
- },
32
- "accent-amber": {
33
- "type": "color",
34
- "value": "#C98A2B"
35
- }
36
- },
37
- "children": [
38
- {
39
- "type": "frame",
40
- "id": "healthy",
41
- "x": 0,
42
- "y": 0,
43
- "name": "Diagnostics Console / Healthy",
44
- "width": 420,
45
- "fill": "$surface-bg",
46
- "layout": "vertical",
47
- "gap": 18,
48
- "padding": [
49
- 18,
50
- 18,
51
- 24,
52
- 18
53
- ],
54
- "children": [
55
- {
56
- "type": "frame",
57
- "id": "deckHealthy",
58
- "name": "Command Deck",
59
- "width": "fill_container",
60
- "fill": [
61
- {
62
- "type": "gradient",
63
- "gradientType": "linear",
64
- "enabled": true,
65
- "rotation": 180,
66
- "size": {
67
- "height": 1
68
- },
69
- "colors": [
70
- {
71
- "color": "$deck-fill",
72
- "position": 0
73
- },
74
- {
75
- "color": "$deck-fill-deep",
76
- "position": 1
77
- }
78
- ]
79
- },
80
- "#FFFFFF12"
81
- ],
82
- "cornerRadius": 26,
83
- "effect": {
84
- "type": "shadow",
85
- "shadowType": "outer",
86
- "color": "#0B16231F",
87
- "offset": {
88
- "x": 0,
89
- "y": 14
90
- },
91
- "blur": 28
92
- },
93
- "layout": "vertical",
94
- "gap": 14,
95
- "padding": 22,
96
- "children": [
97
- {
98
- "type": "text",
99
- "id": "healthyEyebrow",
100
- "name": "heroEyebrow",
101
- "content": "Diagnostics",
102
- "fill": "$accent-amber",
103
- "fontFamily": "Inter",
104
- "fontSize": 12,
105
- "fontWeight": "700",
106
- "letterSpacing": 1.4
107
- },
108
- {
109
- "type": "text",
110
- "id": "healthyTitle",
111
- "name": "heroTitle",
112
- "content": "Healthy runway",
113
- "fill": "#FFFDF8",
114
- "fontFamily": "Inter",
115
- "fontSize": 28,
116
- "fontWeight": "700",
117
- "textGrowth": "fixed-width",
118
- "width": "fill_container"
119
- }
120
- ]
121
- },
122
- {
123
- "type": "frame",
124
- "id": "panelHealthy",
125
- "name": "Runway Panel",
126
- "width": "fill_container",
127
- "fill": "$surface-panel",
128
- "cornerRadius": 24,
129
- "stroke": {
130
- "fill": "$surface-border",
131
- "thickness": 1
132
- },
133
- "layout": "vertical",
134
- "gap": 12,
135
- "padding": 18,
136
- "children": [
137
- {
138
- "type": "text",
139
- "id": "panelHealthyTitle",
140
- "name": "panelTitle",
141
- "content": "Launch blocking scenarios",
142
- "fill": "$surface-ink",
143
- "fontFamily": "Inter",
144
- "fontSize": 24,
145
- "fontWeight": "700",
146
- "textGrowth": "fixed-width",
147
- "width": "fill_container"
148
- },
149
- {
150
- "type": "text",
151
- "id": "panelHealthyBody",
152
- "name": "panelBody",
153
- "content": "Routine and high-risk controls are separated so the dangerous path always reads clearly.",
154
- "fill": "$surface-muted",
155
- "fontFamily": "Inter",
156
- "fontSize": 14,
157
- "fontWeight": "normal",
158
- "lineHeight": 1.45,
159
- "textGrowth": "fixed-width",
160
- "width": "fill_container"
161
- }
162
- ]
163
- }
164
- ]
165
- },
166
- {
167
- "type": "frame",
168
- "id": "degraded",
169
- "x": 500,
170
- "y": 0,
171
- "name": "Diagnostics Console / Degraded",
172
- "width": 420,
173
- "fill": "$surface-bg",
174
- "layout": "vertical",
175
- "gap": 18,
176
- "padding": [
177
- 18,
178
- 18,
179
- 24,
180
- 18
181
- ],
182
- "children": [
183
- {
184
- "type": "frame",
185
- "id": "deckDegraded",
186
- "name": "Command Deck",
187
- "width": "fill_container",
188
- "fill": [
189
- {
190
- "type": "gradient",
191
- "gradientType": "linear",
192
- "enabled": true,
193
- "rotation": 180,
194
- "size": {
195
- "height": 1
196
- },
197
- "colors": [
198
- {
199
- "color": "$deck-fill",
200
- "position": 0
201
- },
202
- {
203
- "color": "$deck-fill-deep",
204
- "position": 1
205
- }
206
- ]
207
- },
208
- "#FFFFFF12"
209
- ],
210
- "cornerRadius": 26,
211
- "effect": {
212
- "type": "shadow",
213
- "shadowType": "outer",
214
- "color": "#0B16231F",
215
- "offset": {
216
- "x": 0,
217
- "y": 14
218
- },
219
- "blur": 28
220
- },
221
- "layout": "vertical",
222
- "gap": 14,
223
- "padding": 22,
224
- "children": [
225
- {
226
- "type": "text",
227
- "id": "degradedEyebrow",
228
- "name": "heroEyebrow",
229
- "content": "Diagnostics",
230
- "fill": "$accent-amber",
231
- "fontFamily": "Inter",
232
- "fontSize": 12,
233
- "fontWeight": "700",
234
- "letterSpacing": 1.4
235
- },
236
- {
237
- "type": "text",
238
- "id": "degradedTitle",
239
- "name": "heroTitle",
240
- "content": "Degraded runway",
241
- "fill": "#FFFDF8",
242
- "fontFamily": "Inter",
243
- "fontSize": 28,
244
- "fontWeight": "700",
245
- "textGrowth": "fixed-width",
246
- "width": "fill_container"
247
- }
248
- ]
249
- },
250
- {
251
- "type": "frame",
252
- "id": "panelDegraded",
253
- "name": "Runway Panel",
254
- "width": "fill_container",
255
- "fill": "$surface-panel",
256
- "cornerRadius": 24,
257
- "stroke": {
258
- "fill": "$surface-border",
259
- "thickness": 1
260
- },
261
- "layout": "vertical",
262
- "gap": 12,
263
- "padding": 18,
264
- "children": [
265
- {
266
- "type": "text",
267
- "id": "panelDegradedTitle",
268
- "name": "panelTitle",
269
- "content": "Signal drift detected",
270
- "fill": "$surface-ink",
271
- "fontFamily": "Inter",
272
- "fontSize": 24,
273
- "fontWeight": "700",
274
- "textGrowth": "fixed-width",
275
- "width": "fill_container"
276
- },
277
- {
278
- "type": "text",
279
- "id": "panelDegradedBody",
280
- "name": "panelBody",
281
- "content": "Telemetry stays legible while the top surface shifts the operator toward the corrective lane.",
282
- "fill": "$surface-muted",
283
- "fontFamily": "Inter",
284
- "fontSize": 14,
285
- "fontWeight": "normal",
286
- "lineHeight": 1.45,
287
- "textGrowth": "fixed-width",
288
- "width": "fill_container"
289
- }
290
- ]
291
- }
292
- ]
293
- }
294
- ]
295
- }