pmx-canvas 0.8.0 → 0.9.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 (341) hide show
  1. package/.github/extensions/pmx-canvas/runtime.json +1 -1
  2. package/.github/extensions/pmx-canvas/tools.json +601 -22
  3. package/CHANGELOG.md +66 -0
  4. package/Readme.md +131 -532
  5. package/dist/canvas/global.css +2759 -510
  6. package/dist/canvas/index.js +168 -107
  7. package/dist/canvas/mcp-app.html +2936 -632
  8. package/dist/json-render/index.css +81 -1
  9. package/dist/json-render/index.js +20 -20
  10. package/dist/types/client/canvas/BarHint.d.ts +12 -2
  11. package/dist/types/client/canvas/BoardLinkDialog.d.ts +3 -1
  12. package/dist/types/client/canvas/BoardPinButton.d.ts +10 -0
  13. package/dist/types/client/canvas/BoardSwitcher.d.ts +1 -1
  14. package/dist/types/client/canvas/CanvasViewport.d.ts +1 -1
  15. package/dist/types/client/canvas/CommandBar.d.ts +3 -4
  16. package/dist/types/client/canvas/ConnectionBanner.d.ts +2 -1
  17. package/dist/types/client/canvas/ContextChip.d.ts +3 -0
  18. package/dist/types/client/canvas/CrossBoardReferences.d.ts +5 -0
  19. package/dist/types/client/canvas/EdgeLayer.d.ts +14 -3
  20. package/dist/types/client/canvas/EmptyState.d.ts +4 -4
  21. package/dist/types/client/canvas/HomeGraphView.d.ts +2 -0
  22. package/dist/types/client/canvas/HomeMapView.d.ts +1 -0
  23. package/dist/types/client/canvas/HomeReviewView.d.ts +6 -0
  24. package/dist/types/client/canvas/HomeView.d.ts +9 -7
  25. package/dist/types/client/canvas/KnowledgeDialog.d.ts +4 -0
  26. package/dist/types/client/canvas/LifecycleChrome.d.ts +5 -0
  27. package/dist/types/client/canvas/LifecycleFields.d.ts +7 -0
  28. package/dist/types/client/canvas/LifecyclePanel.d.ts +1 -0
  29. package/dist/types/client/canvas/MemoryInspector.d.ts +7 -0
  30. package/dist/types/client/canvas/NearPinLayer.d.ts +14 -0
  31. package/dist/types/client/canvas/NodeContextMark.d.ts +40 -7
  32. package/dist/types/client/canvas/RelationPresetsDialog.d.ts +4 -0
  33. package/dist/types/client/canvas/RelationsPanel.d.ts +4 -0
  34. package/dist/types/client/canvas/SessionPanel.d.ts +2 -0
  35. package/dist/types/client/canvas/canvas-area.d.ts +1 -1
  36. package/dist/types/client/canvas/graph-geometry.d.ts +27 -0
  37. package/dist/types/client/canvas/graph-model.d.ts +37 -0
  38. package/dist/types/client/canvas/memory-layout.d.ts +23 -0
  39. package/dist/types/client/canvas/use-chrome-height.d.ts +2 -0
  40. package/dist/types/client/canvas/use-focus-trap.d.ts +1 -0
  41. package/dist/types/client/canvas/use-memory-graph.d.ts +8 -0
  42. package/dist/types/client/canvas/use-pan-zoom.d.ts +4 -1
  43. package/dist/types/client/icons.d.ts +0 -10
  44. package/dist/types/client/nodes/AxStepControls.d.ts +1 -1
  45. package/dist/types/client/nodes/HtmlNode.d.ts +1 -0
  46. package/dist/types/client/nodes/InlineMarkdownEditor.d.ts +4 -4
  47. package/dist/types/client/nodes/MarkdownNode.d.ts +5 -1
  48. package/dist/types/client/nodes/McpAppNode.d.ts +4 -4
  49. package/dist/types/client/nodes/viewer-runtime-state.d.ts +1 -1
  50. package/dist/types/client/state/boards-store.d.ts +81 -9
  51. package/dist/types/client/state/canvas-store.d.ts +14 -1
  52. package/dist/types/client/state/context-chip-store.d.ts +22 -0
  53. package/dist/types/client/state/context-status-store.d.ts +35 -0
  54. package/dist/types/client/state/intent-bridge.d.ts +11 -8
  55. package/dist/types/client/state/knowledge-store.d.ts +22 -0
  56. package/dist/types/client/state/near-pin-store.d.ts +44 -1
  57. package/dist/types/client/state/presence-store.d.ts +4 -4
  58. package/dist/types/client/state/presentation.d.ts +28 -0
  59. package/dist/types/client/state/relation-store.d.ts +10 -0
  60. package/dist/types/client/state/session-store.d.ts +41 -2
  61. package/dist/types/client/state/workspace-settings.d.ts +7 -0
  62. package/dist/types/client/types.d.ts +13 -1
  63. package/dist/types/client/utils/keyboard.d.ts +2 -0
  64. package/dist/types/client/utils/time-ago.d.ts +4 -0
  65. package/dist/types/mcp/context-uri-template.d.ts +3 -1
  66. package/dist/types/server/agent-presence.d.ts +10 -3
  67. package/dist/types/server/ax-context.d.ts +1 -1
  68. package/dist/types/server/canvas-db.d.ts +30 -7
  69. package/dist/types/server/canvas-operations.d.ts +24 -2
  70. package/dist/types/server/canvas-provenance.d.ts +22 -2
  71. package/dist/types/server/canvas-serialization.d.ts +2 -0
  72. package/dist/types/server/canvas-state.d.ts +93 -15
  73. package/dist/types/server/canvas-validation.d.ts +1 -1
  74. package/dist/types/server/context-brief.d.ts +94 -6
  75. package/dist/types/server/context-reads.d.ts +33 -7
  76. package/dist/types/server/edit-summary.d.ts +20 -0
  77. package/dist/types/server/index.d.ts +71 -10
  78. package/dist/types/server/knowledge-lifecycle.d.ts +221 -0
  79. package/dist/types/server/lifecycle-status.d.ts +10 -0
  80. package/dist/types/server/memory-graph.d.ts +9 -0
  81. package/dist/types/server/mutation-history.d.ts +2 -0
  82. package/dist/types/server/operations/ops/ax-shared.d.ts +1 -1
  83. package/dist/types/server/operations/ops/boards.d.ts +2 -1
  84. package/dist/types/server/operations/ops/lifecycle.d.ts +2 -0
  85. package/dist/types/server/operations/ops/memory-graph.d.ts +2 -0
  86. package/dist/types/server/operations/ops/nodes.d.ts +5 -0
  87. package/dist/types/server/operations/ops/query.d.ts +23 -0
  88. package/dist/types/server/operations/registry.d.ts +1 -0
  89. package/dist/types/server/placement.d.ts +7 -0
  90. package/dist/types/server/scope-fence.d.ts +2 -1
  91. package/dist/types/server/spatial-analysis.d.ts +6 -3
  92. package/dist/types/server/tour-control.d.ts +32 -0
  93. package/dist/types/server/wiki-links.d.ts +14 -0
  94. package/dist/types/server/workbench-auth.d.ts +2 -0
  95. package/dist/types/shared/agent-context.d.ts +2 -0
  96. package/dist/types/shared/agent-presence.d.ts +15 -12
  97. package/dist/types/shared/auto-arrange.d.ts +1 -0
  98. package/dist/types/shared/boards.d.ts +4 -4
  99. package/dist/types/shared/knowledge-lifecycle.d.ts +41 -0
  100. package/dist/types/shared/knowledge-presets.d.ts +6 -0
  101. package/dist/types/shared/lifecycle-status.d.ts +24 -0
  102. package/dist/types/shared/memory-graph.d.ts +53 -0
  103. package/dist/types/shared/relations.d.ts +68 -0
  104. package/dist/types/shared/semantic-attention.d.ts +2 -0
  105. package/dist/types/shared/tour.d.ts +38 -0
  106. package/dist/types/shared/workspace-settings.d.ts +11 -0
  107. package/docs/RELEASE.md +21 -3
  108. package/docs/adr-001-bun-only-runtime.md +7 -1
  109. package/docs/adr-002-shared-product-separate-runtimes.md +93 -0
  110. package/docs/api-reference.md +109 -0
  111. package/docs/ax-host-adapter-contract.md +1 -1
  112. package/docs/ax-state-contract.md +5 -4
  113. package/docs/cli.md +153 -15
  114. package/docs/demos/pmx-canvas-demo.mp4 +0 -0
  115. package/docs/design/AgentContext.dc.html +3 -3
  116. package/docs/design/BoardPins.dc.html +37 -22
  117. package/docs/design/Categories.dc.html +208 -0
  118. package/docs/design/CategoryEditor.dc.html +33 -0
  119. package/docs/design/Chrome.dc.html +55 -9
  120. package/docs/design/ContextChip.dc.html +224 -0
  121. package/docs/design/ContextChip600.dc.html +136 -0
  122. package/docs/design/EmptyState.dc.html +45 -0
  123. package/docs/design/Flows.dc.html +23 -1
  124. package/docs/design/HeaderMarks.dc.html +183 -0
  125. package/docs/design/Home.dc.html +19 -1
  126. package/docs/design/HomeGraph.dc.html +86 -202
  127. package/docs/design/HomeMap.dc.html +23 -1
  128. package/docs/design/HomeTeam.dc.html +32 -14
  129. package/docs/design/Lifecycle.dc.html +22 -0
  130. package/docs/design/Lifecycle600.dc.html +18 -0
  131. package/docs/design/LifecycleVariants.dc.html +157 -0
  132. package/docs/design/LinkAuthorship.dc.html +139 -0
  133. package/docs/design/LinksChanged.dc.html +203 -0
  134. package/docs/design/LinksOptions.dc.html +137 -0
  135. package/docs/design/LinksOptions600.dc.html +134 -0
  136. package/docs/design/NearPin.dc.html +9 -10
  137. package/docs/design/Pane600.dc.html +4 -3
  138. package/docs/design/Relations.dc.html +3 -2
  139. package/docs/design/Restore.dc.html +152 -0
  140. package/docs/design/SeeChange.dc.html +267 -0
  141. package/docs/design/Uses.dc.html +12 -6
  142. package/docs/design/canvas.json +146 -19
  143. package/docs/design.md +454 -18
  144. package/docs/environment.md +0 -1
  145. package/docs/evals/curation-effect.md +128 -0
  146. package/docs/http-api.md +465 -38
  147. package/docs/mcp.md +150 -19
  148. package/docs/node-types.md +58 -3
  149. package/docs/plans/design-gaps-2026-10-08.md +53 -0
  150. package/docs/plans/plan-009-tech-debt-backlog.md +379 -0
  151. package/docs/plans/plan-016-context-made-visible-wave-1.md +150 -5
  152. package/docs/plans/plan-017-knowledge-categories-and-lifecycle.md +175 -0
  153. package/docs/plans/plan-018-connected-memory-wave-2.md +594 -0
  154. package/docs/plans/plan-019-post-0.9-roadmap.md +198 -0
  155. package/docs/product-context-vision-2026-09.md +17 -1
  156. package/docs/product-vision-2026-09.md +1082 -43
  157. package/docs/product-vision-review-2026-09.md +13 -1
  158. package/docs/screenshots/workbench-live-dark.png +0 -0
  159. package/docs/screenshots/workbench-live-light.png +0 -0
  160. package/docs/sdk.md +62 -2
  161. package/package.json +1 -1
  162. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
  163. package/plugins/pmx-canvas/plugin.json +1 -1
  164. package/plugins/pmx-canvas/runtime.json +1 -1
  165. package/plugins/pmx-canvas/tools.json +601 -22
  166. package/skills/pmx-canvas/SKILL.md +234 -43
  167. package/skills/pmx-canvas/evals/evals.json +62 -0
  168. package/skills/pmx-canvas/references/ax-html-control-surface.md +6 -0
  169. package/skills/pmx-canvas/references/full-reference.md +120 -21
  170. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +2 -2
  171. package/skills/pmx-canvas/references/importing-context.md +132 -0
  172. package/skills/pmx-canvas/references/installing-pmx-canvas.md +6 -6
  173. package/skills/pmx-canvas/references/tours-and-recording.md +16 -1
  174. package/skills/pmx-canvas-testing/SKILL.md +20 -0
  175. package/src/cli/agent.ts +2 -0
  176. package/src/cli/commands/ax.ts +12 -0
  177. package/src/cli/commands/boards.ts +87 -11
  178. package/src/cli/commands/edges.ts +28 -0
  179. package/src/cli/commands/groups.ts +7 -0
  180. package/src/cli/commands/nodes.ts +4 -2
  181. package/src/cli/commands/query.ts +34 -2
  182. package/src/cli/commands/record.ts +199 -48
  183. package/src/cli/index.ts +7 -1
  184. package/src/client/App.tsx +177 -70
  185. package/src/client/canvas/AttentionHistory.tsx +0 -2
  186. package/src/client/canvas/BarHint.tsx +40 -2
  187. package/src/client/canvas/BoardCopyDialog.tsx +1 -1
  188. package/src/client/canvas/BoardFolderDialog.tsx +7 -7
  189. package/src/client/canvas/BoardLinkDialog.tsx +138 -33
  190. package/src/client/canvas/BoardPinButton.tsx +33 -0
  191. package/src/client/canvas/BoardSwitcher.tsx +26 -40
  192. package/src/client/canvas/CanvasNode.tsx +360 -237
  193. package/src/client/canvas/CanvasViewport.tsx +45 -7
  194. package/src/client/canvas/CommandBar.tsx +29 -23
  195. package/src/client/canvas/CommandPalette.tsx +5 -12
  196. package/src/client/canvas/ConnectionBanner.tsx +3 -3
  197. package/src/client/canvas/ContextChip.tsx +296 -0
  198. package/src/client/canvas/ContextMenu.tsx +63 -5
  199. package/src/client/canvas/CrossBoardReferences.tsx +60 -0
  200. package/src/client/canvas/EdgeLayer.tsx +339 -76
  201. package/src/client/canvas/EmptyState.tsx +33 -18
  202. package/src/client/canvas/ExpandedNodeOverlay.tsx +72 -40
  203. package/src/client/canvas/ExternalWriters.tsx +57 -42
  204. package/src/client/canvas/HomeGraphView.tsx +942 -0
  205. package/src/client/canvas/HomeMapView.tsx +641 -0
  206. package/src/client/canvas/HomeReviewView.tsx +167 -0
  207. package/src/client/canvas/HomeView.tsx +687 -160
  208. package/src/client/canvas/IntentLayer.tsx +19 -12
  209. package/src/client/canvas/KnowledgeDialog.tsx +488 -0
  210. package/src/client/canvas/LifecycleChrome.tsx +133 -0
  211. package/src/client/canvas/LifecycleFields.tsx +91 -0
  212. package/src/client/canvas/LifecyclePanel.tsx +533 -0
  213. package/src/client/canvas/MemoryInspector.tsx +104 -0
  214. package/src/client/canvas/Minimap.tsx +3 -3
  215. package/src/client/canvas/NearPinLayer.tsx +187 -0
  216. package/src/client/canvas/NodeContextMark.tsx +175 -97
  217. package/src/client/canvas/Presentation.tsx +23 -57
  218. package/src/client/canvas/RelationPresetsDialog.tsx +413 -0
  219. package/src/client/canvas/RelationsPanel.tsx +82 -0
  220. package/src/client/canvas/SelectionBar.tsx +3 -6
  221. package/src/client/canvas/SessionPanel.tsx +90 -50
  222. package/src/client/canvas/SessionReceipt.tsx +311 -39
  223. package/src/client/canvas/ShortcutOverlay.tsx +1 -1
  224. package/src/client/canvas/SnapshotPanel.tsx +27 -36
  225. package/src/client/canvas/ToolRail.tsx +367 -192
  226. package/src/client/canvas/TopBar.tsx +354 -124
  227. package/src/client/canvas/canvas-area.ts +10 -3
  228. package/src/client/canvas/graph-geometry.ts +195 -0
  229. package/src/client/canvas/graph-model.ts +254 -0
  230. package/src/client/canvas/memory-layout.ts +106 -0
  231. package/src/client/canvas/use-chrome-height.ts +20 -0
  232. package/src/client/canvas/use-focus-trap.ts +31 -5
  233. package/src/client/canvas/use-memory-graph.ts +58 -0
  234. package/src/client/canvas/use-node-drag.ts +2 -1
  235. package/src/client/canvas/use-pan-zoom.ts +64 -7
  236. package/src/client/icons.tsx +0 -63
  237. package/src/client/mcp-app/style.css +0 -6
  238. package/src/client/nodes/AxStepControls.tsx +74 -50
  239. package/src/client/nodes/BoardNode.tsx +5 -1
  240. package/src/client/nodes/ContextNode.tsx +18 -11
  241. package/src/client/nodes/ExtAppFrame.tsx +0 -1
  242. package/src/client/nodes/FileNode.tsx +46 -42
  243. package/src/client/nodes/HtmlNode.tsx +44 -2
  244. package/src/client/nodes/ImageNode.tsx +1 -1
  245. package/src/client/nodes/InlineFormatBar.tsx +6 -9
  246. package/src/client/nodes/InlineMarkdownEditor.tsx +56 -17
  247. package/src/client/nodes/MarkdownNode.tsx +377 -109
  248. package/src/client/nodes/McpAppNode.tsx +50 -20
  249. package/src/client/nodes/MdFormatBar.tsx +21 -18
  250. package/src/client/nodes/PromptNode.tsx +1 -1
  251. package/src/client/nodes/ResponseNode.tsx +1 -1
  252. package/src/client/nodes/viewer-runtime-state.ts +54 -22
  253. package/src/client/state/attention-bridge.ts +64 -15
  254. package/src/client/state/boards-store.ts +193 -39
  255. package/src/client/state/canvas-store.ts +99 -12
  256. package/src/client/state/context-chip-store.ts +70 -0
  257. package/src/client/state/context-status-store.ts +53 -0
  258. package/src/client/state/intent-bridge.ts +51 -30
  259. package/src/client/state/knowledge-store.ts +43 -0
  260. package/src/client/state/near-pin-store.ts +115 -6
  261. package/src/client/state/presence-store.ts +7 -10
  262. package/src/client/state/presentation.ts +91 -0
  263. package/src/client/state/relation-store.ts +81 -0
  264. package/src/client/state/session-store.ts +144 -18
  265. package/src/client/state/sse-bridge.ts +206 -29
  266. package/src/client/state/workspace-settings.ts +11 -0
  267. package/src/client/theme/global.css +2759 -510
  268. package/src/client/types.ts +12 -0
  269. package/src/client/utils/keyboard.ts +10 -0
  270. package/src/client/utils/time-ago.ts +14 -0
  271. package/src/json-render/renderer/index.css +6 -1
  272. package/src/json-render/renderer/index.tsx +31 -1
  273. package/src/mcp/canvas-app.ts +12 -2
  274. package/src/mcp/context-uri-template.ts +7 -4
  275. package/src/mcp/server.ts +63 -41
  276. package/src/mcp/workbench-app.ts +39 -6
  277. package/src/server/agent-presence.ts +128 -28
  278. package/src/server/ax-context.ts +27 -13
  279. package/src/server/board-export.ts +12 -1
  280. package/src/server/canvas-db.ts +331 -42
  281. package/src/server/canvas-operations.ts +196 -84
  282. package/src/server/canvas-provenance.ts +53 -22
  283. package/src/server/canvas-serialization.ts +2 -0
  284. package/src/server/canvas-state.ts +950 -122
  285. package/src/server/canvas-validation.ts +9 -2
  286. package/src/server/context-brief.ts +345 -38
  287. package/src/server/context-reads.ts +179 -30
  288. package/src/server/demo-state.json +355 -24
  289. package/src/server/edit-summary.ts +74 -0
  290. package/src/server/html-surface.ts +8 -1
  291. package/src/server/index.ts +203 -13
  292. package/src/server/knowledge-lifecycle.ts +304 -0
  293. package/src/server/lifecycle-status.ts +232 -0
  294. package/src/server/memory-graph.ts +166 -0
  295. package/src/server/mutation-history.ts +18 -6
  296. package/src/server/operations/composites.ts +32 -11
  297. package/src/server/operations/http.ts +7 -2
  298. package/src/server/operations/index.ts +4 -0
  299. package/src/server/operations/mcp.ts +32 -1
  300. package/src/server/operations/ops/ax-presence.ts +1 -1
  301. package/src/server/operations/ops/ax-read.ts +57 -6
  302. package/src/server/operations/ops/ax-timeline.ts +28 -2
  303. package/src/server/operations/ops/backup.ts +5 -3
  304. package/src/server/operations/ops/batch.ts +35 -4
  305. package/src/server/operations/ops/boards.ts +188 -47
  306. package/src/server/operations/ops/canvas-wire.ts +31 -1
  307. package/src/server/operations/ops/edges.ts +36 -2
  308. package/src/server/operations/ops/groups.ts +6 -1
  309. package/src/server/operations/ops/json-render.ts +5 -1
  310. package/src/server/operations/ops/lifecycle.ts +262 -0
  311. package/src/server/operations/ops/memory-graph.ts +45 -0
  312. package/src/server/operations/ops/nodes.ts +107 -24
  313. package/src/server/operations/ops/query.ts +281 -68
  314. package/src/server/operations/ops/snapshots.ts +73 -1
  315. package/src/server/operations/ops/validate.ts +5 -1
  316. package/src/server/operations/ops/viewport.ts +97 -3
  317. package/src/server/operations/registry.ts +77 -26
  318. package/src/server/placement.ts +29 -0
  319. package/src/server/scope-fence.ts +78 -6
  320. package/src/server/server.ts +115 -16
  321. package/src/server/spatial-analysis.ts +48 -23
  322. package/src/server/tour-control.ts +82 -0
  323. package/src/server/wiki-links.ts +137 -0
  324. package/src/server/workbench-auth.ts +11 -0
  325. package/src/shared/agent-context.ts +3 -0
  326. package/src/shared/agent-presence.ts +15 -15
  327. package/src/shared/auto-arrange.ts +2 -0
  328. package/src/shared/boards.ts +4 -4
  329. package/src/shared/knowledge-lifecycle.ts +22 -0
  330. package/src/shared/knowledge-presets.ts +80 -0
  331. package/src/shared/lifecycle-status.ts +26 -0
  332. package/src/shared/memory-graph.ts +74 -0
  333. package/src/shared/relations.ts +98 -0
  334. package/src/shared/semantic-attention.ts +15 -1
  335. package/src/shared/tour.ts +23 -5
  336. package/src/shared/workspace-settings.ts +13 -0
  337. package/dist/types/client/canvas/ContextPinBar.d.ts +0 -1
  338. package/dist/types/client/canvas/FocusFieldLayer.d.ts +0 -1
  339. package/dist/types/client/canvas/kind-colors.d.ts +0 -3
  340. package/dist/types/server/agent-context.d.ts +0 -22
  341. package/src/client/canvas/ContextPinBar.tsx +0 -25
