devrites 4.3.0 → 4.4.1

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 (79) hide show
  1. package/CHANGELOG.md +12 -494
  2. package/README.md +4 -4
  3. package/docs/cli.md +22 -7
  4. package/docs/command-map.md +1 -1
  5. package/docs/engine/commands.md +24 -9
  6. package/docs/engine/workspace-schema.md +5 -5
  7. package/engine/commands.go +1 -1
  8. package/engine/internal/gate/gate.go +9 -0
  9. package/engine/internal/gate/gate_test.go +110 -0
  10. package/engine/internal/gate/readiness_binding.go +9 -0
  11. package/engine/internal/gate/readiness_binding_test.go +2 -2
  12. package/engine/internal/lib/observe_summary.go +22 -22
  13. package/engine/internal/lib/open_visual.go +254 -0
  14. package/engine/internal/lib/open_visual_test.go +280 -0
  15. package/engine/internal/lib/taskgraph.go +4 -143
  16. package/engine/internal/lib/taskgraph_test.go +203 -0
  17. package/engine/internal/lib/visual_outline.go +163 -0
  18. package/engine/internal/lib/visual_outline_test.go +161 -0
  19. package/engine/internal/state/taskgraph.go +213 -0
  20. package/engine/internal/testutil/testutil.go +3 -0
  21. package/engine/main.go +6 -2
  22. package/engine/root_routing_test.go +49 -14
  23. package/engine/testdata/visual/open-visual-smoke.html +675 -0
  24. package/engine/testdata/visual/open-visual-smoke.outline.md +54 -0
  25. package/engine/tests/adr_0027_readiness_binding_test.go +1 -1
  26. package/engine/tests/gate_test.go +4 -2
  27. package/pack/.claude/skills/devrites-lib/SKILL.md +2 -0
  28. package/pack/.claude/skills/devrites-lib/reference/visual-playbooks/code.md +46 -0
  29. package/pack/.claude/skills/devrites-lib/reference/visual-playbooks/comparison.md +44 -0
  30. package/pack/.claude/skills/devrites-lib/reference/visual-playbooks/diagram.md +53 -0
  31. package/pack/.claude/skills/devrites-lib/reference/visual-playbooks/index.md +57 -0
  32. package/pack/.claude/skills/devrites-lib/reference/visual-playbooks/input.md +47 -0
  33. package/pack/.claude/skills/devrites-lib/reference/visual-playbooks/outline-template.md +106 -0
  34. package/pack/.claude/skills/devrites-lib/reference/visual-playbooks/plan.md +49 -0
  35. package/pack/.claude/skills/devrites-lib/reference/visual-playbooks/slides.md +42 -0
  36. package/pack/.claude/skills/devrites-lib/reference/visual-playbooks/table.md +44 -0
  37. package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +24 -9
  38. package/pack/.claude/skills/rite-define/SKILL.md +10 -1
  39. package/pack/.claude/skills/rite-explain/SKILL.md +23 -7
  40. package/pack/.claude/skills/rite-explain/reference/intake.md +11 -2
  41. package/pack/.claude/skills/rite-plan/reference/dependency-graph.md +4 -1
  42. package/pack/.claude/skills/rite-spec/reference/spec-template.md +5 -2
  43. package/pack/.claude/skills/rite-spec/reference/state-workspace.md +26 -3
  44. package/pack/generated/claude/skills/devrites-lib/SKILL.md +2 -0
  45. package/pack/generated/claude/skills/devrites-lib/reference/visual-playbooks/code.md +46 -0
  46. package/pack/generated/claude/skills/devrites-lib/reference/visual-playbooks/comparison.md +44 -0
  47. package/pack/generated/claude/skills/devrites-lib/reference/visual-playbooks/diagram.md +53 -0
  48. package/pack/generated/claude/skills/devrites-lib/reference/visual-playbooks/index.md +57 -0
  49. package/pack/generated/claude/skills/devrites-lib/reference/visual-playbooks/input.md +47 -0
  50. package/pack/generated/claude/skills/devrites-lib/reference/visual-playbooks/outline-template.md +106 -0
  51. package/pack/generated/claude/skills/devrites-lib/reference/visual-playbooks/plan.md +49 -0
  52. package/pack/generated/claude/skills/devrites-lib/reference/visual-playbooks/slides.md +42 -0
  53. package/pack/generated/claude/skills/devrites-lib/reference/visual-playbooks/table.md +44 -0
  54. package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +24 -9
  55. package/pack/generated/claude/skills/rite-define/SKILL.md +10 -1
  56. package/pack/generated/claude/skills/rite-explain/SKILL.md +23 -7
  57. package/pack/generated/claude/skills/rite-explain/reference/intake.md +11 -2
  58. package/pack/generated/claude/skills/rite-plan/reference/dependency-graph.md +4 -1
  59. package/pack/generated/claude/skills/rite-spec/reference/spec-template.md +5 -2
  60. package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +26 -3
  61. package/pack/generated/codex/skills/devrites-lib/SKILL.md +2 -0
  62. package/pack/generated/codex/skills/devrites-lib/reference/visual-playbooks/code.md +46 -0
  63. package/pack/generated/codex/skills/devrites-lib/reference/visual-playbooks/comparison.md +44 -0
  64. package/pack/generated/codex/skills/devrites-lib/reference/visual-playbooks/diagram.md +53 -0
  65. package/pack/generated/codex/skills/devrites-lib/reference/visual-playbooks/index.md +57 -0
  66. package/pack/generated/codex/skills/devrites-lib/reference/visual-playbooks/input.md +47 -0
  67. package/pack/generated/codex/skills/devrites-lib/reference/visual-playbooks/outline-template.md +106 -0
  68. package/pack/generated/codex/skills/devrites-lib/reference/visual-playbooks/plan.md +49 -0
  69. package/pack/generated/codex/skills/devrites-lib/reference/visual-playbooks/slides.md +42 -0
  70. package/pack/generated/codex/skills/devrites-lib/reference/visual-playbooks/table.md +44 -0
  71. package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +24 -9
  72. package/pack/generated/codex/skills/rite-define/SKILL.md +10 -1
  73. package/pack/generated/codex/skills/rite-explain/SKILL.md +23 -7
  74. package/pack/generated/codex/skills/rite-explain/reference/intake.md +11 -2
  75. package/pack/generated/codex/skills/rite-plan/reference/dependency-graph.md +4 -1
  76. package/pack/generated/codex/skills/rite-spec/reference/spec-template.md +5 -2
  77. package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +26 -3
  78. package/package.json +1 -1
  79. package/scripts/install-lib.sh +1 -1
