pmx-canvas 0.2.7 → 0.3.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 (189) hide show
  1. package/CHANGELOG.md +148 -1
  2. package/Readme.md +13 -11
  3. package/dist/canvas/index.js +2 -2
  4. package/dist/json-render/index.css +1 -1
  5. package/dist/types/cli/daemon.d.ts +74 -0
  6. package/dist/types/cli/watch.d.ts +2 -2
  7. package/dist/types/client/canvas/CanvasViewport.d.ts +1 -1
  8. package/dist/types/client/canvas/CommandPalette.d.ts +1 -1
  9. package/dist/types/client/canvas/Minimap.d.ts +1 -1
  10. package/dist/types/client/nodes/FileNode.d.ts +1 -1
  11. package/dist/types/client/nodes/ImageNode.d.ts +1 -1
  12. package/dist/types/client/nodes/InlineFormatBar.d.ts +1 -1
  13. package/dist/types/client/nodes/MarkdownNode.d.ts +1 -1
  14. package/dist/types/client/nodes/PromptNode.d.ts +1 -1
  15. package/dist/types/client/nodes/ResponseNode.d.ts +1 -1
  16. package/dist/types/mcp/canvas-access.d.ts +0 -7
  17. package/dist/types/server/ax-state-manager.d.ts +3 -2
  18. package/dist/types/server/bundled-skills.d.ts +3 -3
  19. package/dist/types/server/canvas-schema.d.ts +1 -1
  20. package/dist/types/server/canvas-state.d.ts +2 -0
  21. package/dist/types/server/ext-app-lookup.d.ts +1 -3
  22. package/dist/types/server/html-primitives.d.ts +1 -1
  23. package/dist/types/server/index.d.ts +4 -4
  24. package/dist/types/server/operations/composites.d.ts +29 -23
  25. package/dist/types/server/operations/http.d.ts +2 -1
  26. package/dist/types/server/operations/index.d.ts +1 -1
  27. package/dist/types/server/operations/ops/ax-read.d.ts +2 -0
  28. package/dist/types/server/operations/ops/canvas-wire.d.ts +2 -0
  29. package/dist/types/server/operations/ops/ext-app.d.ts +2 -0
  30. package/dist/types/shared/ax-intent.d.ts +1 -1
  31. package/docs/RELEASE.md +23 -8
  32. package/docs/ax-host-adapter-contract.md +7 -7
  33. package/docs/ax-state-contract.md +7 -6
  34. package/docs/bun-webview-integration.md +5 -5
  35. package/docs/cli.md +1 -1
  36. package/docs/http-api.md +35 -3
  37. package/docs/mcp.md +121 -100
  38. package/docs/node-types.md +45 -35
  39. package/docs/plans/plan-006-mcp-tool-consolidation.md +3 -1
  40. package/docs/plans/plan-008-registry-finish.md +2 -0
  41. package/docs/plans/plan-009-tech-debt-backlog.md +51 -0
  42. package/docs/screenshot.png +0 -0
  43. package/docs/tech-debt-assessment-2026-06.md +1 -1
  44. package/docs/tech-debt-assessment-2026-07.md +135 -0
  45. package/package.json +5 -2
  46. package/skills/data-analysis/SKILL.md +3 -3
  47. package/skills/frontend-design/SKILL.md +3 -2
  48. package/skills/json-render-mcp/SKILL.md +4 -3
  49. package/skills/playwright-cli/SKILL.md +1 -1
  50. package/skills/pmx-canvas/SKILL.md +12 -7
  51. package/skills/pmx-canvas/evals/evals.json +2 -2
  52. package/skills/pmx-canvas/references/ax-html-control-surface.md +3 -1
  53. package/skills/pmx-canvas/references/excalidraw-diagram-authoring.md +2 -2
  54. package/skills/pmx-canvas/references/full-reference.md +58 -56
  55. package/skills/pmx-canvas/references/html-primitives.md +3 -3
  56. package/skills/tufte-viz/SKILL.md +5 -4
  57. package/skills/web-artifacts-builder/SKILL.md +4 -4
  58. package/src/cli/agent.ts +1861 -1548
  59. package/src/cli/daemon.ts +460 -0
  60. package/src/cli/index.ts +63 -326
  61. package/src/cli/watch.ts +2 -10
  62. package/src/client/App.tsx +48 -46
  63. package/src/client/canvas/AttentionHistory.tsx +11 -1
  64. package/src/client/canvas/CanvasNode.tsx +41 -29
  65. package/src/client/canvas/CanvasViewport.tsx +101 -66
  66. package/src/client/canvas/CommandPalette.tsx +61 -27
  67. package/src/client/canvas/ContextMenu.tsx +13 -20
  68. package/src/client/canvas/ContextPinBar.tsx +1 -5
  69. package/src/client/canvas/ContextPinHud.tsx +1 -6
  70. package/src/client/canvas/DockedNode.tsx +4 -4
  71. package/src/client/canvas/EdgeLayer.tsx +37 -36
  72. package/src/client/canvas/ExpandedNodeOverlay.tsx +69 -45
  73. package/src/client/canvas/FocusFieldLayer.tsx +20 -22
  74. package/src/client/canvas/IntentLayer.tsx +31 -14
  75. package/src/client/canvas/Minimap.tsx +11 -16
  76. package/src/client/canvas/SelectionBar.tsx +4 -11
  77. package/src/client/canvas/ShortcutOverlay.tsx +3 -1
  78. package/src/client/canvas/SnapshotPanel.tsx +77 -95
  79. package/src/client/canvas/auto-fit.ts +15 -14
  80. package/src/client/canvas/snap-guides.ts +12 -12
  81. package/src/client/canvas/use-node-resize.ts +1 -5
  82. package/src/client/canvas/use-pan-zoom.ts +25 -26
  83. package/src/client/ext-app/bridge.ts +3 -12
  84. package/src/client/icons.tsx +63 -20
  85. package/src/client/nodes/ContextNode.tsx +14 -25
  86. package/src/client/nodes/ExtAppFrame.tsx +60 -39
  87. package/src/client/nodes/FileNode.tsx +74 -62
  88. package/src/client/nodes/GroupNode.tsx +4 -6
  89. package/src/client/nodes/HtmlNode.tsx +76 -46
  90. package/src/client/nodes/ImageNode.tsx +18 -27
  91. package/src/client/nodes/InlineFormatBar.tsx +4 -21
  92. package/src/client/nodes/InlineMarkdownEditor.tsx +0 -1
  93. package/src/client/nodes/LedgerNode.tsx +10 -4
  94. package/src/client/nodes/MarkdownNode.tsx +3 -10
  95. package/src/client/nodes/McpAppNode.tsx +26 -22
  96. package/src/client/nodes/MdFormatBar.tsx +10 -7
  97. package/src/client/nodes/PromptNode.tsx +23 -51
  98. package/src/client/nodes/ResponseNode.tsx +3 -13
  99. package/src/client/nodes/StatusNode.tsx +5 -9
  100. package/src/client/nodes/StatusSummary.tsx +2 -8
  101. package/src/client/nodes/WebpageNode.tsx +20 -14
  102. package/src/client/nodes/iframe-document-url.ts +25 -16
  103. package/src/client/nodes/image-warnings.ts +1 -7
  104. package/src/client/nodes/md-format.ts +20 -5
  105. package/src/client/state/attention-bridge.ts +4 -9
  106. package/src/client/state/attention-store.ts +1 -7
  107. package/src/client/state/canvas-store.ts +52 -36
  108. package/src/client/state/intent-bridge.ts +176 -112
  109. package/src/client/state/intent-store.ts +4 -1
  110. package/src/client/state/sse-bridge.ts +53 -70
  111. package/src/json-render/catalog.ts +12 -16
  112. package/src/json-render/charts/components.tsx +16 -20
  113. package/src/json-render/charts/extra-components.tsx +8 -16
  114. package/src/json-render/charts/extra-definitions.ts +1 -2
  115. package/src/json-render/charts/tufte-components.tsx +37 -20
  116. package/src/json-render/charts/tufte-definitions.ts +8 -2
  117. package/src/json-render/renderer/index.tsx +42 -22
  118. package/src/json-render/schema.ts +6 -3
  119. package/src/json-render/server.ts +33 -39
  120. package/src/mcp/canvas-access.ts +40 -121
  121. package/src/mcp/server.ts +206 -271
  122. package/src/server/agent-context.ts +63 -36
  123. package/src/server/ax-context.ts +7 -5
  124. package/src/server/ax-interaction.ts +176 -43
  125. package/src/server/ax-state-manager.ts +185 -41
  126. package/src/server/ax-state.ts +142 -47
  127. package/src/server/bundled-skills.ts +3 -3
  128. package/src/server/canvas-db.ts +213 -95
  129. package/src/server/canvas-operations.ts +177 -120
  130. package/src/server/canvas-provenance.ts +1 -4
  131. package/src/server/canvas-schema.ts +500 -102
  132. package/src/server/canvas-serialization.ts +27 -35
  133. package/src/server/canvas-state.ts +155 -58
  134. package/src/server/chart-template.ts +4 -5
  135. package/src/server/code-graph.ts +20 -7
  136. package/src/server/diagram-presets.ts +28 -29
  137. package/src/server/ext-app-lookup.ts +4 -20
  138. package/src/server/html-node-summary.ts +19 -10
  139. package/src/server/html-primitives.ts +326 -97
  140. package/src/server/html-surface.ts +6 -9
  141. package/src/server/image-source.ts +6 -4
  142. package/src/server/index.ts +321 -218
  143. package/src/server/intent-registry.ts +3 -6
  144. package/src/server/mcp-app-candidate.ts +5 -10
  145. package/src/server/mcp-app-host.ts +14 -38
  146. package/src/server/mcp-app-runtime.ts +12 -20
  147. package/src/server/mutation-history.ts +15 -5
  148. package/src/server/operations/composites.ts +38 -40
  149. package/src/server/operations/http.ts +5 -5
  150. package/src/server/operations/index.ts +7 -1
  151. package/src/server/operations/invoker.ts +4 -3
  152. package/src/server/operations/mcp.ts +29 -36
  153. package/src/server/operations/ops/annotation.ts +122 -10
  154. package/src/server/operations/ops/app.ts +102 -77
  155. package/src/server/operations/ops/ax-await.ts +17 -10
  156. package/src/server/operations/ops/ax-read.ts +347 -0
  157. package/src/server/operations/ops/ax-shared.ts +2 -7
  158. package/src/server/operations/ops/ax-state.ts +32 -14
  159. package/src/server/operations/ops/ax-timeline.ts +32 -19
  160. package/src/server/operations/ops/ax-work.ts +54 -37
  161. package/src/server/operations/ops/batch.ts +39 -14
  162. package/src/server/operations/ops/canvas-wire.ts +91 -0
  163. package/src/server/operations/ops/edges.ts +37 -25
  164. package/src/server/operations/ops/ext-app.ts +346 -0
  165. package/src/server/operations/ops/groups.ts +49 -20
  166. package/src/server/operations/ops/intent.ts +18 -12
  167. package/src/server/operations/ops/json-render.ts +239 -98
  168. package/src/server/operations/ops/nodes.ts +300 -111
  169. package/src/server/operations/ops/query.ts +62 -33
  170. package/src/server/operations/ops/snapshots.ts +35 -26
  171. package/src/server/operations/ops/validate.ts +2 -1
  172. package/src/server/operations/ops/viewport.ts +60 -16
  173. package/src/server/operations/ops/webview.ts +44 -18
  174. package/src/server/operations/registry.ts +2 -3
  175. package/src/server/operations/types.ts +7 -5
  176. package/src/server/operations/webview-runner.ts +1 -3
  177. package/src/server/placement.ts +8 -18
  178. package/src/server/server.ts +121 -1012
  179. package/src/server/spatial-analysis.ts +39 -25
  180. package/src/server/trace-manager.ts +3 -8
  181. package/src/server/web-artifacts.ts +23 -27
  182. package/src/server/webpage-node.ts +5 -13
  183. package/src/shared/auto-arrange.ts +12 -5
  184. package/src/shared/ax-intent.ts +1 -1
  185. package/src/shared/content-height-reporter.ts +8 -6
  186. package/src/shared/ext-app-tool-result.ts +2 -6
  187. package/src/shared/placement.ts +1 -4
  188. package/src/shared/semantic-attention.ts +39 -37
  189. package/src/shared/surface.ts +8 -4