@@ -384,16 +384,23 @@ draw visual attention to critical paths.
384
384
 
385
385
  ### Layout: spacing and groups
386
386
 
387
- Agents tend to pack boards too tightly. Give nodes room to breathe — readability beats density.
388
-
389
- - **Default spacing:** leave a clear gap between neighbors — roughly half a node's width
390
- horizontally and half its height vertically. A 280×180 node reads well with ~360px between
391
- column origins and ~260px between row origins.
392
- - **When nodes are connected by edges, space them further apart** so the edge line, its arrowhead,
393
- and its label are clearly visible in the gap between nodes. Crowded nodes hide the flow — this is
394
- the most common cause of an unreadable board.
395
- - **Inside a group, keep the same breathing room** (groups no longer auto-pack children, so the
396
- spacing you set is what the human sees). Edges between grouped children especially need the gap.
387
+ Use the smallest clear gap that serves the content and its relationships. Both crowded labels
388
+ and oversized empty channels make a board harder to use.
389
+
390
+ - **Unconnected notes and walkthroughs:** start compact, often a 40–80px clear gap. For a
391
+ 280×180 node with a 40px gap, origins are 320px apart horizontally and 220px vertically.
392
+ This is an unconnected-card example, not a minimum for connected cards. Do not reserve
393
+ half a card's width or a large uniform channel where there is no edge.
394
+ - **Connected cards:** provide enough room for the rendered edge line, arrowhead, label and
395
+ authorship marks. Short flow labels may need less room than long explanations. After content settles,
396
+ inspect the rendered relations at the intended viewing zoom, including after auto-arrange.
397
+ `validate` checks node non-overlap, not label/arrowhead clearance or relation readability;
398
+ widen or reroute crowded relations rather than relying on a fixed pixel gap.
399
+ - **After rendering:** use settled card sizes, not just the requested creation heights. Close
400
+ gaps left by automatic shrinking, check the numbered reading order, and frame a readable
401
+ cluster or tour stop. A whole-board fit is orientation, not proof that body text is readable.
402
+ - **Inside a group, apply the same distinction** between compact notes and clear relation
403
+ channels. Groups no longer auto-pack children, so the spacing you set is what the human sees.
397
404
  - **Size a group frame larger than its children's bounding box** so the group header — including the
