@erclx/canon 5.27.0 → 5.29.0

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 (177) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/draft-ready/SKILL.md +3 -1
  3. package/claude/skills/git-pr/references/evidence.md +1 -1
  4. package/claude/skills/review-address/REQUIREMENT.md +4 -2
  5. package/claude/skills/review-address/SKILL.md +16 -7
  6. package/claude/skills/review-address/references/rebase-conflicts.md +4 -2
  7. package/claude/skills/review-craft/references/ui.md +4 -4
  8. package/claude/skills/review-ui/REQUIREMENT.md +2 -2
  9. package/claude/skills/review-ui/SKILL.md +13 -11
  10. package/claude/skills/review-ui/references/driving.md +8 -8
  11. package/claude/skills/review-ui/references/post.md +19 -7
  12. package/claude/skills/review-ui/references/tick.md +22 -0
  13. package/claude/skills/role-orchestrator/REQUIREMENT.md +15 -15
  14. package/claude/skills/role-orchestrator/SKILL.md +24 -26
  15. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +18 -16
  16. package/claude/skills/role-orchestrator/references/orchestrator-handback.md +12 -2
  17. package/claude/skills/role-orchestrator/references/orchestrator-inbound.md +1 -1
  18. package/claude/skills/role-orchestrator/references/orchestrator-launch.md +1 -1
  19. package/claude/skills/role-orchestrator/references/orchestrator-output.md +4 -4
  20. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +5 -5
  21. package/claude/skills/role-orchestrator/references/orchestrator-poll.md +11 -2
  22. package/claude/skills/role-orchestrator/references/orchestrator-refill.md +6 -6
  23. package/claude/skills/role-orchestrator/references/orchestrator-sweep.md +2 -2
  24. package/claude/skills/role-orchestrator/scripts/poll.ts +304 -10
  25. package/claude/skills/role-planner/REQUIREMENT.md +1 -1
  26. package/claude/skills/role-planner/SKILL.md +2 -2
  27. package/claude/skills/role-reviewer/SKILL.md +1 -1
  28. package/claude/skills/role-worker/SKILL.md +3 -2
  29. package/claude/skills/ui-checklist/SKILL.md +1 -1
  30. package/docs/agents/canvas.md +12 -4
  31. package/docs/agents/gate.md +3 -1
  32. package/docs/agents/index.md +2 -1
  33. package/docs/agents/pr-evidence.md +15 -14
  34. package/docs/agents/pr-tick.md +72 -0
  35. package/docs/agents/sandbox.md +2 -2
  36. package/docs/agents/tasks-plans.md +1 -1
  37. package/docs/agents/tasks-validate.md +18 -18
  38. package/docs/agents/tasks.md +1 -1
  39. package/docs/workflow/operating-model.md +10 -15
  40. package/docs/workflow/parallel-features.md +7 -7
  41. package/package.json +1 -1
  42. package/src/audits/baseline.ts +1 -1
  43. package/src/audits/run.ts +1 -1
  44. package/src/canvas/capture.ts +1 -1
  45. package/src/canvas/client/app.tsx +105 -4
  46. package/src/canvas/client/inspector/field.tsx +61 -2
  47. package/src/canvas/client/inspector/fields.css +78 -0
  48. package/src/canvas/client/inspector/token-select.tsx +109 -0
  49. package/src/canvas/client/inspector/values.ts +74 -2
  50. package/src/canvas/client/inspector.tsx +134 -15
  51. package/src/canvas/client/keys.ts +71 -0
  52. package/src/canvas/client/pages-panel.tsx +5 -1
  53. package/src/canvas/client/panel-handle.tsx +90 -0
  54. package/src/canvas/client/selection.tsx +3 -1
  55. package/src/canvas/client/shell.css +194 -4
  56. package/src/canvas/client/state.ts +255 -11
  57. package/src/canvas/client/surface.tsx +231 -18
  58. package/src/canvas/client/tools.tsx +153 -0
  59. package/src/canvas/content.ts +66 -1
  60. package/src/canvas/edit.ts +207 -25
  61. package/src/canvas/history.ts +116 -0
  62. package/src/canvas/server.ts +119 -13
  63. package/src/canvas/tokens.ts +1 -1
  64. package/src/claude/seeds.ts +2 -2
  65. package/src/claude/skills-reach.ts +1 -1
  66. package/src/{exec.ts → cli/exec.ts} +2 -2
  67. package/src/{help.ts → cli/help.ts} +1 -1
  68. package/src/cli.ts +1 -1
  69. package/src/commands/audits.ts +9 -1
  70. package/src/commands/autoship.ts +1 -1
  71. package/src/commands/canvas.ts +2 -2
  72. package/src/commands/capture.ts +1 -1
  73. package/src/commands/census.ts +1 -1
  74. package/src/commands/claude.ts +5 -5
  75. package/src/commands/comments.ts +1 -1
  76. package/src/commands/context.ts +1 -1
  77. package/src/commands/demo.ts +9 -1
  78. package/src/commands/deps.ts +1 -1
  79. package/src/commands/design.ts +12 -4
  80. package/src/commands/docs.ts +2 -2
  81. package/src/commands/driver.ts +9 -1
  82. package/src/commands/feedback.ts +3 -3
  83. package/src/commands/gate.ts +10 -3
  84. package/src/commands/gov.ts +3 -3
  85. package/src/commands/indexes.ts +1 -1
  86. package/src/commands/init.ts +11 -3
  87. package/src/commands/intake.ts +1 -1
  88. package/src/commands/inventory.ts +9 -1
  89. package/src/commands/labels.ts +1 -1
  90. package/src/commands/markdown.ts +1 -1
  91. package/src/commands/migrate.ts +2 -2
  92. package/src/commands/pr.ts +324 -9
  93. package/src/commands/records.ts +1 -1
  94. package/src/commands/repo.ts +1 -1
  95. package/src/commands/sandbox.ts +11 -14
  96. package/src/commands/secrets.ts +9 -1
  97. package/src/commands/sessions.ts +1 -1
  98. package/src/commands/slides.ts +2 -2
  99. package/src/commands/standards.ts +1 -1
  100. package/src/commands/sync.ts +3 -3
  101. package/src/commands/targets.ts +1 -1
  102. package/src/commands/tasks.ts +20 -3
  103. package/src/commands/teach.ts +1 -1
  104. package/src/commands/tooling.ts +2 -2
  105. package/src/commands/transcripts.ts +2 -2
  106. package/src/commands/upgrade.ts +2 -2
  107. package/src/commands/wiki.ts +2 -2
  108. package/src/commands/worktrees.ts +1 -1
  109. package/src/context/architecture.ts +1 -1
  110. package/src/context/citations.ts +2 -2
  111. package/src/context/classify/extract.ts +1 -1
  112. package/src/context/folders.ts +2 -2
  113. package/src/context/requirements.ts +1 -1
  114. package/src/docs/read.ts +1 -1
  115. package/src/feedback/github.ts +1 -1
  116. package/src/gate/measures.ts +2 -2
  117. package/src/gate/sequencer.ts +55 -3
  118. package/src/gate/stages.ts +8 -0
  119. package/src/gate/suite-lock.ts +214 -0
  120. package/src/init/run.ts +1 -1
  121. package/src/intake/folder.ts +1 -1
  122. package/src/labels/map.ts +1 -1
  123. package/src/labels/phase.ts +3 -3
  124. package/src/migrate/record-layout.ts +1 -1
  125. package/src/migrate/records.ts +11 -11
  126. package/src/migrate/rename.ts +1 -1
  127. package/src/migrate/scratch-evidence.ts +1 -1
  128. package/src/migrate/surface-roots.ts +6 -6
  129. package/src/pr/evidence.ts +188 -16
  130. package/src/pr/tick.ts +56 -0
  131. package/src/process/harness.ts +1 -1
  132. package/src/records/backup.ts +1 -1
  133. package/src/records/ordinal.ts +1 -1
  134. package/src/records/prune.ts +1 -1
  135. package/src/records/size.ts +1 -1
  136. package/src/records/stale-canonical.ts +1 -1
  137. package/src/records/stale.ts +1 -1
  138. package/src/records/validate.ts +2 -2
  139. package/src/{project-root.ts → roots/project.ts} +2 -2
  140. package/src/{record-root.ts → roots/record.ts} +1 -1
  141. package/src/sandbox/coverage.ts +11 -12
  142. package/src/sandbox/equivalence.ts +57 -26
  143. package/src/sandbox/headless/run.ts +1 -1
  144. package/src/sandbox/hooks.ts +1 -1
  145. package/src/sandbox/provision.ts +79 -29
  146. package/src/sandbox/scenario.ts +588 -0
  147. package/src/serve/report.ts +1 -1
  148. package/src/sessions/live.ts +4 -1
  149. package/src/sessions/transfer/bundle.ts +1 -1
  150. package/src/slides/decks.ts +1 -1
  151. package/src/standards/read.ts +1 -1
  152. package/src/sync/engine.ts +2 -2
  153. package/src/sync/git.ts +1 -1
  154. package/src/sync/layout.ts +2 -2
  155. package/src/sync/stamp.ts +1 -1
  156. package/src/sync/workflow.ts +1 -1
  157. package/src/targets/validate.ts +2 -2
  158. package/src/tasks/answers.ts +1 -1
  159. package/src/tasks/archive.ts +1 -1
  160. package/src/tasks/label.ts +1 -1
  161. package/src/tasks/reach.ts +1 -1
  162. package/src/tasks/validate.ts +38 -14
  163. package/src/teach/nav.ts +1 -1
  164. package/src/teach/workspace.ts +1 -1
  165. package/src/tooling/inject.ts +2 -2
  166. package/src/tooling/read.ts +1 -1
  167. package/src/tooling/scan.ts +1 -1
  168. package/src/tooling/verify-stack.ts +1 -1
  169. package/src/version/installed.ts +1 -1
  170. package/src/version/skew.ts +1 -1
  171. package/standards/board.md +3 -3
  172. package/standards/plan.md +13 -7
  173. package/tsconfig.json +1 -1
  174. /package/src/{cli-run.ts → cli/run.ts} +0 -0
  175. /package/src/{ui.ts → cli/ui.ts} +0 -0
  176. /package/src/{legacy-path.ts → roots/legacy.ts} +0 -0
  177. /package/src/{surface-root.ts → roots/surface.ts} +0 -0