package/docs/mcp.md CHANGED
@@ -1,17 +1,19 @@
1
1
  # MCP reference
2
2
 
3
- PMX Canvas ships an MCP stdio server with **84 tools** + **14 core resources**,
3
+ PMX Canvas ships an MCP stdio server with **27 tools** + **14 core resources**,
4
4
  plus per-skill resources at `canvas://skills/<name>`. The server emits
5
5
  `notifications/resources/updated` when canvas state changes — humans pin
6
6
  nodes in the browser, agents are notified immediately.
7
7
 
8
- > **Consolidation in progress (plan-006/008).** The 84 tools are 15 action-discriminated
9
- > **composites** (recommended see below) plus 69 legacy single-purpose tools.
10
- > The composites fold the legacy tools behind an `action` (and, for `canvas_ax_gate`,
11
- > a `kind`) enum; each action dispatches to the same operation, so behavior is
12
- > identical. Folded legacy tools are marked `Deprecated:` in their descriptions and
13
- > are removed in v0.3 per [`api-stability.md`](api-stability.md). **Prefer the
14
- > composites.**
8
+ > **Consolidation completed in v0.3.0 (plan-006/008).** The MCP surface shrank
9
+ > from 84 tools to 27: 15 action-discriminated **composites** (recommended
10
+ > see below) plus 12 standalone tools. The 57 legacy single-purpose tools that
11
+ > the composites replaced were removed in v0.3.0 per
12
+ > [`api-stability.md`](api-stability.md); their descriptions carried
13
+ > `Deprecated:` prefixes for one minor before removal. The 6 snapshot tools
14
+ > remain registered as deprecated standalones — they fold into a
15
+ > `canvas_snapshot` composite in v0.4 (see [Standalone tools](#standalone-tools)
16
+ > below). **Prefer the composites.**
15
17
 
16
18
  ## Connect
17
19
 
@@ -35,15 +37,15 @@ The canvas auto-starts on first tool call.
35
37
  Action-discriminated tools that consolidate the single-purpose tools. Each maps
36
38
  its `action` to the same operation the legacy tool used, so results are identical.
37
39
 
38
- | Composite | `action` values | Replaces |
40
+ | Composite | `action` values | Replaced (removed in v0.3.0) |
39
41
  |-----------|-----------------|----------|
40
42
  | `canvas_node` | `add` · `get` · `update` · `remove` | `canvas_add_node`, `canvas_get_node`, `canvas_update_node`, `canvas_remove_node`, `canvas_add_html_node` (`add` + `type:"html"`), `canvas_add_html_primitive` (`add` + `type:"html"`, `primitive:"<kind>"`), `canvas_refresh_webpage_node` (`update` + `refresh:true`) |
41
43
  | `canvas_render` | `describe-schema` · `validate` · `add-json-render` · `stream-json-render` · `add-graph` | `canvas_describe_schema`, `canvas_validate_spec`, `canvas_add_json_render_node`, `canvas_stream_json_render_node`, `canvas_add_graph_node` |
42
44
  | `canvas_edge` | `add` · `remove` | `canvas_add_edge`, `canvas_remove_edge` |
43
45
  | `canvas_group` | `create` · `add` · `ungroup` | `canvas_create_group`, `canvas_group_nodes`, `canvas_ungroup` |
44
46
  | `canvas_history` | `undo` · `redo` | `canvas_undo`, `canvas_redo` |
45
- | `canvas_view` | `arrange` · `focus` · `fit` · `clear` | `canvas_arrange`, `canvas_focus_node`, `canvas_fit_view`, `canvas_clear` |
46
- | `canvas_query` | `search` · `layout` | `canvas_search`, `canvas_get_layout` |
47
+ | `canvas_view` | `arrange` · `focus` · `fit` · `clear` · `remove-annotation` | `canvas_arrange`, `canvas_focus_node`, `canvas_fit_view`, `canvas_clear`, `canvas_remove_annotation` |
48
+ | `canvas_query` | `search` · `layout` · `validate` | `canvas_search`, `canvas_get_layout`, `canvas_validate` |
47
49
  | `canvas_webview` | `status` · `start` · `stop` · `resize` · `evaluate` | `canvas_webview_status`, `canvas_webview_start`, `canvas_webview_stop`, `canvas_resize`, `canvas_evaluate` |
48
50
  | `canvas_app` | `open-mcp-app` · `diagram` · `build-artifact` | `canvas_open_mcp_app`, `canvas_add_diagram`, `canvas_build_web_artifact` |
49
51
  | `canvas_ax_state` | `get` · `set-focus` · `set-policy` · `report-capability` | `canvas_get_ax`, `canvas_set_ax_focus`, `canvas_set_ax_policy`, `canvas_report_host_capability` |
@@ -64,7 +66,7 @@ veto it — before the mutation lands.
64
66
  - `clear` — abandon/dissolve it explicitly. Normal linked mutations settle automatically.
65
67
 
66
68
  Intents are **ephemeral presence**: never persisted, never snapshotted, never in
67
- `canvas_get_layout`, and auto-expiring. They ride their own SSE channel
69
+ `canvas_query { action: "layout" }`, and auto-expiring. They ride their own SSE channel
68
70
  (`ax-intent` / `ax-intent-clear`) and replay to reconnecting browsers while still
69
71
  live. Best practice — narrate your next move: `signal` → mutate with the returned
70
72
  `intent.id` as `intentId`. A vetoed or expired linked mutation is rejected, and a
@@ -87,86 +89,105 @@ tools). `canvas_screenshot` also stays standalone — it returns a binary image
87
89
  the composite/registry JSON wire shape does not model. (Wave 5 folded
88
90
  `canvas_refresh_webpage_node` → `canvas_node { action: "update", refresh: true }` after
89
91
  fixing `node.update`'s `formatResult` to surface a FAILED refresh as `isError` +
90
- `{ ok:false, error }` instead of masking it as a false `{ ok:true }`.) Snapshots
91
- fold as their registry slice lands.
92
+ `{ ok:false, error }` instead of masking it as a false `{ ok:true }`.) The 6
93
+ snapshot tools are the one deferred fold: a `canvas_snapshot` composite is
94
+ planned for v0.4 (the name collides with the legacy `canvas_snapshot` save
95
+ tool, and [`api-stability.md`](api-stability.md) requires deprecating a tool
96
+ for one minor before removing it) — they stay registered as deprecated
97
+ standalones until then.
92
98
 