398
405
  node-count badge — stays visible and isn't hidden under the top-left child. For an explicit
399
406
  (manual) group frame, add margin on every side (≈56px) plus room at the top for the header rather
@@ -438,15 +445,15 @@ single-purpose tools behind an `action` (and, for `canvas_ax_gate`, a `kind`) di
438
445
  | Composite | `action` values | What it does |
439
446
  |-----------|-----------------|--------------|
440
447
  | `canvas_import` | `list` · `read` · `submit` · `unavailable` | Process human-requested imports: inspect metadata/controlled bytes, submit reviewable Markdown with references/warnings/agent description, or report unavailable. Request/cancel/commit are human-only |
441
- | `canvas_node` | `add` · `get` · `update` · `remove` | Create / read / mutate / delete a node. **`add` covers html + primitives too**: `{ action:"add", type:"html", html:"…" }` and `{ action:"add", type:"html", primitive:"choice-grid", data:{} }` — no separate add-html tool needed |
448
+ | `canvas_node` | `add` · `get` · `update` · `remove` · `lifecycle-get` · `lifecycle-set` · `review` · `lifecycle-restore` | Node CRUD, lifecycle qualification, evidence-backed review and reversible retirement; retired cards leave active context |
442
449
  | `canvas_render` | `describe-schema` · `validate` · `add-json-render` · `stream-json-render` · `add-graph` · `workboard` | Schema introspection, spec dry-run validation, native json-render / graph node creation, and a live AX work-item board |