@@ -0,0 +1,90 @@
1
+ /** @jsxImportSource preact */
2
+ import type { JSX } from 'preact'
3
+ import { useRef } from 'preact/hooks'
4
+ import {
5
+ clampPanelWidth,
6
+ MAX_PANEL_WIDTH,
7
+ MIN_PANEL_WIDTH,
8
+ type PanelSide,
9
+ panelWidths,
10
+ setPanelWidth,
11
+ } from '@/canvas/client/state'
12
+
13
+ const KEY_STEP = 16
14
+ const SHIFT_STEP = 64
15
+
16
+ const NAMES: Readonly<Record<PanelSide, string>> = {
17
+ left: 'Resize pages panel',
18
+ right: 'Resize details panel',
19
+ }
20
+
21
+ /**
22
+ * The drag handle on a panel's inner edge, a focusable separator the arrow
23
+ * keys move as well. It sits outside the surface, so a drag on it never pans,
24
+ * moves a frame, or reaches the undo history.
25
+ */
26
+ export function PanelHandle({
27
+ side,
28
+ controls,
29
+ }: {
30
+ readonly side: PanelSide
31
+ readonly controls: string
32
+ }): JSX.Element {
33
+ const drag = useRef<{ x: number; width: number } | undefined>(undefined)
34
+ const width = panelWidths.value[side]
35
+ /* Dragging right widens the left panel and narrows the right one. */
36
+ const sign = side === 'left' ? 1 : -1
37
+
38
+ const handlePointerDown = (event: PointerEvent) => {
39
+ event.preventDefault()
40
+ drag.current = { x: event.clientX, width }
41
+ ;(event.currentTarget as HTMLElement).setPointerCapture?.(event.pointerId)
42
+ }
43
+
44
+ const handlePointerMove = (event: PointerEvent) => {
45
+ const start = drag.current
46
+ if (!start) return
47
+ setPanelWidth(
48
+ side,
49
+ start.width + sign * (event.clientX - start.x),
50
+ window.innerWidth,
51
+ )
52
+ }
53
+
54
+ const handlePointerUp = () => {
55
+ drag.current = undefined
56
+ }
57
+
58
+ const handleKeyDown = (event: KeyboardEvent) => {
59
+ const step = event.shiftKey ? SHIFT_STEP : KEY_STEP
60
+ const next: Record<string, number | undefined> = {
61
+ ArrowRight: width + sign * step,
62
+ ArrowLeft: width - sign * step,
63
+ Home: MIN_PANEL_WIDTH,
64
+ End: MAX_PANEL_WIDTH,
65
+ }
66
+ const wanted = next[event.key]
67
+ if (wanted === undefined) return
68
+ event.preventDefault()
69
+ setPanelWidth(side, wanted, window.innerWidth)
70
+ }
71
+
72
+ return (
73
+ <div
74
+ class={`panel-handle panel-handle-${side}`}
75
+ role="separator"
76
+ aria-orientation="vertical"
77
+ aria-label={NAMES[side]}
78
+ aria-controls={controls}
79
+ aria-valuenow={width}
80
+ aria-valuemin={MIN_PANEL_WIDTH}
81
+ aria-valuemax={clampPanelWidth(side, MAX_PANEL_WIDTH, window.innerWidth)}
82
+ tabIndex={0}
83
+ onPointerDown={handlePointerDown}
84
+ onPointerMove={handlePointerMove}
85
+ onPointerUp={handlePointerUp}
86
+ onPointerCancel={handlePointerUp}
87
+ onKeyDown={handleKeyDown}
88
+ />
89
+ )
90
+ }
@@ -1,7 +1,7 @@
1
1
  /** @jsxImportSource preact */