@@ -0,0 +1,675 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>open-visual — dual-read smoke</title>
7
+ <style>
8
+ :root {
9
+ color-scheme: light dark;
10
+ /* Restrained tool surface: near-neutral off-white tinted toward teal (not cream/sand). */
11
+ --bg: oklch(0.985 0.006 195);
12
+ --surface: oklch(0.995 0.003 195);
13
+ --surface-2: oklch(0.965 0.01 195);
14
+ --ink: oklch(0.28 0.02 250);
15
+ --muted: oklch(0.45 0.02 250);
16
+ --accent: oklch(0.48 0.1 195);
17
+ --accent-soft: oklch(0.94 0.03 195);
18
+ --line: oklch(0.86 0.015 195);
19
+ --ok: oklch(0.52 0.12 155);
20
+ --warn: oklch(0.55 0.12 75);
21
+ --shadow: 0 1px 0 oklch(0.3 0.02 250 / 0.04), 0 8px 24px oklch(0.3 0.02 250 / 0.06);
22
+ --radius: 12px;
23
+ --space-1: 0.5rem;
24
+ --space-2: 0.75rem;
25
+ --space-3: 1rem;
26
+ --space-4: 1.5rem;
27
+ --space-5: 2rem;
28
+ --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
29
+ --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
30
+ --z-sticky: 10;
31
+ }
32
+
33
+ @media (prefers-color-scheme: dark) {
34
+ :root {
35
+ --bg: oklch(0.22 0.015 250);
36
+ --surface: oklch(0.27 0.015 250);
37
+ --surface-2: oklch(0.32 0.018 250);
38
+ --ink: oklch(0.93 0.01 195);
39
+ --muted: oklch(0.72 0.02 250);
40
+ --accent: oklch(0.78 0.1 185);
41
+ --accent-soft: oklch(0.35 0.04 195);
42
+ --line: oklch(0.42 0.02 250);
43
+ --ok: oklch(0.78 0.12 155);
44
+ --warn: oklch(0.82 0.12 85);
45
+ --shadow: 0 1px 0 oklch(0 0 0 / 0.35), 0 10px 28px oklch(0 0 0 / 0.35);
46
+ }
47
+ }
48
+
49
+ * { box-sizing: border-box; }
50
+
51
+ body {
52
+ margin: 0;
53
+ min-height: 100vh;
54
+ font-family: var(--font);
55
+ font-size: 1rem;
56
+ line-height: 1.5;
57
+ background:
58
+ radial-gradient(1200px 480px at 10% -10%, oklch(from var(--accent) l c h / 0.08), transparent 60%),
59
+ var(--bg);
60
+ color: var(--ink);
61
+ }
62
+
63
+ a { color: var(--accent); }
64
+
65
+ code, kbd {
66
+ font-family: var(--mono);
67
+ font-size: 0.875em;
68
+ background: var(--surface-2);
69
+ border: 1px solid var(--line);
70
+ border-radius: 6px;
71
+ padding: 0.1em 0.35em;
72
+ }
73
+
74
+ kbd { font-size: 0.8em; }
75
+
76
+ .wrap {
77
+ width: min(52rem, calc(100% - 2rem));
78
+ margin-inline: auto;
79
+ padding-block: var(--space-4) var(--space-5);
80
+ }
81
+
82
+ header#viz-title {
83
+ display: grid;
84
+ gap: var(--space-2);
85
+ padding: var(--space-4);
86
+ margin-bottom: var(--space-4);
87
+ background: var(--surface);
88
+ border: 1px solid var(--line);
89
+ border-radius: calc(var(--radius) + 2px);
90
+ box-shadow: var(--shadow);
91
+ }
92
+
93
+ .badge-row {
94
+ display: flex;
95
+ flex-wrap: wrap;
96
+ gap: var(--space-1);
97
+ }
98
+
99
+ .badge {
100
+ display: inline-flex;
101
+ align-items: center;
102
+ gap: 0.35rem;
103
+ padding: 0.2rem 0.55rem;
104
+ border-radius: 999px;
105
+ border: 1px solid var(--line);
106
+ background: var(--surface-2);
107
+ color: var(--muted);
108
+ font-size: 0.75rem;
109
+ font-weight: 600;
110
+ letter-spacing: 0.01em;
111
+ }
112
+
113
+ .badge[data-tone="accent"] {
114
+ background: var(--accent-soft);
115
+ border-color: color-mix(in oklch, var(--accent) 35%, var(--line));
116
+ color: var(--accent);
117
+ }
118
+
119
+ .badge[data-tone="ok"] {
120
+ color: var(--ok);
121
+ border-color: color-mix(in oklch, var(--ok) 35%, var(--line));
122
+ }
123
+
124
+ .badge[data-tone="warn"] {
125
+ color: var(--warn);
126
+ border-color: color-mix(in oklch, var(--warn) 35%, var(--line));
127
+ }
128
+
129
+ .badge[data-tone="low"] {
130
+ color: var(--muted);
131
+ border-color: var(--line);
132
+ }
133
+
134
+ h1 {
135
+ margin: 0;
136
+ font-size: 1.5rem;
137
+ font-weight: 700;
138
+ letter-spacing: -0.02em;
139
+ text-wrap: balance;
140
+ line-height: 1.25;
141
+ }
142
+
143
+ .lede {
144
+ margin: 0;
145
+ max-width: 65ch;
146
+ color: var(--muted);
147
+ text-wrap: pretty;
148
+ }
149
+
150
+ .cmd {
151
+ display: flex;
152
+ flex-wrap: wrap;
153
+ align-items: center;
154
+ gap: var(--space-2);
155
+ margin: 0;
156
+ padding: var(--space-2) var(--space-3);
157
+ background: var(--surface-2);
158
+ border: 1px dashed color-mix(in oklch, var(--accent) 40%, var(--line));
159
+ border-radius: var(--radius);
160
+ font-family: var(--mono);
161
+ font-size: 0.85rem;
162
+ overflow-x: auto;
163
+ }
164
+
165
+ .cmd span.label {
166
+ flex: 0 0 auto;
167
+ font-family: var(--font);
168
+ font-size: 0.7rem;
169
+ font-weight: 700;
170
+ text-transform: uppercase;
171
+ letter-spacing: 0.04em;
172
+ color: var(--accent);
173
+ }
174
+
175
+ main {
176
+ display: grid;
177
+ gap: var(--space-4);
178
+ }
179
+
180
+ section {
181
+ background: var(--surface);
182
+ border: 1px solid var(--line);
183
+ border-radius: var(--radius);
184
+ padding: var(--space-3) var(--space-4);
185
+ box-shadow: var(--shadow);
186
+ }
187
+
188
+ .section-head {
189
+ display: flex;
190
+ flex-wrap: wrap;
191
+ align-items: baseline;
192
+ justify-content: space-between;
193
+ gap: var(--space-2);
194
+ margin-bottom: var(--space-3);
195
+ }
196
+
197
+ .section-head h2 {
198
+ margin: 0;
199
+ font-size: 0.95rem;
200
+ font-weight: 700;
201
+ letter-spacing: -0.01em;
202
+ }
203
+
204
+ .section-head p {
205
+ margin: 0;
206
+ color: var(--muted);
207
+ font-size: 0.875rem;
208
+ max-width: 42ch;
209
+ }
210
+
211
+ #diagram-overview {
212
+ padding-bottom: var(--space-2);
213
+ }
214
+
215
+ .figure-frame {
216
+ border: 1px solid var(--line);
217
+ border-radius: var(--radius);
218
+ background:
219
+ linear-gradient(180deg, var(--accent-soft), transparent 42%),
220
+ var(--surface-2);
221
+ padding: var(--space-3);
222
+ }
223
+
224
+ svg {
225
+ display: block;
226
+ width: 100%;
227
+ height: auto;
228
+ color: var(--ink);
229
+ }
230
+
231
+ .node rect {
232
+ fill: var(--surface);
233
+ stroke: var(--accent);
234
+ stroke-width: 2;
235
+ filter: drop-shadow(0 2px 6px oklch(0.3 0.02 250 / 0.08));
236
+ }
237
+
238
+ .node text.primary {
239
+ fill: var(--ink);
240
+ font-size: 15px;
241
+ font-weight: 700;
242
+ font-family: var(--font);
243
+ }
244
+
245
+ .node text.secondary {
246
+ fill: var(--muted);
247
+ font-size: 11px;
248
+ font-family: var(--mono);
249
+ }
250
+
251
+ .edge {
252
+ stroke: color-mix(in oklch, var(--accent) 55%, var(--line));
253
+ stroke-width: 2.25;
254
+ fill: none;
255
+ marker-end: url(#arrow);
256
+ }
257
+
258
+ .edge-label {
259
+ fill: var(--muted);
260
+ font-size: 11px;
261
+ font-weight: 600;
262
+ font-family: var(--font);
263
+ letter-spacing: 0.02em;
264
+ }
265
+
266
+ .step-dot {
267
+ fill: var(--accent);
268
+ }
269
+
270
+ .step-dot-text {
271
+ fill: oklch(0.99 0 0);
272
+ font-size: 11px;
273
+ font-weight: 700;
274
+ font-family: var(--font);
275
+ }
276
+
277
+ @media (prefers-color-scheme: dark) {
278
+ .step-dot-text { fill: oklch(0.2 0.02 250); }
279
+ .node rect {
280
+ filter: drop-shadow(0 2px 8px oklch(0 0 0 / 0.35));
281
+ }
282
+ }
283
+
284
+ #diagram-detail .steps {
285
+ display: grid;
286
+ gap: var(--space-3);
287
+ grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
288
+ margin: 0;
289
+ padding: 0;
290
+ list-style: none;
291
+ }
292
+
293
+ #diagram-detail .steps li {
294
+ display: grid;
295
+ gap: 0.35rem;
296
+ padding: var(--space-3);
297
+ border-radius: calc(var(--radius) - 2px);
298
+ background: var(--surface-2);
299
+ border: 1px solid var(--line);
300
+ }
301
+
302
+ #diagram-detail .steps .n {
303
+ width: 1.5rem;
304
+ height: 1.5rem;
305
+ display: grid;
306
+ place-items: center;
307
+ border-radius: 999px;
308
+ background: var(--accent);
309
+ color: oklch(0.99 0 0);
310
+ font-size: 0.75rem;
311
+ font-weight: 700;
312
+ }
313
+
314
+ @media (prefers-color-scheme: dark) {
315
+ #diagram-detail .steps .n { color: oklch(0.2 0.02 250); }
316
+ }
317
+
318
+ #diagram-detail .steps h3 {
319
+ margin: 0;
320
+ font-size: 0.95rem;
321
+ font-weight: 700;
322
+ text-wrap: balance;
323
+ }
324
+
325
+ #diagram-detail .steps p {
326
+ margin: 0;
327
+ color: var(--muted);
328
+ font-size: 0.875rem;
329
+ text-wrap: pretty;
330
+ }
331
+
332
+ #diagram-legend dl {
333
+ display: grid;
334
+ gap: var(--space-2);
335
+ margin: 0;
336
+ }
337
+
338
+ #diagram-legend .row {
339
+ display: grid;
340
+ grid-template-columns: minmax(7rem, 10rem) 1fr;
341
+ gap: var(--space-2);
342
+ align-items: start;
343
+ padding-block: 0.35rem;
344
+ border-bottom: 1px solid var(--line);
345
+ }
346
+
347
+ #diagram-legend .row:last-child { border-bottom: 0; }
348
+
349
+ #diagram-legend dt {
350
+ margin: 0;
351
+ font-weight: 700;
352
+ font-size: 0.85rem;
353
+ }
354
+
355
+ #diagram-legend dd {
356
+ margin: 0;
357
+ color: var(--muted);
358
+ font-size: 0.875rem;
359
+ text-wrap: pretty;
360
+ }
361
+
362
+ #diagram-citations table,
363
+ #viz-open-questions table {
364
+ width: 100%;
365
+ border-collapse: collapse;
366
+ font-size: 0.875rem;
367
+ }
368
+
369
+ #diagram-citations th,
370
+ #diagram-citations td,
371
+ #viz-open-questions th,
372
+ #viz-open-questions td {
373
+ text-align: left;
374
+ vertical-align: top;
375
+ padding: 0.55rem 0.35rem;
376
+ border-bottom: 1px solid var(--line);
377
+ }
378
+
379
+ #diagram-citations th,
380
+ #viz-open-questions th {
381
+ color: var(--muted);
382
+ font-size: 0.72rem;
383
+ font-weight: 700;
384
+ text-transform: uppercase;
385
+ letter-spacing: 0.04em;
386
+ }
387
+
388
+ #diagram-citations td:first-child {
389
+ width: 38%;
390
+ font-weight: 600;
391
+ }
392
+
393
+ #viz-open-questions td:first-child {
394
+ width: 7.5rem;
395
+ font-family: var(--mono);
396
+ font-size: 0.8rem;
397
+ font-weight: 600;
398
+ }
399
+
400
+ #viz-open-questions .q-meta {
401
+ display: flex;
402
+ flex-wrap: wrap;
403
+ gap: var(--space-1);
404
+ margin-top: 0.35rem;
405
+ }
406
+
407
+ footer.note {
408
+ margin-top: var(--space-2);
409
+ color: var(--muted);
410
+ font-size: 0.8rem;
411
+ }
412
+
413
+ @media (max-width: 560px) {
414
+ .wrap { width: min(52rem, calc(100% - 1.25rem)); }
415
+ header#viz-title, section { padding: var(--space-3); }
416
+ #diagram-legend .row { grid-template-columns: 1fr; gap: 0.15rem; }
417
+ h1 { font-size: 1.25rem; }
418
+ }
419
+
420
+ @media (prefers-reduced-motion: reduce) {
421
+ * {
422
+ transition: none !important;
423
+ animation: none !important;
424
+ }
425
+ }
426
+ </style>
427
+ </head>
428
+ <body>
429
+ <div class="wrap">
430
+ <header id="viz-title">
431
+ <div class="badge-row">
432
+ <span class="badge" data-tone="accent">diagram playbook</span>
433
+ <span class="badge" data-tone="ok">local only · no network</span>
434
+ <span class="badge">smoke fixture</span>
435
+ </div>
436
+ <h1>How does <code>open-visual</code> resolve a dual-read visual?</h1>
437
+ <p class="lede">
438
+ Agents write a portable HTML figure plus a sibling Markdown outline.
439
+ Humans review the HTML; agents trust the outline as SSOT. The engine
440
+ opener resolves that pair locally and can print tips without launching a browser.
441
+ </p>
442
+ <p class="cmd" aria-label="Smoke command">
443
+ <span class="label">try</span>
444
+ <span>devrites-engine open-visual engine/testdata/visual/open-visual-smoke.html --no-open</span>
445
+ </p>
446
+ </header>
447
+
448
+ <main>
449
+ <section id="diagram-overview" aria-label="Overview diagram">
450
+ <div class="section-head">
451
+ <h2>Overview</h2>
452
+ <p>One concept: HTML + outline → opener tips.</p>
453
+ </div>
454
+ <div class="figure-frame">
455
+ <svg viewBox="0 0 420 420" role="img" aria-labelledby="diagram-title">
456
+ <title id="diagram-title">HTML fixture pairs with outline SSOT; open-visual prints tips locally</title>
457
+ <defs>
458
+ <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
459
+ <path d="M 0 0 L 10 5 L 0 10 z" fill="currentColor"></path>
460
+ </marker>
461
+ </defs>
462
+
463
+ <g class="node" id="node-html">
464
+ <circle class="step-dot" cx="48" cy="54" r="12"></circle>
465
+ <text class="step-dot-text" x="48" y="58" text-anchor="middle">1</text>
466
+ <rect x="78" y="24" width="300" height="64" rx="12"></rect>
467
+ <text class="primary" x="228" y="50" text-anchor="middle">HTML visual</text>
468
+ <text class="secondary" x="228" y="70" text-anchor="middle">*.html · human review surface</text>
469
+ </g>
470
+
471
+ <path class="edge" id="edge-html-outline" d="M 228 88 V 118"></path>
472
+ <text class="edge-label" x="246" y="110">dual-read pair</text>
473
+
474
+ <g class="node" id="node-outline">
475
+ <circle class="step-dot" cx="48" cy="166" r="12"></circle>
476
+ <text class="step-dot-text" x="48" y="170" text-anchor="middle">2</text>
477
+ <rect x="78" y="136" width="300" height="64" rx="12"></rect>
478
+ <text class="primary" x="228" y="162" text-anchor="middle">Outline SSOT</text>
479
+ <text class="secondary" x="228" y="182" text-anchor="middle">*.outline.md · agent truth</text>
480
+ </g>
481
+
482
+ <path class="edge" id="edge-outline-opener" d="M 228 200 V 230"></path>
483
+ <text class="edge-label" x="246" y="222">tips + resolved path</text>
484
+
485
+ <g class="node" id="node-opener">
486
+ <circle class="step-dot" cx="48" cy="278" r="12"></circle>
487
+ <text class="step-dot-text" x="48" y="282" text-anchor="middle">3</text>
488
+ <rect x="78" y="248" width="300" height="64" rx="12"></rect>
489
+ <text class="primary" x="228" y="274" text-anchor="middle">open-visual</text>
490
+ <text class="secondary" x="228" y="294" text-anchor="middle">engine command · local only</text>
491
+ </g>
492
+
493
+ <text class="edge-label" x="210" y="350" text-anchor="middle">On conflict, outline wins until both regenerate</text>
494
+ <text class="edge-label" x="210" y="372" text-anchor="middle">--no-open prints tips · never launches the browser</text>
495
+ </svg>
496
+ </div>
497
+ </section>
498
+
499
+ <section id="diagram-detail" aria-label="How the opener behaves">
500
+ <div class="section-head">
501
+ <h2>What each step does</h2>
502
+ <p>Same story as the figure, in readable prose.</p>
503
+ </div>
504
+ <ol class="steps">
505
+ <li>
506
+ <span class="n" aria-hidden="true">1</span>
507
+ <h3>Ship a dual-read pair</h3>
508
+ <p>
509
+ Write <code>visual/&lt;name&gt;.html</code> for humans and
510
+ <code>visual/&lt;name&gt;.outline.md</code> for agents — same claim, two surfaces.
511
+ </p>
512
+ </li>
513
+ <li>
514
+ <span class="n" aria-hidden="true">2</span>
515
+ <h3>Treat outline as SSOT</h3>
516
+ <p>
517
+ Inventory, relationships, and citations live in the outline.
518
+ If HTML and outline disagree, the outline wins.
519
+ </p>
520
+ </li>
521
+ <li>
522
+ <span class="n" aria-hidden="true">3</span>
523
+ <h3>Resolve with <code>open-visual</code></h3>
524
+ <p>
525
+ The engine finds the HTML (path or workspace name), warns if the
526
+ sibling outline is missing, prints agent tips, and optionally opens the OS browser.
527
+ </p>
528
+ </li>
529
+ </ol>
530
+ </section>
531
+
532
+ <section id="diagram-legend" aria-label="Legend">
533
+ <div class="section-head">
534
+ <h2>Legend</h2>
535
+ <p>Symbols and guarantees for this fixture.</p>
536
+ </div>
537
+ <dl>
538
+ <div class="row">
539
+ <dt>Arrow</dt>
540
+ <dd>Local resolve / tip print only — no fetch, CDN, or remote I/O.</dd>
541
+ </div>
542
+ <div class="row">
543
+ <dt>Outline SSOT</dt>
544
+ <dd>Agent-facing source of truth for ids, relationships, and citations.</dd>
545
+ </div>
546
+ <div class="row">
547
+ <dt><kbd>--no-open</kbd></dt>
548
+ <dd>Smoke path: resolve + tips print; browser launch is skipped.</dd>
549
+ </div>
550
+ <div class="row">
551
+ <dt>Playbook</dt>
552
+ <dd>This page follows the <code>diagram</code> visual playbook landmarks.</dd>
553
+ </div>
554
+ </dl>
555
+ </section>
556
+
557
+
558
+ <section id="viz-open-questions" aria-label="Open questions">
559
+ <div class="section-head">
560
+ <h2>Open questions</h2>
561
+ <p>Uncertainty left for humans; mirrored in the outline + JSON embed.</p>
562
+ </div>
563
+ <table>
564
+ <thead>
565
+ <tr>
566
+ <th scope="col">Id</th>
567
+ <th scope="col">Question</th>
568
+ </tr>
569
+ </thead>
570
+ <tbody>
571
+ <tr>
572
+ <td><code>q-json-ssot</code></td>
573
+ <td>
574
+ Should agents ever prefer <code>#devrites-outline</code> JSON over
575
+ <code>.outline.md</code> when both exist?
576
+ <div class="q-meta">
577
+ <span class="badge" data-tone="ok">confidence: high</span>
578
+ <span class="badge">status: open</span>
579
+ <span class="badge" data-tone="accent">outline.md wins</span>
580
+ </div>
581
+ </td>
582
+ </tr>
583
+ <tr>
584
+ <td><code>q-browser-ci</code></td>
585
+ <td>
586
+ Do all interactive review hosts have a usable OS opener, or is
587
+ <kbd>--no-open</kbd> the default smoke path?
588
+ <div class="q-meta">
589
+ <span class="badge" data-tone="warn">confidence: medium</span>
590
+ <span class="badge">status: open</span>
591
+ </div>
592
+ </td>
593
+ </tr>
594
+ </tbody>
595
+ </table>
596
+ </section>
597
+
598
+ <section id="diagram-citations" aria-label="Citations">
599
+ <div class="section-head">
600
+ <h2>Citations</h2>
601
+ <p>Claims on this page map to repo paths.</p>
602
+ </div>
603
+ <table>
604
+ <thead>
605
+ <tr>
606
+ <th scope="col">Claim</th>
607
+ <th scope="col">Path</th>
608
+ </tr>
609
+ </thead>
610
+ <tbody>
611
+ <tr>
612
+ <td>Opener implementation</td>
613
+ <td><code>engine/internal/lib/open_visual.go</code></td>
614
+ </tr>
615
+ <tr>
616
+ <td>Playbook router</td>
617
+ <td><code>pack/.claude/skills/devrites-lib/reference/visual-playbooks/index.md</code></td>
618
+ </tr>
619
+ <tr>
620
+ <td>Outline template</td>
621
+ <td><code>pack/.claude/skills/devrites-lib/reference/visual-playbooks/outline-template.md</code></td>
622
+ </tr>
623
+ <tr>
624
+ <td>Explain tip hook</td>
625
+ <td><code>pack/.claude/skills/rite-explain/SKILL.md</code></td>
626
+ </tr>
627
+ </tbody>
628
+ </table>
629
+ <p class="note">Fixture path: <code>engine/testdata/visual/open-visual-smoke.html</code></p>
630
+ </section>
631
+ </main>
632
+ </div>
633
+
634
+ <script type="application/json" id="devrites-outline">
635
+ {
636
+ "version": 1,
637
+ "title": "open-visual smoke fixture",
638
+ "purpose": "Prove that devrites-engine open-visual resolves a portable HTML+outline pair under engine/testdata/visual/, prints agent tips, runs a non-fatal inventory↔HTML id consistency check, and honors --no-open without launching a browser.",
639
+ "playbooks": ["diagram"],
640
+ "ids": [
641
+ {"id": "viz-title", "meaning": "Page header / question the fixture answers"},
642
+ {"id": "diagram-overview", "meaning": "Core relationship figure"},
643
+ {"id": "diagram-title", "meaning": "Accessible SVG title"},
644
+ {"id": "node-html", "meaning": "HTML fixture node"},
645
+ {"id": "node-outline", "meaning": "Outline companion node"},
646
+ {"id": "node-opener", "meaning": "open-visual command node"},
647
+ {"id": "edge-html-outline", "meaning": "Dual-read pairing"},
648
+ {"id": "edge-outline-opener", "meaning": "Opener tip path"},
649
+ {"id": "diagram-detail", "meaning": "Prose steps mirroring the overview"},
650
+ {"id": "diagram-legend", "meaning": "Symbol / behavior key"},
651
+ {"id": "viz-open-questions", "meaning": "Open questions with confidence"},
652
+ {"id": "diagram-citations", "meaning": "Repo path citations"}
653
+ ],
654
+ "relationships": [
655
+ {"from": "node-html", "to": "node-outline", "note": "Dual-read pair; outline is SSOT on conflict"},
656
+ {"from": "node-outline", "to": "node-opener", "note": "Opener prints outline path + playbook tip"},
657
+ {"from": "open-visual --no-open", "to": "browser", "note": "Must not launch; tips still print"},
658
+ {"from": "diagram-overview", "to": "diagram-detail", "note": "Same three-step story: pair → SSOT → opener"},
659
+ {"from": "#devrites-outline", "to": ".outline.md", "note": "JSON is a mirror; outline.md wins on conflict"}
660
+ ],
661
+ "citations": [
662
+ {"claim": "Opener implementation", "path": "engine/internal/lib/open_visual.go"},
663
+ {"claim": "Playbook router", "path": "pack/.claude/skills/devrites-lib/reference/visual-playbooks/index.md"},
664
+ {"claim": "Outline template", "path": "pack/.claude/skills/devrites-lib/reference/visual-playbooks/outline-template.md"},
665
+ {"claim": "rite-explain tip hook", "path": "pack/.claude/skills/rite-explain/SKILL.md"}
666
+ ],
667
+ "open_questions": [
668
+ {"id": "q-json-ssot", "text": "Should agents ever prefer #devrites-outline JSON over .outline.md when both exist?", "confidence": "high"},
669
+ {"id": "q-browser-ci", "text": "Do all interactive review hosts have a usable OS opener, or is --no-open the default smoke path?", "confidence": "medium"}
670
+ ],
671
+ "confidence": "high"
672
+ }
673
+ </script>
674
+ </body>
675
+ </html>