443
450
  | `canvas_edge` | `add` · `update` · `remove` | Connect / edit / disconnect nodes |
444
451
  | `canvas_group` | `create` · `add` · `ungroup` | Manage spatial group containers |
445
452
  | `canvas_history` | `undo` · `redo` | Time travel through the mutation ring buffer |
446
- | `canvas_board` | `list` · `get` · `create` · `create-from` · `open` · `update` | Boards, shared-workbench switching, README designation, links/backlinks, and inactive copies |
447
- | `canvas_snapshot` | `save` · `list` · `restore` · `delete` · `gc` · `diff` | Named snapshots: save/list/restore/delete, garbage-collect old ones, diff current canvas vs a snapshot (`diff` takes `snapshot`, not `id`) |
448
- | `canvas_view` | `arrange` · `focus` · `fit` · `clear` · `remove-annotation` · `get-tour` · `set-tour` | Auto-arrange, pan-to-node, fit viewport, clear the board, delete annotations, read/persist ordered presentation stops (`tour: null` derives group order) |
449
- | `canvas_query` | `context` · `search` · `layout` · `validate` | Read the budgeted cross-board brief, find nodes by keyword, read full layout, or **`validate`** the board for node collisions / group-containment / dangling edges |
453
+ | `canvas_board` | `list` · `get` · `create` · `create-from` · `open` · `update` · `pin` · `unpin` · `knowledge-list` · `category-set` · `category-remove` · `folder-set` · `relation-preset-set` · `relation-preset-remove` | Boards and workspace categories/folder properties/shared relation templates; knowledge writes never open a board. Template edits/deletion never rewrite boards or connections. `category-remove` takes `id` and refuses explicit folder assignments, including empty folders; reassign or clear them first. |
454
+ | `canvas_snapshot` | `save` · `list` · `restore` · `restore-node` · `delete` · `gc` · `diff` | Named snapshots: save/list/restore/delete, garbage-collect old ones, diff current canvas vs a snapshot (`diff` takes `snapshot`, not `id`); `restore-node` puts one card (`nodeId`) back to its content in snapshot `id` |
455
+ | `canvas_view` | `arrange` · `focus` · `fit` · `clear` · `remove-annotation` · `get-tour` · `set-tour` · `tour-go` · `tour-exit` · `move` | Auto-arrange, pan-to-node, fit viewport, clear the board, delete annotations, read/persist ordered presentation stops (`tour: null` derives group order), present a stop / exit, ease the camera to a node, rect or viewport (never an undo step) |
456
+ | `canvas_query` | `context` · `graph` · `search` · `layout` · `validate` | Read the budgeted brief or summary-only connected memory graph, find nodes, read full layout, or **`validate`** collisions / containment / dangling edges |
450
457
  | `canvas_app` | `open-mcp-app` · `diagram` · `build-artifact` | Hosted MCP apps, the Excalidraw diagram preset, and bundled web artifacts (folds `canvas_open_mcp_app` / `canvas_add_diagram` / `canvas_build_web_artifact`) |
451
458
  | `canvas_webview` | `status` · `start` · `stop` · `resize` · `evaluate` | Headless Bun.WebView automation for the workbench (folds the `canvas_webview_*` / `canvas_resize` / `canvas_evaluate` tools) |
452
459
  | `canvas_ax_state` | `get` · `set-focus` · `set-policy` · `report-capability` · `presence` · `set-presence` | Read AX state; set AX focus; patch tool/prompt policy (the scope fence is human-set, read-only here); report host capability; read / set agent presence (phase, cursor, focus, attached session) |
@@ -484,6 +491,88 @@ reads inactive boards without opening them. Board links are native text-only
484
491
  creation: `prompt`, `response`, `trace`, and `mcp-app` are excluded; README and
485
492
  group structure are optional; fresh IDs are allocated; pins, AX state, and
486
493
  history are not copied; the source remains unchanged and the new board inactive.
494
+ The new board records `previousBoardId` pointing to its source.
495
+
496
+ ### Connected memory (wave 2)
497
+
498
+ Home Folders, Map and Graph, wiki links/backlinks, and brief discovery share one
499
+ board/card projection. Folders are neutral and independent of links. Map uses
500
+ deterministic folder regions and saved draggable SQLite `mapPosition`; board
501
+ update accepts `{ x, y }` or `null` to reset. Graph uses neutral circular clusters,
502
+ not force simulation; labels are in selection details, filters in the shared
503
+ toolbar, with no synthetic Home board, levels, or simulation sliders.
504
+
505
+ Read it with `canvas_query { action: "graph", focus: "pins", depth: 2 }`,
506
+ `canvas://graph{?focus,depth}`, `GET /api/canvas/memory-graph`, SDK
507
+ `getMemoryGraph({ focus, depth })`, or CLI `pmx-canvas memory-graph`. Focus is
508
+ a board ID, a JSON qualified key `["board-id","card-id"]` (or
509
+ `["board-id",null]`), or `pins`; omitted means the library. URL-encode resource
510
+ focus. Depth is an integer 0–4, default 2, traversing undirected links including
511
+ membership. Explicit 0 is valid; empty depth and bare CLI `--depth` are rejected.
512
+ HTTP, SDK and the MCP tool accept `projection: "full" | "boards"` (default `full`);
513
+ CLI uses `--projection full|boards`. The graph resource uses the default full projection.
514
+ Home Map uses compact `boards` summaries, loading depth-2 details on selection.
515
+ Metadata edits and dragging do not refetch relationships; **Refresh** reloads them.
516
+ Cross-board briefs give incoming and outgoing connections readable board/card titles.
517
+ The result has `nodes`, `edges`, `unresolvedLinks: [{ source, ref }]`,
518
+ `focus`, and `depth`. Nodes include qualified `key`, `boardId`, optional `cardId`,
519
+ `kind`, `title`, `summary`, `folder`, `inContext`, and `summaryOnly: true`;
520
+ summaries are capped at 280 characters. Edge projection kinds are `member`,
521
+ `portal`, `wiki`, `card`, and `previous`, with qualified `from`/`to`,
522
+ `source`/`target`, and explicit `missing`. Reads count links seen only when a node's
523
+ incident relations are complete; focused nodes with `linksComplete: false` do
524
+ not acknowledge omitted links. Brief entries with capped or omitted relations also
525
+ carry `linksComplete: false`; near-pin, human, linked README and pinned-board
526
+ summaries do not acknowledge unseen relations. Graph cards and complete brief
527
+ relations carry their captured `linksRevision`, so later writes remain unseen;
528
+ brief delivery is qualified by source board, never by a bare cross-board card ID.
529
+ AX excerpts and annotation anchors, including `canvas://ax-context` and
530
+ `pmx-current-context`, never mark incident relations seen; read a complete graph
531
+ or brief relation entry instead.
532
+ Single-card and layout reads record content delivery, not incident relations.
533
+ Reads never switch boards or record full content reads. Pull cards in full
534
+ when needed. Counts stay 24 tools, 15 static canvas resources; there are now two
535
+ canvas templates, context and graph, besides the skill file template.
536
+
537
+ Markdown save resolves `[[Board]]`, `[[Board#Card]]`, and `|alias` forms into
538
+ server-owned `data.wikiLinks` ID bindings. IDs win; name/title matches must be
539
+ unique. Existing bindings survive renames. Ambiguous/missing links stay explicit
540
+ and never guess or silently retarget. Rendering follows saved bindings: unresolved
541
+ links stay unresolved until an explicit save, and Undo/restore preserves bindings.
542
+ Legacy Markdown without binding metadata gets a one-time persisted backfill;
543
+ saved empty or unresolved records are not reinterpreted on reads.
544
+ Code spans/fences are ignored. A human
545
+ Create dialog for an unresolved board-only link creates exactly the authored
546
+ name and re-saves its source using the captured board/revision, refusing stale
547
+ source writes.
548
+
549
+ Once the board list has loaded, all workbench context-pin writes send the captured
550
+ `expectedBoardId`; a server-side board switch returns 409 instead of changing
551
+ another board's pins. Receipt pin actions require reopening the owning board.
552
+
553
+ Embedded JSON/graph runtime readiness waits up to 10 seconds after document load
554
+ and retries a failed handoff once while retaining the last painted document.
555
+ After another failure, the error toast/history say **Reload to retry**; a later
556
+ healthy authored update can recover. This is not an overall network-load timeout.
557
+
558
+ Native edge add accepts optional `toBoardId`: `from` is on the open source board,
559
+ `to` is a card on the target board. CLI: `pmx-canvas edge add --from <source> --to <target>
560
+ --to-board <board-id> --type references`. The human More menu offers a card
561
+ picker and the source footer shows remote connections. Auto-arrange stays local;
562
+ static export omits remote lines but shows explicit external connection evidence.
563
+ Edges accept open `kind`/`reason`; update null clears them. Boards accept
564
+ `relationVocabulary: [{kind, ink, directed}]`, with six fixed relation inks, never pin/agent/warning
565
+ colours. `derived-from`, `contradicts`, `supersedes` point source → dependent; `depends-on` points
566
+ dependent → prerequisite. Ordinary references/wiki links/portals do not propagate review due.
567
+ Lifecycle reads return qualification, including source observations. Validating dependent cards
568
+ requires `expectedSources` from the evidence checked, plus `expectedContentRevision`; stale or
569
+ unavailable sources refuse. Retired cards leave briefs/pinned context but remain marked in search
570
+ and direct reads. Brief `lifecycle.complete: false` requires rechecking cached cards before reuse;
571
+ empty incomplete lists are not a health claim. Recipe metadata is inert and refresh never validates.
572
+ For human review, Home's Review needed count filters to affected boards, and each Review button
573
+ opens an affected card. More → Review offers one short note and Checked, still correct; Not now
574
+ writes nothing. Settings and history discloses clocks, policies and advanced outcomes. The folder
575
+ tree is navigation, and No review policy is informational rather than an outstanding task.
487
576
 