2
2
  import type { JSX } from 'preact'
3
3
  import { useRef, useState } from 'preact/hooks'
4
- import { view } from '@/canvas/client/state'
4
+ import { shownTool, view } from '@/canvas/client/state'
5
5
 
6
6
  /** Screen pixels a press travels before it reads as a drag and not a click. */
7
7
  export const DRAG_THRESHOLD = 3
@@ -97,6 +97,8 @@ export function SelectionOverlay({
97
97
 
98
98
  const handlePointerDown = (corner: Corner) => (event: PointerEvent) => {
99
99
  if (event.button !== 0) return
100
+ /* A pan drag starting on a handle belongs to the surface behind it. */
101
+ if (shownTool.value === 'pan') return
100
102
  event.preventDefault()
101
103
  event.stopPropagation()
102
104
  const origin = measure()
@@ -38,11 +38,58 @@ code {
38
38
  }
39
39
 
40
40
  .shell {
41
+ position: relative;
41
42
  display: grid;
42
- grid-template-columns: 15rem minmax(0, 1fr) 16rem;
43
+ grid-template-columns: var(--panel-left, 15rem) minmax(0, 1fr) var(
44
+ --panel-right,
45
+ 16rem
46
+ );
43
47
  height: 100%;
44
48
  }
45
49
 
50
+ /*
51
+ * Centered on the edge, so a 24 pixel circle around it stays inside the
52
+ * panel's padding on one side and short of the tool strip on the other, with
53
+ * a line drawn at the edge itself.
54
+ */
55
+ .panel-handle {
56
+ position: absolute;
57
+ top: 0;
58
+ bottom: 0;
59
+ z-index: 2;
60
+ width: 12px;
61
+ cursor: col-resize;
62
+ touch-action: none;
63
+ }
64
+
65
+ .panel-handle-left {
66
+ left: calc(var(--panel-left, 15rem) - 6px);
67
+ }
68
+
69
+ .panel-handle-right {
70
+ right: calc(var(--panel-right, 16rem) - 6px);
71
+ }
72
+
73
+ .panel-handle::after {
74
+ content: '';
75
+ position: absolute;
76
+ top: 0;
77
+ bottom: 0;
78
+ left: 5px;
79
+ width: 2px;
80
+ background: transparent;
81
+ }
82
+
83
+ .panel-handle:hover::after,
84
+ .panel-handle:active::after,
85
+ .panel-handle:focus-visible::after {
86
+ background: var(--color-accent);
87
+ }
88
+
89
+ .shell.panels-hidden > .panel-handle {
90
+ display: none;
91
+ }
92
+
46
93
  .panel {
47
94
  overflow-y: auto;
48
95
  padding: var(--space-md);
@@ -217,6 +264,27 @@ code {
217
264
  cursor: grabbing;
218
265
  }
219
266
 
267
+ /* Inset, so the panels on either side never clip the ring. */
268
+ .surface:focus-visible {
269
+ outline-offset: -2px;
270
+ }
271
+
272
+ /*
273
+ * While panning, a frame takes no pointer, so a drag over one moves the view
274
+ * rather than picking an element, and the cursor reads the viewport's grab.
275
+ * The descendants are named too, since a selection handle sets its own
276
+ * pointer events and would otherwise take the drag as a resize. Nothing in
277
+ * the viewport starts a text selection while a pan drags across it.
278
+ */
279
+ .surface[data-tool='pan'] .viewport {
280
+ user-select: none;
281
+ }
282
+
283
+ .surface[data-tool='pan'] .frame,
284
+ .surface[data-tool='pan'] .frame * {
285
+ pointer-events: none;
286
+ }
287
+
220
288
  .plane {
221
289
  position: absolute;
222
290
  top: 0;
@@ -239,8 +307,9 @@ code {
239
307
  align-items: center;
240
308
  /*
241
309
  * Sized against the zoom, so the label reads at one screen size at any zoom.
242
- * A row too short for all three parts wraps the theme switch and then the
243
- * size onto lines the clip hides, so the name keeps the row longest.
310
+ * A row too short for every part wraps the theme switch, then the editing
311
+ * badge, then the size onto lines the clip hides, so the name keeps the row
312
+ * longest.
244
313
  */
245
314
  flex-wrap: wrap;
246
315
  align-content: flex-start;
@@ -273,6 +342,40 @@ code {
273
342
  font-variant-numeric: tabular-nums;
274
343
  }
275
344
 
345
+ /*
346
+ * Ahead of the theme switch, so a row too short for both wraps the switch
347
+ * first. The editing outline still marks the frame when the badge wraps too.
348
+ */
349
+ .frame-editing {
350
+ display: inline-flex;
351
+ align-items: center;
352
+ gap: calc(4px * var(--outline-scale, 1));
353
+ padding: calc(1px * var(--outline-scale, 1))
354
+ calc(6px * var(--outline-scale, 1));
355
+ border-radius: var(--radius-pill);
356
+ background: color-mix(in srgb, var(--color-success) 16%, transparent);
357
+ color: var(--color-success);
358
+ font-weight: 600;
359
+ }
360
+
361
+ .frame-editing:empty {
362
+ display: none;
363
+ }
364
+
365
+ .frame-editing::before {
366
+ content: '';
367
+ width: calc(6px * var(--outline-scale, 1));
368
+ height: calc(6px * var(--outline-scale, 1));
369
+ border-radius: 50%;
370
+ background: currentColor;
371
+ }
372
+
373
+ /* Dashed, so it reads apart from the solid accent of a selection. */
374
+ .frame[data-editing='true'] iframe {
375
+ outline: calc(2px * var(--outline-scale, 1)) dashed var(--color-success);
376
+ outline-offset: calc(2px * var(--outline-scale, 1));
377
+ }
378
+
276
379
  .frame-theme {
277
380
  margin-left: auto;
278
381
  padding: calc(1px * var(--outline-scale, 1))
@@ -425,6 +528,82 @@ code {
425
528
  background: var(--color-chrome);
426
529
  }
427
530
 
531
+ .tools {
532
+ position: absolute;
533
+ top: var(--space-md);
534
+ left: var(--space-md);
535
+ display: flex;
536
+ flex-direction: column;
537
+ gap: 2px;
538
+ padding: var(--space-xs);
539
+ border: 1px solid var(--color-border);
540
+ border-radius: var(--radius-panel);
541
+ background: var(--color-chrome);
542
+ }
543
+
544
+ .tool-button {
545
+ display: grid;
546
+ place-items: center;
547
+ width: 28px;
548
+ height: 28px;
549
+ padding: 0;
550
+ border: 0;
551
+ border-radius: var(--radius-action);
552
+ background: transparent;
553
+ color: var(--color-text-secondary);
554
+ cursor: pointer;
555
+ }
556
+
557
+ .tool-button svg {
558
+ width: 16px;
559
+ height: 16px;
560
+ fill: none;
561
+ stroke: currentColor;
562
+ stroke-width: 1.3;
563
+ stroke-linejoin: round;
564
+ stroke-linecap: round;
565
+ }
566
+
567
+ .tool-button[data-tool='move'] svg {
568
+ fill: currentColor;
569
+ }
570
+
571
+ .tool-button:hover {
572
+ color: var(--color-text);
573
+ background: var(--color-surface);
574
+ }
575
+
576
+ .tool-button[aria-pressed='true'] {
577
+ color: var(--color-text);
578
+ background: var(--color-border);
579
+ }
580
+
581
+ .tool-button:disabled {
582
+ opacity: 0.4;
583
+ background: transparent;
584
+ cursor: not-allowed;
585
+ }
586
+
587
+ .tools-divider {
588
+ height: 1px;
589
+ margin: 2px var(--space-xs);
590
+ background: var(--color-border);
591
+ }
592
+
593
+ .history-notice {
594
+ position: absolute;
595
+ top: var(--space-md);
596
+ left: 50%;
597
+ max-width: min(28rem, calc(100% - 8rem));
598
+ margin: 0;
599
+ padding: var(--space-xs) var(--space-sm);
600
+ border: 1px solid var(--color-border);
601
+ border-radius: var(--radius-panel);
602
+ background: var(--color-chrome);
603
+ color: var(--color-text);
604
+ transform: translateX(-50%);
605
+ }
606
+
428
607
  .zoom {
429
608
  min-width: 3.5rem;
430
609
  text-align: center;
@@ -437,7 +616,8 @@ code {
437
616
  grid-template-columns: 12rem minmax(0, 1fr);
438
617
  }
439
618
 
440
- .panel-right {
619
+ .panel-right,
620
+ .panel-handle {
441
621
  display: none;
442
622
  }
443
623
  }
@@ -455,6 +635,16 @@ code {
455
635
  }
456
636
  }
457
637
 
638
+ /* Two classes outrank every width's own columns, so hiding holds at each. */
639
+ .shell.panels-hidden {
640
+ grid-template-columns: minmax(0, 1fr);
641
+ grid-template-rows: minmax(0, 1fr);
642
+ }
643
+
644
+ .shell.panels-hidden > .panel {
645
+ display: none;
646
+ }
647
+
458
648
  .frame-row {
459
649
  display: flex;
460
650
  align-items: center;