93
- ## Tools (legacy single-purpose)
99
+ ## Standalone tools
100
+
101
+ 12 tools that intentionally stay outside the composites — either because
102
+ folding them would hurt (distinct callers, binary payloads, trust-boundary or
103
+ execution-intent semantics), or because they're mid-deprecation pending a
104
+ v0.4 composite. See [Migration reference](#migration-reference) below for the
105
+ 57 tools these composites replaced.
94
106
 
95
107
  | Tool | Description |
96
108
  |------|-------------|
97
- | `canvas_add_node` | Add a node (markdown, status, context, file, webpage, html, etc.) |
98
- | `canvas_add_html_node` | **Deprecated** `canvas_node { action: "add", type: "html" }`. Create an `html` node from a self-contained HTML/JS document (sandboxed iframe) |
99
- | `canvas_add_html_primitive` | **Deprecated** `canvas_node { action: "add", type: "html", primitive: "<kind>" }`. Create a reusable generated HTML communication primitive as a sandboxed `html` node |
100
- | `canvas_add_diagram` | Hand-drawn diagram via the hosted Excalidraw MCP App (preset alias for `canvas_open_mcp_app`) |
101
- | `canvas_open_mcp_app` | Open any [MCP Apps](https://modelcontextprotocol.io/docs/extensions/apps) server's `ui://` resource as an iframe node |
102
- | `canvas_describe_schema` | Describe the running server's create schemas, examples, json-render catalog, and HTML primitive catalog |
103
- | `canvas_validate_spec` | Validate a json-render spec, graph payload, or HTML primitive payload without creating a node |
104
- | `canvas_refresh_webpage_node` | **Deprecated** → `canvas_node { action: "update", refresh: true }`. Re-fetch and update a webpage node from its stored URL |
105
- | `canvas_add_json_render_node` | Create a native json-render node from a validated spec |
106
- | `canvas_stream_json_render_node` | Progressively build a json-render node from SpecStream JSON-Patch ops (live/streaming panels) |
107
- | `canvas_add_graph_node` | Create a native graph node (line, bar, pie, area, scatter, radar, stacked-bar, composed, sparkline, dot-plot, bullet, slopegraph) |
108
- | `canvas_build_web_artifact` | Build a bundled HTML artifact and open it on the canvas |
109
-
110
- `canvas_add_html_node` accepts optional `summary`, `agentSummary`, `embeddedNodeIds`, and
111
- `embeddedUrls`. PMX also derives a bounded text summary from visible HTML, so rich HTML nodes stay
112
- searchable and readable in pinned/spatial context.
113
- | `canvas_update_node` | Update content, position, size, collapsed state |
114
- | `canvas_remove_node` | Remove a node and its edges |
115
- | `canvas_get_layout` | Get full canvas state |
116
- | `canvas_get_node` | Get a single node by ID |
117
- | `canvas_remove_annotation` | Remove a human-drawn annotation by ID |
118
- | `canvas_add_edge` | Connect two nodes |
119
- | `canvas_remove_edge` | Remove a connection |
120
- | `canvas_arrange` | Auto-arrange (grid/column/flow) |
121
- | `canvas_validate` | Validate collisions, containment, and missing edge endpoints |
122
- | `canvas_focus_node` | Pan viewport to a node; use CLI `focus --no-pan` when you only need to select/raise |
123
- | `canvas_fit_view` | Fit the canvas viewport to all nodes or a selected subset |
124
- | `canvas_get_ax` | Read the PMX AX state (focus, work items, approvals, review annotations, host capability) plus pinned/focused context |
125
- | `canvas_set_ax_focus` | Set the host-agnostic AX focus node set; adapters can pass a source such as `codex` |
126
- | `canvas_record_ax_event` | Record a normalized timeline `agent-event` (prompt/assistant-message/tool-start/tool-result/failure/approval/steering) |
127
- | `canvas_send_steering` | Record a `steering-message`: a user instruction from the surface to the active agent session |
128
- | `canvas_get_ax_timeline` | Read the bounded AX timeline (events, evidence, steering) plus counts |
129
- | `canvas_add_work_item` | Add a canvas-bound `work-item` (visible task/plan/status tied to nodes) |
130
- | `canvas_update_work_item` | Update a work item's title/status/detail/nodeIds by ID |
131
- | `canvas_request_approval` | Request human approval via an `approval-gate` (pending) before a high-impact action |
132
- | `canvas_resolve_approval` | Resolve a pending approval gate (`approved`/`rejected`) |
133
- | `canvas_add_evidence` | Record an `evidence-item` on the timeline (logs/tool-result/screenshot/file/diff/test-output) |
134
- | `canvas_add_review_annotation` | Add a canvas-bound `review-annotation` (comment/finding) anchored to a node, file, or region |
135
- | `canvas_report_host_capability` | Report a host/session `host-capability` for diagnostics |
109
+ | `canvas_batch` | Run a batch of canvas operations with `$ref` support |
110
+ | `canvas_pin_nodes` | Pin nodes to include in agent context |
111
+ | `canvas_invoke_command` | Invoke a registry command (`pmx.plan`, `pmx.execute`, `pmx.promote-context`, `pmx.summarize`, `pmx.review`); records a `command` agent-event, unknown names rejected |
136
112
  | `canvas_ax_interaction` | Submit one capability-gated AX interaction envelope (`{ type, sourceNodeId, payload }`) that maps onto an AX operation; the server re-validates and clamps sandboxed surfaces to their own node |
137
- | `canvas_claim_ax_delivery` | Claim undelivered steering messages for an adapterless consumer (loop-safe — never returns steering the consumer originated) |
138
- | `canvas_mark_ax_delivery` | Mark a steering message delivered for a consumer |
139
- | `canvas_request_elicitation` | Request structured human input via a canvas-bound `elicitation` (pending) |
140
- | `canvas_respond_elicitation` | Respond to / resolve a pending elicitation |
141
- | `canvas_request_mode` | Request a workflow `mode-request` transition (plan/execute/autonomous) |
142
- | `canvas_resolve_mode` | Resolve a pending mode request |
143
113
  | `canvas_ingest_activity` | Ingest a harness-forwarded agent activity (tool/session event); the board auto-reacts with kind-driven, overridable defaults (failure → work item + review + evidence; `tool-result`+success → evidence). Makes AX bidirectional |
144
- | `canvas_await_approval` | Block until an approval gate resolves (human approves/rejects in the browser) or the timeout elapses (`timeoutMs` 0 = immediate read). Gates that actually gate |
145
- | `canvas_await_elicitation` | Block until an elicitation is answered or the timeout elapses |
146
- | `canvas_await_mode` | Block until a mode request resolves or the timeout elapses |
147
- | `canvas_invoke_command` | Invoke a registry command (`pmx.plan`, `pmx.execute`, `pmx.promote-context`, `pmx.summarize`, `pmx.review`); records a `command` agent-event, unknown names rejected |
148
- | `canvas_set_ax_policy` | Patch the canvas-bound tool/prompt policy (`tools.allowed\|excluded\|approvalRequired`, `prompt.systemAppend\|mode`); patches merge and are normalized |
149
- | `canvas_pin_nodes` | Pin nodes to include in agent context |
150
- | `canvas_clear` | Clear all nodes and edges |
151
- | `canvas_snapshot` | Save current canvas as a named snapshot |
152
- | `canvas_list_snapshots` | List saved snapshots, bounded to the newest 20 by default |
153
- | `canvas_gc_snapshots` | Delete old snapshots while keeping the newest N |
154
- | `canvas_restore` | Restore canvas from a saved snapshot |
155
- | `canvas_delete_snapshot` | Delete a saved snapshot |
156
- | `canvas_search` | Find nodes by title/content keywords |
157
- | `canvas_undo` | Undo the last canvas mutation |
158
- | `canvas_redo` | Redo the last undone mutation |
159
- | `canvas_diff` | Compare current canvas vs a saved snapshot |
160
- | `canvas_create_group` | Create a group containing specified nodes |
161
- | `canvas_group_nodes` | Add nodes to an existing group |
162
- | `canvas_ungroup` | Release all children from a group |
163
- | `canvas_batch` | Run a batch of canvas operations with `$ref` support |
164
- | `canvas_webview_status` | Get Bun.WebView automation status for the workbench |
165
- | `canvas_webview_start` | Start or replace the Bun.WebView automation session |
166
- | `canvas_webview_stop` | Stop the active Bun.WebView automation session |
167
- | `canvas_evaluate` | Evaluate JavaScript in the active workbench automation session |
168
- | `canvas_resize` | Resize the active workbench automation viewport |
169
114
  | `canvas_screenshot` | Capture a screenshot from the active workbench automation session |
115
+ | `canvas_snapshot` | **Deprecated** → folds into the `canvas_snapshot` composite in v0.4 (actions `save\|list\|restore\|delete\|gc\|diff`). Save current canvas as a named snapshot |
116
+ | `canvas_list_snapshots` | **Deprecated** → folds into the `canvas_snapshot` composite in v0.4 (actions `save\|list\|restore\|delete\|gc\|diff`). List saved snapshots, bounded to the newest 20 by default |
117
+ | `canvas_restore` | **Deprecated** → folds into the `canvas_snapshot` composite in v0.4 (actions `save\|list\|restore\|delete\|gc\|diff`). Restore canvas from a saved snapshot |
118
+ | `canvas_delete_snapshot` | **Deprecated** → folds into the `canvas_snapshot` composite in v0.4 (actions `save\|list\|restore\|delete\|gc\|diff`). Delete a saved snapshot |
119
+ | `canvas_gc_snapshots` | **Deprecated** → folds into the `canvas_snapshot` composite in v0.4 (actions `save\|list\|restore\|delete\|gc\|diff`). Delete old snapshots while keeping the newest N |
120
+ | `canvas_diff` | **Deprecated** → folds into the `canvas_snapshot` composite in v0.4 (actions `save\|list\|restore\|delete\|gc\|diff`). Compare current canvas vs a saved snapshot |
121
+
122
+ `canvas_node { action: "add", type: "html" }` accepts optional `summary`, `agentSummary`,
123
+ `embeddedNodeIds`, and `embeddedUrls`. PMX also derives a bounded text summary from visible
124
+ HTML, so rich HTML nodes stay searchable and readable in pinned/spatial context.
125
+
126
+ ## Migration reference
127
+
128
+ The 57 legacy single-purpose tools below were removed in v0.3.0. Each row is
129
+ the composite call that replaces it — kept as a lookup table for anyone
130
+ migrating an older integration.
131
+
132
+ | Removed tool | Composite replacement |
133
+ |------|-------------|
134
+ | `canvas_add_node` | `canvas_node { action: "add" }` |
135
+ | `canvas_get_node` | `canvas_node { action: "get" }` |
136
+ | `canvas_update_node` | `canvas_node { action: "update" }` |
137
+ | `canvas_remove_node` | `canvas_node { action: "remove" }` |
138
+ | `canvas_add_html_node` | `canvas_node { action: "add", type: "html" }` |
139
+ | `canvas_add_html_primitive` | `canvas_node { action: "add", type: "html", primitive: "<kind>" }` |
140
+ | `canvas_refresh_webpage_node` | `canvas_node { action: "update", refresh: true }` |
141
+ | `canvas_describe_schema` | `canvas_render { action: "describe-schema" }` |
142
+ | `canvas_validate_spec` | `canvas_render { action: "validate" }` |
143
+ | `canvas_add_json_render_node` | `canvas_render { action: "add-json-render" }` |
144
+ | `canvas_stream_json_render_node` | `canvas_render { action: "stream-json-render" }` |
145
+ | `canvas_add_graph_node` | `canvas_render { action: "add-graph" }` |
146
+ | `canvas_add_edge` | `canvas_edge { action: "add" }` |
147
+ | `canvas_remove_edge` | `canvas_edge { action: "remove" }` |
148
+ | `canvas_create_group` | `canvas_group { action: "create" }` |
149
+ | `canvas_group_nodes` | `canvas_group { action: "add" }` |
150
+ | `canvas_ungroup` | `canvas_group { action: "ungroup" }` |
151
+ | `canvas_undo` | `canvas_history { action: "undo" }` |
152
+ | `canvas_redo` | `canvas_history { action: "redo" }` |
153
+ | `canvas_arrange` | `canvas_view { action: "arrange" }` |
154
+ | `canvas_focus_node` | `canvas_view { action: "focus" }` |
155
+ | `canvas_fit_view` | `canvas_view { action: "fit" }` |
156
+ | `canvas_clear` | `canvas_view { action: "clear" }` |
157
+ | `canvas_remove_annotation` | `canvas_view { action: "remove-annotation" }` |
158
+ | `canvas_search` | `canvas_query { action: "search" }` |
159
+ | `canvas_get_layout` | `canvas_query { action: "layout" }` |
160
+ | `canvas_validate` | `canvas_query { action: "validate" }` |
161
+ | `canvas_open_mcp_app` | `canvas_app { action: "open-mcp-app" }` |
162
+ | `canvas_add_diagram` | `canvas_app { action: "diagram" }` |
163
+ | `canvas_build_web_artifact` | `canvas_app { action: "build-artifact" }` |
164
+ | `canvas_webview_status` | `canvas_webview { action: "status" }` |
165
+ | `canvas_webview_start` | `canvas_webview { action: "start" }` |
166
+ | `canvas_webview_stop` | `canvas_webview { action: "stop" }` |
167
+ | `canvas_resize` | `canvas_webview { action: "resize" }` |
168
+ | `canvas_evaluate` | `canvas_webview { action: "evaluate" }` |
169
+ | `canvas_get_ax` | `canvas_ax_state { action: "get" }` |
170
+ | `canvas_set_ax_focus` | `canvas_ax_state { action: "set-focus" }` |
171
+ | `canvas_set_ax_policy` | `canvas_ax_state { action: "set-policy" }` |
172
+ | `canvas_report_host_capability` | `canvas_ax_state { action: "report-capability" }` |
173
+ | `canvas_add_work_item` | `canvas_ax_work { action: "add" }` |
174
+ | `canvas_update_work_item` | `canvas_ax_work { action: "update" }` |
175
+ | `canvas_add_review_annotation` | `canvas_ax_work { action: "annotate" }` |
176
+ | `canvas_request_approval` | `canvas_ax_gate { kind: "approval", action: "request" }` |
177
+ | `canvas_resolve_approval` | `canvas_ax_gate { kind: "approval", action: "resolve" }` |
178
+ | `canvas_await_approval` | `canvas_ax_gate { kind: "approval", action: "await" }` |
179
+ | `canvas_request_elicitation` | `canvas_ax_gate { kind: "elicitation", action: "request" }` |
180
+ | `canvas_respond_elicitation` | `canvas_ax_gate { kind: "elicitation", action: "resolve" }` |
181
+ | `canvas_await_elicitation` | `canvas_ax_gate { kind: "elicitation", action: "await" }` |
182
+ | `canvas_request_mode` | `canvas_ax_gate { kind: "mode", action: "request" }` |
183
+ | `canvas_resolve_mode` | `canvas_ax_gate { kind: "mode", action: "resolve" }` |
184
+ | `canvas_await_mode` | `canvas_ax_gate { kind: "mode", action: "await" }` |
185
+ | `canvas_get_ax_timeline` | `canvas_ax_timeline { action: "read" }` |
186
+ | `canvas_record_ax_event` | `canvas_ax_timeline { action: "record-event" }` |
187
+ | `canvas_add_evidence` | `canvas_ax_timeline { action: "add-evidence" }` |
188
+ | `canvas_send_steering` | `canvas_ax_timeline { action: "send-steering" }` |
189
+ | `canvas_claim_ax_delivery` | `canvas_ax_delivery { action: "claim" }` |
190
+ | `canvas_mark_ax_delivery` | `canvas_ax_delivery { action: "mark" }` |
170
191
 
171
192
  ## Resources
172
193
 
@@ -255,27 +276,27 @@ raw stroke geometry or visual shape.
255
276
 
256
277
  Annotations are a browser-visible markup layer. Use the pen toolbar button to
257
278
  draw and the eraser toolbar button to remove an annotation again; agents can also
258
- remove a known annotation ID with `canvas_remove_annotation`.
279
+ remove a known annotation ID with `canvas_view { action: "remove-annotation", id }`.
259
280
 
260
281
  Use WebView automation when an agent needs to actually see annotations as drawn.
261
- For example, inspect `.annotation-layer path` with `canvas_evaluate` or capture a
262
- `canvas_screenshot` to distinguish an arrow from a line, circle, or highlight.
282
+ For example, inspect `.annotation-layer path` with `canvas_webview { action: "evaluate" }`
283
+ or capture a `canvas_screenshot` to distinguish an arrow from a line, circle, or highlight.
263
284
 
264
285
  ## Node-type routing
265
286
 
266
- MCP node creation uses dedicated tools for structured node families. Read
267
- `mcp.nodeTypeRouting` from `canvas_describe_schema` / `canvas://schema` when
268
- in doubt:
269
-
270
- - `json-render` → `canvas_add_json_render_node`
271
- - `graph` → `canvas_add_graph_node`
272
- - `html-primitive` → `canvas_node { action: "add", type: "html", primitive: "<kind>" }` (or the deprecated `canvas_add_html_primitive`)
273
- - `html` → `canvas_node { action: "add", type: "html" }` (or the deprecated `canvas_add_html_node`)
274
- - `web-artifact` → `canvas_build_web_artifact`
275
- - `mcp-app` → `canvas_open_mcp_app`
276
- - `group` → `canvas_create_group`
287
+ MCP node creation uses dedicated composite actions for structured node
288
+ families. Read `mcp.nodeTypeRouting` from `canvas_render { action:
289
+ "describe-schema" }` / `canvas://schema` when in doubt:
290
+
291
+ - `json-render` → `canvas_render { action: "add-json-render" }`
292
+ - `graph` → `canvas_render { action: "add-graph" }`
293
+ - `html-primitive` → `canvas_node { action: "add", type: "html", primitive: "<kind>" }`
294
+ - `html` → `canvas_node { action: "add", type: "html" }`
295
+ - `web-artifact` → `canvas_app { action: "build-artifact" }`
296
+ - `mcp-app` → `canvas_app { action: "open-mcp-app" }`
297
+ - `group` → `canvas_group { action: "create" }`
277
298
  - Basic nodes (`markdown`, `status`, `file`, `image`, `webpage`) →
278
- `canvas_add_node`
299
+ `canvas_node { action: "add" }`
279
300
 
280
301
  ## CLI/MCP alignment
281
302
 
@@ -45,7 +45,7 @@ When an agent edits a file through its normal tools, the canvas node updates
45
45
  automatically via `fs.watch()`.
46
46
 
47
47
  ```ts
48
- canvas_add_node({ type: 'file', content: 'src/server/index.ts' })
48
+ canvas_node({ action: 'add', type: 'file', content: 'src/server/index.ts' })
49
49
  ```
50
50
 
51
51
  ## Image nodes
@@ -55,7 +55,8 @@ HTTP(S)-backed images preserve provenance so agents can tell where evidence
55
55
  came from. Nodes can carry validation status or warnings.
56
56
 
57
57
  ```ts
58
- canvas_add_node({
58
+ canvas_node({
59
+ action: 'add',
59
60
  type: 'image',
60
61
  content: 'artifacts/dashboard.png',
61
62
  data: {
@@ -73,8 +74,8 @@ keep enough information for an agent to refresh the node from the original
73
74
  URL later.
74
75
 
75
76
  ```ts
76
- canvas_add_node({ type: 'webpage', url: 'https://example.com/docs' })
77
- canvas_refresh_webpage_node({ id: 'node-abc123' })
77
+ canvas_node({ action: 'add', type: 'webpage', url: 'https://example.com/docs' })
78
+ canvas_node({ action: 'update', id: 'node-abc123', refresh: true })
78
79
  ```
79
80
 
80
81
  ## MCP App nodes
@@ -82,11 +83,12 @@ canvas_refresh_webpage_node({ id: 'node-abc123' })
82
83
  `mcp-app` nodes embed other MCP servers' UI resources (`ui://...`) directly
83
84
  on the canvas as sandboxed iframes. Any server implementing the
84
85
  [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps)
85
- can be opened with `canvas_open_mcp_app`.
86
+ can be opened with `canvas_app { action: "open-mcp-app" }`.
86
87
 
87
88
  Generic `pmx-canvas node add --type mcp-app` is intentionally rejected —
88
- these nodes need tool/session metadata. Use `canvas_open_mcp_app` (or the
89
- `canvas_add_diagram` Excalidraw preset) instead.
89
+ these nodes need tool/session metadata. Use `canvas_app { action:
90
+ "open-mcp-app" }` (or the `canvas_app { action: "diagram" }` Excalidraw
91
+ preset) instead.
90
92
 
91
93
  ### Excalidraw preset (hand-drawn diagrams)
92
94
 
@@ -95,7 +97,8 @@ server at `https://mcp.excalidraw.com/mcp`. PMX Canvas exposes a one-call
95
97
  preset:
96
98
 
97
99
  ```ts
98
- canvas_add_diagram({
100
+ canvas_app({
101
+ action: 'diagram',
99
102
  elements: [
100
103
  { type: 'rectangle', id: 'a', x: 80, y: 120, width: 180, height: 80,
101
104
  roundness: { type: 3 }, backgroundColor: '#a5d8ff', fillStyle: 'solid',
@@ -111,8 +114,8 @@ canvas_add_diagram({
111
114
  });
112
115
  ```
113
116
 
114
- For any other MCP App, call `canvas_open_mcp_app` directly with the server's
115
- transport, tool name, and arguments.
117
+ For any other MCP App, call `canvas_app { action: "open-mcp-app" }` directly
118
+ with the server's transport, tool name, and arguments.
116
119
 
117
120
  ## json-render nodes
118
121
 
@@ -122,7 +125,8 @@ transport, tool name, and arguments.
122
125
  and component catalog (core + react + shadcn).
123
126
 
124
127
  ```ts
125
- canvas_add_json_render_node({
128
+ canvas_render({
129
+ action: 'add-json-render',
126
130
  title: 'Deploy status',
127
131
  spec: {
128
132
  root: 'card',
@@ -144,7 +148,8 @@ host-provided handlers. PMX wires AX handlers named after interaction types, so
144
148
  spec action named `ax.*` becomes a capability-gated AX interaction:
145
149
 
146
150
  ```ts
147
- canvas_add_json_render_node({
151
+ canvas_render({
152
+ action: 'add-json-render',
148
153
  title: 'Approve plan',
149
154
  spec: {
150
155
  root: 'btn',
@@ -164,8 +169,8 @@ source + per-viewer nonce + node id) and submits it server-side; `json-render` /
164
169
  `graph` viewers are sandboxed surfaces, so caller-supplied `nodeIds` are clamped
165
170
  to the node's own id. See the [MCP reference](mcp.md#node-interactions-capability-gated).
166
171
 
167
- Use `canvas_describe_schema` / `canvas_validate_spec` to introspect the
168
- component catalog before building a spec.
172
+ Use `canvas_render { action: "describe-schema" }` / `canvas_render { action:
173
+ "validate" }` to introspect the component catalog before building a spec.
169
174
 
170
175
  ## HTML nodes
171
176
 
@@ -182,7 +187,9 @@ match the active theme. Theme updates are posted into sandboxed HTML iframes,
182
187
  so theme-aware HTML can follow dark/light switches without reopening the node.
183
188
 
184
189
  ```ts
185
- canvas_add_html_node({
190
+ canvas_node({
191
+ action: 'add',
192
+ type: 'html',
186
193
  title: 'Cost projection',
187
194
  html: '<canvas id="c"></canvas><script src="https://cdn.jsdelivr.net/npm/chart.js"></script><script>...</script>',
188
195
  })
@@ -213,8 +220,10 @@ presentation, explainer, status report, incident report, triage board, config
213
220
  editor, or prompt tuner.
214
221
 
215
222
  ```ts
216
- canvas_add_html_primitive({
217
- kind: 'choice-grid',
223
+ canvas_node({
224
+ action: 'add',
225
+ type: 'html',
226
+ primitive: 'choice-grid',
218
227
  title: 'Implementation options',
219
228
  data: {
220
229
  items: [
@@ -246,9 +255,9 @@ A **web artifact** is a single-file, fully bundled HTML app (React + Tailwind
246
255
  real interactive app — charts, forms, mini-dashboards — beyond what a static
247
256
  node or `html` snippet can express.
248
257
 
249
- `canvas_build_web_artifact` takes source strings (`App.tsx`, optional
250
- `index.css`, `main.tsx`, `index.html`, plus extra files), runs the bundled
251
- web-artifacts-builder scripts, writes the self-contained HTML to
258
+ `canvas_app { action: "build-artifact" }` takes source strings (`App.tsx`,
259
+ optional `index.css`, `main.tsx`, `index.html`, plus extra files), runs the
260
+ bundled web-artifacts-builder scripts, writes the self-contained HTML to
252
261
  `.pmx-canvas/artifacts/<slug>.html`, and (by default) opens it in the canvas.
253
262
 
254
263
  ```bash
@@ -277,7 +286,7 @@ as dashed-border frames with a title bar and optional accent color.
277
286
  lay children out inside it
278
287
 
279
288
  ```ts
280
- canvas_create_group({ title: 'Auth Module', childIds: ['node-1', 'node-2'], color: '#4a9eff' })
289
+ canvas_group({ action: 'create', title: 'Auth Module', childIds: ['node-1', 'node-2'], color: '#4a9eff' })
281
290
  ```
282
291
 
283
292
  ## Edge types
@@ -296,25 +305,26 @@ All edges support labels, styles (solid/dashed/dotted), and animation.
296
305
  Agents don't have to guess node shapes. The running server exposes its create
297
306
  schemas, json-render component catalog, and node-type examples:
298
307
 
299
- - `canvas_describe_schema` / `GET /api/canvas/schema` — list all node-create
300
- schemas, required fields, json-render components, HTML primitives, and sample payloads
301
- - `canvas_validate_spec` / `POST /api/canvas/schema/validate` — validate a
308
+ - `canvas_render { action: "describe-schema" }` / `GET /api/canvas/schema` — list all
309
+ node-create schemas, required fields, json-render components, HTML primitives, and sample payloads
310
+ - `canvas_render { action: "validate" }` / `POST /api/canvas/schema/validate` — validate a
302
311
  json-render spec, graph payload, or HTML primitive payload **without** creating a node
303
- - `canvas_validate` / `GET /api/canvas/validate` — validate the current
312
+ - `canvas_query { action: "validate" }` / `GET /api/canvas/validate` — validate the current
304
313
  layout for collisions, containment, and missing edge endpoints
305
314
  - `canvas://schema` — the same data as an MCP resource
306
315
 
307
316
  The CLI's `node schema` / `validate spec` subcommands surface the same data
308
317
  from the terminal.
309
318
 
310
- MCP node creation uses dedicated tools for structured node families. Read
311
- `mcp.nodeTypeRouting` from `canvas_describe_schema` when in doubt:
312
- `json-render` `canvas_add_json_render_node`,
313
- `graph` → `canvas_add_graph_node`,
314
- `html-primitive` → `canvas_add_html_primitive`,
315
- `html` → `canvas_add_html_node`,
316
- `web-artifact` → `canvas_build_web_artifact`,
317
- `mcp-app` → `canvas_open_mcp_app`,
318
- `group` → `canvas_create_group`.
319
+ MCP node creation uses dedicated composite actions for structured node
320
+ families. Read `mcp.nodeTypeRouting` from `canvas_render { action:
321
+ "describe-schema" }` when in doubt:
322
+ `json-render` → `canvas_render { action: "add-json-render" }`,
323
+ `graph` → `canvas_render { action: "add-graph" }`,
324
+ `html-primitive` → `canvas_node { action: "add", type: "html", primitive: "<kind>" }`,
325
+ `html` → `canvas_node { action: "add", type: "html" }`,
326
+ `web-artifact` → `canvas_app { action: "build-artifact" }`,
327
+ `mcp-app` → `canvas_app { action: "open-mcp-app" }`,
328
+ `group` → `canvas_group { action: "create" }`.
319
329
  Basic nodes (`markdown`, `status`, `file`, `image`, `webpage`) use
320
- `canvas_add_node`.
330
+ `canvas_node { action: "add" }`.
@@ -1,6 +1,8 @@
1
1
  # Plan 006: MCP tool consolidation (69 tools to 21)
2
2
 
3
- **Status:** In progress — wave 1 landed (7 canvas composites) and the AX wave landed (5 composites: `canvas_ax_state`, `canvas_ax_work`, `canvas_ax_gate`, `canvas_ax_timeline`, `canvas_ax_delivery`). Remaining: `canvas_snapshot` (name held by the legacy save tool until v0.3), `canvas_app`/`canvas_webview` (need plan-005 item 8), and the deferred actions `refresh` / `add-primitive` / `remove-annotation` / board validation.
3
+ **Status: completed in v0.3.0 (canvas_snapshot composite moved to v0.4).**
4
+
5
+ **Status (historical):** In progress — wave 1 landed (7 canvas composites) and the AX wave landed (5 composites: `canvas_ax_state`, `canvas_ax_work`, `canvas_ax_gate`, `canvas_ax_timeline`, `canvas_ax_delivery`). Remaining (closed by plan-008 + the v0.3.0 removal): `canvas_snapshot` (name held by the legacy save tool until v0.4), `canvas_app`/`canvas_webview` (landed via plan-005 item 8), and the deferred actions `refresh` / `add-primitive` / `remove-annotation` / board validation (landed via plan-008 waves 1 and 5).
4
6
  **Date:** 2026-06-12
5
7
  **Depends on:** plan-005 (operation registry). Slices 1-4 are migrated; consolidation lands per-domain as the corresponding registry slices complete.
6
8
  **Motivation:** docs/tech-debt-assessment-2026-06.md item 2. Governed by docs/api-stability.md (deprecation: marked in one minor, removed in the next).
@@ -1,5 +1,7 @@
1
1
  # Plan 008 — Finish the operation-registry refactor (plan-005 items 8–9 + plan-006 completion)
2
2
 
3
+ **Status: completed in v0.3.0 (canvas_snapshot composite moved to v0.4).**
4
+
3
5
  **Status:** Complete
4
6
  **Date:** 2026-06-15
5
7
  **Depends on:** plan-005 (registry — slices 1–7 merged), plan-006 (consolidation — waves 1–2 merged), plan-007 (AX domain — merged).
@@ -0,0 +1,51 @@
1
+ # Plan 009 — Tech debt backlog (open findings from the July 2026 assessment)
2
+
3
+ **Status:** Open
4
+ **Date:** 2026-07-06
5
+ **Motivation:** [`docs/tech-debt-assessment-2026-07.md`](../tech-debt-assessment-2026-07.md) (revised 2026-07-06). The v0.3.0 tree plus the same-day fix pass closed H1, H2, M12, and the small residuals of C2/C3/M4/M7/M9/L7. This plan records everything still open so it survives the release. Finding IDs below refer to the assessment, which carries the full `file:line` evidence.
6
+
7
+ Each item is tagged by shape: **project** (multi-day, own plan/PR), **decision** (needs a maintainer call before code), or **small** (safe standalone change).
8
+
9
+ ## Phase 1 — finish what was started
10
+
11
+ | Item | Source | What's open | Shape |
12
+ |---|---|---|---|
13
+ | Complete the registry migration | C1 | Fold the remaining hand-written routes in `server.ts` into operations; collapse the 76-method `PmxCanvas` onto `executeOperation`; migrate the CLI's 60 raw `fetch` calls onto the operation invoker. **Slice 1 done post-0.3.0:** the 7 `/api/ext-app/*` routes moved to `ops/ext-app.ts` (~290 lines out of `server.ts`, the 5 clone handlers — old M5 — collapsed to one parametrized definition; wire envelopes byte-identical, no new MCP tools). **Slice 2 done:** the 15 ax wire routes (8 GET lists + `ax/context` + `ax/surface-snapshot` + `pinned-context` + `code-graph` + `ax/activity` + `ax/interaction` + legacy `PATCH /api/canvas/ax`) moved to `ops/ax-read.ts` (~370 more lines out of `server.ts`); `open-external` stays hand-written (coupled to the live server port + system browser opener). **Slice 3 done:** theme GET/POST, canvas update, viewport, annotation POST, and node `:id/refresh` moved to the registry (`ops/canvas-wire.ts`, plus additions to `ops/viewport.ts`, `ops/annotation.ts`, `ops/nodes.ts`); the two dispatch gates unified into one `/api/` gate; `server.ts` is at ~2,980 lines (from 3,873 at the audit). **Remaining routes (~20)** are the deliberate poor fits (SSE, binary, HTML/static, file bodies) plus four server-coupled handlers: `prompt` + workbench `state`/`open`/`intent` (module-local `primaryWorkbench*` session state), `render` (Excalidraw URL-normalizer chain), `open-external` (live port + system browser). Those need state injection like the webview runner — a deliberate follow-up, not mechanical. After routes, the bigger halves remain: `PmxCanvas` (76 methods) onto `executeOperation`, and the CLI's raw fetches onto the invoker. | project (in progress) |
14
+ | One HTTP envelope | M4 residual | Registry ops return bare bodies, legacy ext-app handlers `{ok:true,result}`, 404s plaintext. Unify (naturally falls out of C1). Also: surface persistence save failures via a `/health` degraded flag — `canvas-state.ts` has 63 catch→warn→continue sites. | project (with C1) |
15
+ | `dist/` commit policy | C3 residual | **Decided + done post-0.3.0:** `dist/` stays committed; `test.yml` now has a `dist/types` staleness gate (rebuild + fail on drift). The `dist/canvas`/`dist/json-render` bundles are deliberately not byte-checked — bundler output varies across bun versions; their staleness story folds into M1. | done |
16
+ | Skill-mirror validator | C2 residual | `validate:agent-skills` exits 0 when the (local-only, gitignored) trees are absent. Docs are honest about this now; decide whether to repoint, gate, or delete the check. | decision |
17
+ | Generate CLAUDE.md/AGENTS.md from one source | M9 residual | The twins are byte-identical again but still hand-maintained; generate or symlink to prevent the next drift. | small |
18
+
19
+ ## Phase 2 — make the safety net real
20
+
21
+ | Item | Source | What's open | Shape |
22
+ |---|---|---|---|
23
+ | Lint/format tooling | H6 | **Done post-0.3.0:** Biome 2.5 adopted — `bun run lint` / `lint:fix`, wired into `release:check` (making RELEASE.md's "lints" claim true) and CI. First-run format sweep landed as its own mechanical commit; 7 lint errors hand-fixed (incl. a real conditional-hooks bug in `SelectionBar.tsx`). Deliberately disabled rules to revisit: a11y group, `useExhaustiveDependencies` (misfires on Preact signals), `noNonNullAssertion` (143 deliberate uses), `noArrayIndexKey` (13 sites). ~77 warnings (unused imports/vars, optional-chain) left as a burn-down list — warnings don't fail the gate. | done |
24
+ | Client + server unit coverage | H7 | Zero component render tests (no DOM test lib in repo); untested big modules: `ax-state-manager.ts` (826 lines), `mcp-app-host.ts` (814), `canvas-schema.ts` (644), `shared/semantic-attention.ts` (600, in zero test files). Coverage is collected but gates nothing; tests aren't typechecked. Add `happy-dom` + `@testing-library/preact`, a tests tsconfig, and a coverage threshold. | project |
25
+ | Daemon port/lock lifecycle | H4 | **Fixed post-0.3.0** (`src/cli/daemon.ts` + `tests/unit/cli-daemon.test.ts`). Note the original finding was partly wrong: the daemon child was already strict-port (`PmxCanvas.start` pins `allowPortFallback: false`), so fallback-blind polling could not occur. The real defects — fixed — were: the pre-check accepted any responsive `/health` as "already running" (even a foreign workspace's daemon), a TOCTOU double-spawn window, the pid recorded only after health passed (slow start → unkillable orphan), and no PID-recycling guard. The flaky `pmx-canvas-sdk.test.ts` port collision is NOT this — it's M8's copy-pasted `getAvailablePort()` racing. | done |
26
+ | Test-suite structure | M8 | Integration monoliths under `tests/unit/` (`server-api` 5,213 lines, `cli-node` 2,903); `getAvailablePort()` copy-pasted in 5 files; `waitForPersistence(650ms)` fixed sleeps in 9 files + ~19 magic `Bun.sleep`s; Playwright lacks `retries`/`forbidOnly`; `test:e2e-cli` (517-line script) runs in no CI workflow. *Post-0.3.0:* `operation-parity` now binds port 0 and derives the port from the base URL (removes one reserve-then-rebind race); the shared-helper dedup and the rest remain. Also noted: `ax-parity.test.ts` is a string-matching source-analysis test whose needles broke when Biome re-wrapped `cmd(` calls — its CLI needles are now layout-independent, but any future needle should avoid depending on line layout. **Also noted (2026-07-07):** the `server-api` Excalidraw diagram tests do real network round-trips to the hosted `mcp.excalidraw.com` MCP server and intermittently time out (30s) under full parallel runs — they should be mocked or moved to a network-tagged tier. | project |
27
+ | `noUncheckedIndexedAccess` | L7 residual | Enable in tsconfig; will surface many index-access errors — treat as its own sweep. | small (noisy) |
28
+
29
+ ## Phase 3 — client consolidation
30
+
31
+ | Item | Source | What's open | Shape |
32
+ |---|---|---|---|
33
+ | One node factory | H5 | `sse-bridge.ts` (1,066 lines) keeps its own `DEFAULT_POSITIONS`/`makeNode` divergent from `canvas-store`; `responseToThreadMap` is not cleaned on SSE reconnect; server-side `syncEventToCanvasState` (~330 lines) mirrors it as an if/else chain with geometry defaults duplicated against `ensureDefaultDockedNodes`. Share one factory; treat `canvas-layout-update` as the single source of truth; table-drive the event sync. | project |
34
+ | Shared AX iframe bridge | M2 | The ~33-line `onAxMessage` listener is copy-pasted in `HtmlNode.tsx`, `McpAppNode.tsx`, `ExtAppFrame.tsx`; `pmx-canvas-ax` protocol string spread across 9 sites. This is the sandboxed-surface trust boundary (prior near-miss: LRN-20260607-005). Extract `useAxSurfaceBridge` + a protocol-constants module. | project (security-adjacent) |
35
+ | `ExtAppFrame.tsx` timing hacks | M3 | 915 lines, 12 refs, 7 `setTimeout`s, 5 rAFs, WebKit remount slot counter, 1200 ms fallback bootstrap, two near-duplicate `buildHostContext` blocks. Extract `useExtAppBridge`; single `buildHostContext`. | project |
36
+ | json-render stack decision | M1 | Preact + React 19 + recharts all runtime deps; 7 exact-pinned `@json-render/*` incl. 3 devtools packages shipped; `dist/json-render/index.js` 1.37 MB and `dist/canvas/index.js` 599 KB both unminified; build script shells to python3 with a stale-bundle fallback. Minimum: minify, move devtools to devDependencies, lazy-load the viewer, replace the python mtime gate. Bigger call: separate optional package if the React stack stays. | decision + project |
37
+ | Client dead code / duplication | L1, L2, L3 | `ContextPinHud.tsx` unreferenced; three pure re-export shims; two parallel markdown-format stacks; `global.css` 3,746 lines with 11 `!important` vs 227 inline styles and unscaled z-indexes. | small (batchable) |
38
+
39
+ ## Smaller items (no phase, pick up opportunistically)
40
+
41
+ | Item | Source | What's open |
42
+ |---|---|---|
43
+ | Per-action composite schemas | M6 | Composite tools advertise every field optional; per-action required fields fail only at runtime. Decide: `oneOf` schemas vs per-action requirement docs. (Now the only schema surface agents see.) |
44
+ | Packaging | M10 | `files` ships both `src/` and `dist/` (~double tarball, two import paths); `bin` points at raw `.ts` (undocumented Bun-only constraint); `@types/turndown` in runtime deps. |
45
+ | Env var hygiene | M11 residual | `PMX_CANVAS_PORT` vs `PMX_WEB_CANVAS_PORT` applied inconsistently across entry points; `PMX_DATA` (15 uses) and the `PMX_CANVAS_JSON_RENDER_*` / `PMX_MCP_APP_HOST_*` families undocumented. |
46
+ | `publish.yml` e2e gap | M11 residual | Publishing skips e2e with no verification that the tagged SHA passed `test.yml`. |
47
+ | MCP notification fan-out | M7 residual | Five `canvas://` resource URIs notify on every mutation regardless of type (`mcp/server.ts:95-99`). |
48
+ | Legacy migration code on every boot | L4 | JSON→SQLite migration runs unconditionally (`canvas-state.ts:758,785`); gate behind a one-shot marker and retire. |
49
+ | Suppression/mutation plumbing | L5 | Two parallel suppression depth-counters; `onMutation` is a single-slot setter wired twice (last-write-wins). |
50
+ | Module-global lifecycle leaks | L6 | ~20 module-level `let`s in `server.ts`; code-graph debounce timer survives `stopCanvasServer`; remote SSE watcher loop has no cancellation. |
51
+ | June doc citations | L7 residual | `tech-debt-assessment-2026-06.md` cites gitignored `.learnings/` entries future maintainers can't read. |
Binary file
@@ -1,6 +1,6 @@
1
1
  # Tech Debt Assessment & Direction Proposal — June 2026
2
2
 
3
- **Status:** Proposed
3
+ **Status:** Superseded by [tech-debt-assessment-2026-07.md](tech-debt-assessment-2026-07.md)
4
4
  **Date:** 2026-06-10
5
5
  **Scope:** Full-repo audit at v0.1.36 (~70k lines TS; src/server 23.2k, src/client 13.9k, src/mcp 4.2k, src/cli 4.0k)
6
6