488
577
  Use `canvas_board { action: "open", id: "<board-id>" }` to open a created or
489
578
  existing board before authoring. Verify the returned `activeBoardId`; opening
@@ -869,7 +958,7 @@ Useful workbench selectors:
869
958
  the eraser in the rail's Annotate popover; agents can remove a known annotation ID with
870
959
  `canvas_view { action: "remove-annotation" }`.
871
960
  - Canvas chrome (rail-chrome-v2): `.tool-rail`, `.top-bar` (`.connection-dot`, `.agent-chip`,
872
- `.gate-badge`, `.context-budget`, `.external-indicator`, `.start-session-btn`),
961
+ `.gate-badge`, `.context-chip`, `.external-indicator`, `.start-session-btn`),
873
962
  `.connection-banner`, `.canvas-region`, `.session-panel`, `.command-bar`, `.selection-bar`,
874
963
  `.activity-feed`, `.writers-sheet`, `.session-receipt`, `.snapshot-panel` (the History
875
964
  drawer), `.minimap`, `.empty-state`, `.command-palette`, `.expanded-overlay-panel`.
@@ -1042,6 +1131,9 @@ what the human has set up and what they're focusing on.
1042
1131
 
1043
1132
  | Resource | What it provides |
1044
1133
  |----------|-----------------|
1134
+ | `canvas://context{?budget,consumer,since}` | Budgeted cross-board map brief with optional durable consumer cursor |
1135
+ | `canvas://graph{?focus,depth}` | Summary-only connected memory projection, library or focused undirected membership-inclusive exploration |
1136
+ | `canvas://boards` | Board library with README summaries, links/backlinks and previews |
1045
1137
  | `canvas://pinned-context` | Content of pinned nodes + nearby unpinned neighbors |
1046
1138
  | `canvas://schema` | Running-server create schemas and json-render catalog metadata |
1047
1139
  | `canvas://layout` | Full canvas state (viewport, nodes, edges) |
@@ -1165,10 +1257,16 @@ nodes.
1165
1257
  - **Session receipt:** attaching a session (`set-presence { attached: true }` or a
1166
1258
  `session-start` activity) over a non-empty board saves a `Before session · <label> · HH:MM`
1167
1259
  snapshot; ending it (`attached: false`, `session-end`, or idle expiry) emits one
1168
- `agent-session-ended` SSE frame — `{ label, endedAt, counts: { items, done, vetoed },
1169
- snapshot }` — which the browser shows as a receipt with *View diff* (the session's own
1170
- changes) and a restore path. End your session explicitly so the receipt is prompt. The
1171
- human's *Start agent session* button is the same attach with `source: "browser"`.
1260
+ `agent-session-ended` SSE frame with `label`, `endedAt`, `snapshot`, `context` and counts
1261
+ (`items`, `done`, `cancelled`, `rejected`, `held`). Changed top-level endings show a receipt;
1262
+ worker and unchanged endings stay in History, and qualifying endings merge into an open receipt.
1263
+ **See change** opens one stacked Before/After; whole-card Undo preserves geometry and links.
1264
+ Receipt Undo sends `preserveHumanEdits: true` to `restore-node`, so the server skips latest-human
1265
+ edits even when the browser is stale. Agent callers can pass that option to `canvas_snapshot`
1266
+ too: `restored: false, skipped: "human-edit"` is not an undone card. Receipt Unpin only removes
1267
+ its recorded pins, never re-pins them. Reopen a receipt's board before Undo or comparison;
1268
+ Home dismisses it. End your session explicitly so the receipt is prompt. The human's
1269
+ *Start agent session* button is the same attach with `source: "browser"`.
1172
1270
  - **Human collaborators / user wins:** every open workbench tab is a presence (`GET
1173
1271
  /api/canvas/human-presence`); a node a human is dragging is locked — an agent write to it is
1174
1272
  refused with 409 until release (requeue and retry), and a pending explicit intent on it is
@@ -1340,7 +1438,7 @@ All POST/PATCH endpoints accept `Content-Type: application/json`. Default base U
1340
1438
  | POST | `/api/canvas/snapshots` | Save snapshot |
1341
1439
  | POST | `/api/canvas/snapshots/<id>` | Restore snapshot |
1342
1440
  | DELETE | `/api/canvas/snapshots/<id>` | Delete snapshot |
1343
- | POST | `/api/canvas/context-pins` | Replace pinned nodes |
1441
+ | POST | `/api/canvas/context-pins` | Set, add or remove pinned nodes (20 live pins; removal IDs are not capped) |
1344
1442
  | GET | `/api/canvas/pinned-context` | Get current pins with neighborhood context |
1345
1443
  | GET | `/api/canvas/search?q=...` | Search nodes |
1346
1444
  | POST | `/api/canvas/json-render` | Create a native json-render node |
@@ -1365,6 +1463,7 @@ All POST/PATCH endpoints accept `Content-Type: application/json`. Default base U
1365
1463
  | POST | `/api/canvas/redo` | Redo |
1366
1464
  | GET | `/api/canvas/history` | Mutation history |
1367
1465
  | GET | `/api/canvas/code-graph` | File dependency graph |
1466
+ | GET | `/api/canvas/memory-graph` | Summary-only board/card projection; optional focus and depth |
1368
1467
  | GET | `/api/workbench/events` | SSE event stream |
1369
1468
 
1370
1469
  ## Workflow Patterns
@@ -47,8 +47,8 @@ panel.
47
47
 
48
48
  The extension also reports Copilot's real context window: the SDK's `session.usage_info`
49
49
  event (`currentTokens` / `tokenLimit`, root agent only) is posted to the presence as
50
- `contextUsage`, so the board's top-bar meter reads **Context** with the live numbers rather
51
- than the pinned-context estimate (**Pins**).
50
+ `contextUsage`, so the board's context panel names how many tokens Copilot's whole context holds,
51
+ next to what the brief costs.
52
52
 
53
53
  The extension attaches a Copilot session to the canvas when it joins the host session, flips the
54
54
  phase to `thinking` on every submitted prompt, and detaches on exit. While attached the workbench
@@ -0,0 +1,132 @@
1
+ # Importing context
2
+
3
+ Use this workflow when bringing existing context into PMX Canvas from folders, repositories,
4
+ documents, notes, backlogs, structured exports or knowledge bases. It guides organization and
5
+ verification, not a universal file converter or live sync. PDF/Office attachments still require
6
+ the human-consented extraction and review workflow; this guide does not bypass it.
7
+
8
+ ## Inspect the source before choosing the structure
9
+
10
+ Inventory the requested scope, file types, existing categories, references and assets. Read
11
+ representative content before mapping it: folder names alone do not establish board boundaries,
12
+ and files may contain several independent items or one argument that should stay together.
13
+ Identify duplicates, historical material, unsupported formats and inaccessible sources. Report
14
+ gaps rather than silently skipping them or claiming extraction succeeded. Keep private context
15
+ within the authorized destination; a folder import is not permission to include credentials.
16
+
17
+ ## Choose boards and cards by purpose
18
+
19
+ Categories organize boards; boards hold coherent topics or workflows; cards hold useful content.
20
+ Preserve useful categories and folder context without mechanically copying the directory tree.
21
+ A project folder may become an overview board, a backlog with item cards, and a research board.
22
+ Several small files may belong on one board; a single export may need several boards. Choose
23
+ cards by useful units of meaning, not automatically one per file, heading or data row. Retain
24
+ section context, nested details and useful source ordering when splitting content. These are
25
+ examples, not a fixed taxonomy. Explain consequential grouping choices and make them easy to revise.
26
+ Several boards in one category are fine. Keep an argument together when splitting would lose
27
+ meaning. Group related cards and use the main skill's README and focus guidance for a readable start.
28
+
29
+ Lead each card with the task, item, decision or document section the reader came to find.
30
+ Retain ancestor headings, folder/repository context and source details below that content,
31
+ not as repeated preambles that push every useful item below the card's visible area. Preserve
32
+ meaning and source hierarchy without making the reader expand every card to identify it.
33
+
34
+ ## Preserve sources and stable targets
35
+
36
+ Work in an isolated workspace for a test import. Preserve an existing import before replacing
37
+ it, and leave source material unchanged unless editing it was requested. Record source paths,
38
+ URLs or record IDs, revisions where available, and the source-item/section → board/card ID
39
+ mapping so interruptions and retries can reuse created objects instead of duplicating them.
40
+ Distinguish faithful imports from summaries; keep a path back to the original. Capture the active
41
+ board before writes, separately from any explicit destination. Create destination boards and
42
+ cards before rewriting internal references, use returned IDs rather than guessed titles, and
43
+ preserve external links.
44
+
45
+ Create supported static cards, groups and edges on an inactive board with `destinationBoardId`
46
+ without switching the human's view. Static nodes are Markdown, status, context, ledger, trace,
47
+ diff, Mermaid and plain HTML; images, runtime-backed nodes, generated primitives and intents
48
+ are not supported by this path. Updates/removals still require the active board. In a named
49
+ `canvas_batch`, each operation's `args` carries its destination and retry fields; group children
50
+ and edge endpoints can use the existing named references. `toBoardId` independently chooses a
51
+ remote edge endpoint, not the board that owns its source.
52
+
53
+ Assign a stable `requestId` to each logical static node/group/edge creation and retain it in
54
+ the source-to-canvas mapping. Retry with the same input and destination: the durable receipt
55
+ returns the original result after interruption or restart, even if the object was subsequently
56
+ edited or deleted. It does not restore deleted objects. Changed-input reuse conflicts; a new
57
+ intentional creation gets a new ID even when its content is identical. Batch operations commit
58
+ in sequence, not as one all-or-nothing import. Persist the mapping as receipts arrive.
59
+
60
+ `expectedBoardId` guards the active board even with an explicit destination. A stale guard
61
+ means refresh context, not substitute the destination ID or switch the human's view to retry.
62
+ For active-only work, explicitly open the intended board and verify its returned ID first.
63
+ Scope fences and human edit locks remain enforced. If Home is active, list/open a suitable
64
+ board before reading board context; library discovery and background static creation can
65
+ stay on Home. For retrieval, `canvas_query` search accepts intersecting `boardId` and `folder`
66
+ filters. Folder includes slash-boundary descendants; `folder: ""` selects unfiled boards.
67
+ Filters default omitted scope to library, while explicit active scope never widens. Search
68
+ never changes the active board.
69
+
70
+ Use native `references` edges for explicit source links and qualified remote endpoints for
71
+ cross-board cards. Add semantic relations only when the source supports them; record the
72
+ supporting passage or source reference in `reason`. Similar subjects alone do not establish
73
+ dependency, causation or supersession. Follow the main skill's direction rules for dependency kinds.
74
+
75
+ Preserve historical/draft status and source evidence dates. Importing, reading or rendering
76
+ content does not revalidate it. Flag source contradictions separately from conversion errors
77
+ and product bugs; do not silently rewrite uncertain facts or invent validation dates.
78
+
79
+ ## Markdown link conversion
80
+
81
+ For content represented as Markdown, use returned IDs in `[[board-id#card-id|label]]` links
82
+ and keep code spans/fences unchanged. In table cells, escape the alias pipe:
83
+ `[[board-id#card-id\|label]]`. Otherwise the table parser can split the link before the wiki
84
+ link parser sees it. Zero unresolved links is not sufficient: compare expected source link
85
+ occurrences with rendered anchors. This is a Markdown rendering concern, not a source-format requirement.
86
+
87
+ ## Keep picture bytes out of long Markdown
88
+
89
+ Prefer local asset references over large base64 image strings embedded in Markdown. The
90
+ embedded bytes enlarge layout/render requests; inline pictures remain supported. Choosing
91
+ file-backed assets is a representation choice, not proof that a parser or loading bug is fixed.
92
+ Keep private pictures in the authorized workspace; do not upload them to a public image host.
93
+
94
+ One supported approach is a file-backed image card in the document's board:
95
+
96
+ ```json
97
+ {"action":"add","type":"image","content":"assets/diagram.png","title":"System diagram"}
98
+ ```
99
+
100
+ Call `canvas_node` with that payload, then use the returned ID in the document:
101
+ `![System diagram](/api/canvas/image/<image-node-id>)`. Keep the original alt text and connect
102
+ the document to its figure card when useful. This route resolves nodes on the active board:
103
+ keep the image card in the same board, and recheck references after copying or moving cards.
104
+
105
+ Relative image creation paths resolve against the canvas workspace root, not the server's
106
+ launch directory. An absolute path inside that workspace also works. Image creation requires
107
+ opening its destination board; do not send `destinationBoardId` or `requestId` for image nodes.
108
+ The database stores the path, not the file bytes: retain the assets alongside it, update paths
109
+ if relocating the workspace, and verify images in exports rather than assuming the live URL
110
+ is portable.
111
+
112
+ ## Verify the imported workspace, not just the API receipts
113
+
114
+ 1. Account for each in-scope source item as imported, summarized, intentionally excluded or
115
+ unresolved. Reconcile split items, references and assets with the mapping. Check edge endpoints,
116
+ group containment and collisions with `canvas_query { action: "validate" }`.
117
+ 2. Open representative boards for the content actually imported: for example a dense backlog,
118
+ long document, linked table or picture-heavy board. Wait for the correct board and populated
119
+ card bodies, not only titles or HTTP success. Images must load with nonzero natural dimensions.
120
+ 3. Wait for the intended card/group bounding rectangle after a camera update. Two animation
121
+ frames do not prove an asynchronous layout update arrived. Inspect screenshots at the
122
+ intended viewing zoom for readable cards, relations, labels and unobscured controls.
123
+ 4. Click local and cross-board links; verify the intended card becomes visible. On a disposable
124
+ test service, restart and verify persistence, including pictures. Compare asset bytes when
125
+ changing image representation to confirm originals were preserved.
126
+ 5. If loading is slow, measure board-open to rendered-content readiness on repeated equivalent
127
+ workloads. Include a text-only control; record payload sizes, cache conditions and whether
128
+ measurements used a local connection or the user's host. Separate observations from causes.
129
+
130
+ Report coverage, unresolved issues and storage requirements. Distinguish an import workaround
131
+ from a product fix, and a source snapshot from live synchronization. Keep private source paths,
132
+ names and content out of reusable skills and public issue reports.
@@ -4,7 +4,7 @@ Use this reference when the `pmx-canvas` skill is installed but the `pmx-canvas`
4
4
 
5
5
  ## Prerequisites and version
6
6
 
7
- The examples pin **PMX Canvas 0.8.0**, which requires **Bun >=1.4.2** even when
7
+ The examples pin **PMX Canvas 0.9.0**, which requires **Bun >=1.4.2** even when
8
8
  installed through npm: the CLI runs through Bun, not Node. Check `bun --version`
9
9
  first. If missing or older, follow the [Bun installation instructions](https://bun.sh/docs/installation)
10
10
  with the user's authorization. Ensure Bun is on the MCP host's PATH as well as the
@@ -17,11 +17,11 @@ in `SKILL.md`. Do not pair newer checkout guidance with an older runtime unknowi
17
17
  ## Install from npm
18
18
 
19
19
  ```bash
20
- npm install -g pmx-canvas@0.8.0
20
+ npm install -g pmx-canvas@0.9.0
21
21
  pmx-canvas --version
22
22
  ```
23
23
 
24
- Without a global install, use `bunx pmx-canvas@0.8.0` in place of `pmx-canvas` in
24
+ Without a global install, use `bunx pmx-canvas@0.9.0` in place of `pmx-canvas` in
25
25
  the commands below. Do not use unpinned `bunx pmx-canvas` for a version-specific check.
26
26
 
27
27
  ## Choose the workspace and target
@@ -91,7 +91,7 @@ discards empty env values, remove `PMX_CANVAS_URL` from its launch environment i
91
91
  "mcpServers": {
92
92
  "canvas": {
93
93
  "command": "bunx",
94
- "args": ["pmx-canvas@0.8.0", "--mcp"],
94
+ "args": ["pmx-canvas@0.9.0", "--mcp"],
95
95
  "env": {
96
96
  "PMX_CANVAS_WORKSPACE_ROOT": "/absolute/path/to/project",
97
97
  "PMX_CANVAS_PORT": "14313",
@@ -135,7 +135,7 @@ Amp executes, not only on the user's laptop. Merge this into that workspace's
135
135
  "amp.mcpServers": {
136
136
  "canvas": {
137
137
  "command": "bunx",
138
- "args": ["pmx-canvas@0.8.0", "--mcp"],
138
+ "args": ["pmx-canvas@0.9.0", "--mcp"],
139
139
  "includeTools": ["canvas_*"],
140
140
  "env": {
141
141
  "PMX_CANVAS_WORKSPACE_ROOT": "/absolute/path/to/project",
@@ -169,7 +169,7 @@ The agent must not run this approval itself or move the command to global settin
169
169
  `--mcp-config` to bypass workspace trust. Approval authorizes execution of this MCP
170
170
  command; it is not a new Canvas import consent or permission to mutate arbitrary boards.
171
171
  After approval, reload MCP connections (`reload_mcp` in Amp) and require `connected`.
172
- Discover `canvas` tools with `tool_search`; for the full unfiltered 0.8.0 server there
172
+ Discover `canvas` tools with `tool_search`; for the full unfiltered 0.9.0 server there
173
173
  are 24 tools. If still absent, inspect the server status, command/PATH and target instead
174
174
  of reinstalling blindly. See [Amp MCP documentation](https://ampcode.com/docs/customize/mcp).
175
175
 
@@ -19,10 +19,11 @@ Persist a tour with `canvas_view { action: "set-tour", tour: { stops: [...] } }`
19
19
  ```
20
20
 
21
21
  - `nodeId` may identify a card or group. Its persisted position and size determine the camera.
22
+ - `rect` is a world-space box `{ x, y, width, height }` (top-left corner plus size).
22
23
  - An explicit viewport is `{ x, y, scale }`, with positive `scale`. Translation is screen-space:
23
24
  `screen = world * scale + offset`.
24
25
  - `duration` is transition time in seconds (default 1; zero jumps). Valid values are 0–3600.
25
- - `padding` is screen pixels around a node target (default 40).
26
+ - `padding` is screen pixels around a node or rect target (default 40).
26
27
  - `easing` is `linear`, `ease-in-out` (default), or `ease-out`.
27
28
  - `pullback` is 0–4 (default 0). It zooms out during the move without changing either endpoint.
28
29
  - A tour contains at most 1000 stops.
@@ -39,6 +40,20 @@ ID. A saved `{ "stops": [] }` intentionally has no stops; it does not enable gro
39
40
  Preserve the tour of each board when working across boards and target the intended board before
40
41
  setting or reading it.
41
42
 
43
+ ## Drive a tour
44
+
45
+ The server holds one in-memory tour cursor, tied to the board it was started on (a tour on another board replaces it). `canvas_view { action: "tour-go", stop: 0 }`
46
+ starts presenting in open workbenches; `step: "next" | "previous"` moves relative to wherever the
47
+ audience is (a human's arrow keys move the same cursor) and clamps at the ends. Pass
48
+ `present: false` to move only viewers already presenting. `canvas_view { action: "tour-exit" }`
49
+ leaves presentation everywhere. `get-tour` reports `position` (the current stop, or null).
50
+
51
+ `canvas_view { action: "move", nodeId | rect | viewport, duration, easing, padding, pullback }`
52
+ eases the camera without a tour. Outside a presentation the end camera becomes the board
53
+ viewport. Tour steps and moves never enter undo history, never count as agent writes, and are
54
+ allowed under a scope fence. Viewers with reduced motion jump instead of animating; a
55
+ deterministic capture ignores both.
56
+
42
57
  ## Capture prerequisites and behavior
43
58
 
44
59
  Start and verify the matching PMX daemon first: its `/health` workspace and version must match the
@@ -53,6 +53,10 @@ and check `$?`, then read the file.
53
53
  - Test-only changes: run `bun run test` and `bun run test:coverage` if coverage matters
54
54
  - Client/UI/browser interaction changes: run `bun run test:web-canvas`
55
55
  - Cross-stack or non-trivial changes: run `bun run test:all`
56
+ - Bundled skill/documentation-only changes: run
57
+ `bun test tests/unit/bundled-skills.test.ts tests/unit/no-native-tooltips.test.ts`
58
+ and `git diff --check`; inspect public-contract prose against source. No client rebuild or
59
+ full browser suite is needed when executable behavior has not changed.
56
60
  - Before changing browser-visible behavior under `src/client/`: rebuild with `bun run build`
57
61
  Manual browser validation also requires a fresh client bundle. `bun run test:web-canvas`
58
62
  already does this for you.
@@ -146,6 +150,13 @@ Four ways a test silently stops discriminating in this repo:
146
150
  between those assertions. Separately verify a real spec revision resets to authored state,
147
151
  and that local drafts never entered the server spec. See `tests/e2e/json-render-forms.pw.ts`.
148
152
  Eval definitions are not evidence of executing this browser regression or a model benchmark.
153
+ - For viewer refreshes, run `tests/e2e/viewer-refresh-frames.pw.ts`: it delays the runtime-state
154
+ restore beyond document load and checks every captured frame for blank/white flashes in
155
+ Harbor with light/dark OS settings and Daylight with dark OS settings. Keep the frame assertions;
156
+ a settled DOM check cannot prove the handoff stayed painted. Reset canvas-bound work as well as
157
+ nodes between cases. The inert replacement renders behind the active iframe, so select the
158
+ foreground with `iframe:not([inert])`, not `iframe:visible`. Keep capturing until the last
159
+ update is actually in the foreground; a momentary one-iframe count may precede the next poll.
149
160
  - When checking embedded frame fit manually, start from a clean seeded state, rebuild stale bundles,
150
161
  and inspect the actual iframe document in a browser. Server dimensions can look correct while the
151
162
  embedded content is still clipped.
@@ -158,6 +169,15 @@ Four ways a test silently stops discriminating in this repo:
158
169
  - User-facing creation flows must end with the new nodes visible: after the create (plus the
159
170
  skill-mandated focus/fit), a screenshot must show them without any manual pan. A `panned: true`
160
171
  API result alone is not proof — verify the frame is on-screen and unobscured.
172
+ - Layout validation proves node non-overlap, not relation readability. After content settles,
173
+ inspect edge lines, labels, arrowheads and authorship marks at the intended viewing zoom,
174
+ including after auto-arrange; do not prescribe one universal gap.
175
+ - Context/receipt checks must distinguish map delivery (seen links and board read status) from
176
+ full card-content reads. Check one-open stacked See change, clipped-excerpt captions and
177
+ whole-card Undo with a latest-human-edit negative control. Retain a receipt across a board
178
+ switch: snapshot comparison must reopen its owning board, Undo/Unpin must not affect the
179
+ foreign board, and lens marks must use only active-board touches. Check the narrow agent
180
+ initial's counts/lens/panel interaction separately. Use disposable boards for these checks.
161
181
 
162
182
  ## Failure Handling
163
183
 
package/src/cli/agent.ts CHANGED
@@ -52,6 +52,8 @@ Usage:
52
52
 
53
53
  Presentation and capture:
54
54
  pmx-canvas tour get|set Read or persist ordered camera stops
55
+ pmx-canvas tour go <n|next|previous> Present a stop; tour exit leaves presentation
56
+ pmx-canvas camera move --node <id> Ease the camera to a node, --rect or --viewport
55
57
  pmx-canvas record --help Optionally capture the same board tour or live presentation
56
58
 
57
59
  Server:
@@ -397,6 +397,18 @@ cmd(
397
397
  },
398
398
  );
399
399
 
400
+ cmd(
401
+ 'ax reads status',
402
+ 'Read status for a board: its last agent read, per-node reads and revisions, and pin metadata',
403
+ ['pmx-canvas ax reads status', 'pmx-canvas ax reads status --board <board-id>'],
404
+ async (args) => {
405
+ const { flags } = parseFlags(args);
406
+ if (flags.help || flags.h) return showCommandHelp('ax reads status');
407
+
408
+ output(await invokeOperation('ax.reads.status', typeof flags.board === 'string' ? { board: flags.board } : {}));
409
+ },
410
+ );
411
+
400
412
  cmd(
401
413
  'ax work add',
402
414
  'Add a canvas-bound AX work item',
@@ -2,6 +2,55 @@
2
2
 
3
3
  import { cmd, die, getStringFlag, invokeOperation, output, parseFlags, showCommandHelp } from '../shared.js';
4
4
 
5
+ cmd(
6
+ 'board vocabulary',
7
+ 'Set a board relation vocabulary from JSON',
8
+ ['pmx-canvas board vocabulary <id> \'[{"kind":"supports","ink":"supports","directed":true}]\''],
9
+ async (args) => {
10
+ const [id, json] = args;
11
+ if (!id || !json) die('Expected board id and vocabulary JSON.');
12
+ output(await invokeOperation('board.update', { id, relationVocabulary: JSON.parse(json) }));
13
+ },
14
+ );
15
+
16
+ cmd(
17
+ 'board knowledge',
18
+ 'Workspace knowledge actions with a JSON payload',
19
+ [
20
+ 'pmx-canvas board knowledge list',
21
+ 'pmx-canvas board knowledge category-remove \'{"id":"unused-category"}\'',
22
+ 'pmx-canvas board knowledge folder-set \'{"path":"A","categoryId":null,"lifecycleOverride":null}\'',
23
+ ],
24
+ async (args) => {
25
+ const [action, payload = '{}'] = args;
26
+ if (
27
+ ![
28
+ 'list',
29
+ 'category-set',
30
+ 'category-remove',
31
+ 'folder-set',
32
+ 'relation-preset-set',
33
+ 'relation-preset-remove',
34
+ ].includes(action ?? '')
35
+ )
36
+ die('Expected list, category-set, category-remove, folder-set, relation-preset-set or relation-preset-remove.');
37
+ output(await invokeOperation(`knowledge.${action}`, JSON.parse(payload)));
38
+ },
39
+ );
40
+
41
+ cmd(
42
+ 'board lifecycle',
43
+ 'Card lifecycle and audited reviews with a JSON payload',
44
+ ['pmx-canvas board lifecycle get \'{"id":"card"}\''],
45
+ async (args) => {
46
+ const [action, payload = '{}'] = args;
47
+ if (!['get', 'set', 'review', 'restore'].includes(action ?? '')) die('Expected get, set, review or restore.');
48
+ output(
49
+ await invokeOperation(action === 'review' ? 'node.review' : `node.lifecycle-${action}`, JSON.parse(payload)),
50
+ );
51
+ },
52
+ );
53
+
5
54
  cmd('board list', 'List boards, most recently opened first', ['pmx-canvas board list'], async (args) => {
6
55
  const { flags } = parseFlags(args);
7
56
  if (flags.help || flags.h) return showCommandHelp('board list');
@@ -49,14 +98,41 @@ cmd(
49
98
  cmd(
50
99
  'board create',
51
100
  'Create an empty board (use board open with its returned id before writing)',
52
- ['pmx-canvas board create "Q4 planning"', 'pmx-canvas board create "Q4 planning" --category Planning'],
101
+ ['pmx-canvas board create "Q4 planning"', 'pmx-canvas board create "Q4 planning" --folder Planning'],
53
102
  async (args) => {
54
103
  const { positional, flags } = parseFlags(args);
55
104
  if (flags.help || flags.h) return showCommandHelp('board create');
56
105
  const name = positional.join(' ').trim();
57
- if (!name) die('Usage: pmx-canvas board create <name> [--category <category>]');
58
- const category = getStringFlag(flags, 'category');
59
- output(await invokeOperation('board.create', { name, ...(category ? { category } : {}) }));
106
+ if (!name) die('Usage: pmx-canvas board create <name> [--folder <folder>]');
107
+ const folder = getStringFlag(flags, 'folder');
108
+ output(await invokeOperation('board.create', { name, ...(folder ? { folder } : {}) }));
109
+ },
110
+ );
111
+
112
+ cmd(
113
+ 'board pin',
114
+ "Pin a board into the agent's working set (its README and pinned cards reach every brief)",
115
+ ['pmx-canvas board pin board-abc123', 'pmx-canvas board pin board-abc123 --reason "Q4 rests on it"'],
116
+ async (args) => {
117
+ const { positional, flags } = parseFlags(args);
118
+ if (flags.help || flags.h) return showCommandHelp('board pin');
119
+ const [id] = positional;
120
+ if (!id || positional.length > 1) die('Usage: pmx-canvas board pin <board-id> [--reason <why>]');
121
+ const reason = getStringFlag(flags, 'reason');
122
+ output(await invokeOperation('board.pin', { id, ...(reason ? { reason } : {}) }));
123
+ },
124
+ );
125
+
126
+ cmd(
127
+ 'board unpin',
128
+ "Remove a board from the agent's working set",
129
+ ['pmx-canvas board unpin board-abc123'],
130
+ async (args) => {
131
+ const { positional, flags } = parseFlags(args);
132
+ if (flags.help || flags.h) return showCommandHelp('board unpin');
133
+ const [id] = positional;
134
+ if (!id || positional.length > 1) die('Usage: pmx-canvas board unpin <board-id>');
135
+ output(await invokeOperation('board.unpin', { id }));
60
136
  },
61
137
  );
62
138
 
@@ -70,15 +146,15 @@ cmd('board rename', 'Rename a board', ['pmx-canvas board rename board-abc123 "Q4
70
146
  });
71
147
 
72
148
  cmd(
73
- 'board category',
74
- 'File a board under a category on Home (--clear removes it)',
75
- ['pmx-canvas board category board-abc123 "Planning"', 'pmx-canvas board category board-abc123 --clear'],
149
+ 'board folder',
150
+ 'File a board under a folder on Home (--clear removes it)',
151
+ ['pmx-canvas board folder board-abc123 "Planning"', 'pmx-canvas board folder board-abc123 --clear'],
76
152
  async (args) => {
77
153
  const { positional, flags } = parseFlags(args);
78
- if (flags.help || flags.h) return showCommandHelp('board category');
154
+ if (flags.help || flags.h) return showCommandHelp('board folder');
79
155
  const [id, ...rest] = positional;
80
- const category = flags.clear ? '' : rest.join(' ').trim();
81
- if (!id || (!category && !flags.clear)) die('Usage: pmx-canvas board category <board-id> <category> | --clear');
82
- output(await invokeOperation('board.update', { id, category }));
156
+ const folder = flags.clear ? '' : rest.join(' ').trim();
157
+ if (!id || (!folder && !flags.clear)) die('Usage: pmx-canvas board folder <board-id> <folder> | --clear');
158
+ output(await invokeOperation('board.update', { id, folder }));
83
159
  },
84
160
  );