pmx-canvas 0.7.1 → 0.7.3
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.
- package/.github/extensions/pmx-canvas/extension.mjs +79 -61
- package/CHANGELOG.md +29 -1
- package/Readme.md +48 -9
- package/dist/canvas/global.css +144 -45
- package/dist/canvas/index.js +90 -90
- package/dist/json-render/index.js +55 -55
- package/dist/types/client/canvas/ChoiceMenu.d.ts +13 -0
- package/dist/types/client/canvas/ExternalWriters.d.ts +3 -3
- package/dist/types/client/icons.d.ts +15 -0
- package/dist/types/client/nodes/ExtAppFrame.d.ts +1 -1
- package/dist/types/client/nodes/FileNode.d.ts +0 -12
- package/dist/types/client/nodes/McpAppNode.d.ts +2 -1
- package/dist/types/client/nodes/viewer-runtime-state.d.ts +1 -0
- package/dist/types/client/state/canvas-store.d.ts +2 -0
- package/dist/types/client/state/intent-bridge.d.ts +1 -1
- package/dist/types/json-render/renderer/index.d.ts +2 -1
- package/dist/types/json-render/server.d.ts +2 -0
- package/dist/types/shared/ax-surface-protocol.d.ts +2 -0
- package/dist/types/shared/delimited-text.d.ts +14 -0
- package/docs/ax-host-adapter-contract.md +13 -2
- package/docs/cli.md +9 -2
- package/docs/http-api.md +12 -8
- package/docs/mcp.md +8 -1
- package/docs/sdk.md +2 -2
- package/package.json +1 -1
- package/skills/pmx-canvas/SKILL.md +41 -16
- package/skills/pmx-canvas/evals/evals.json +112 -2
- package/skills/pmx-canvas/references/full-reference.md +9 -4
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +17 -3
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +76 -4
- package/skills/pmx-canvas-testing/SKILL.md +49 -0
- package/src/cli/commands/backup.ts +15 -4
- package/src/cli/commands/boards.ts +16 -3
- package/src/client/canvas/AttentionHistory.tsx +2 -1
- package/src/client/canvas/BoardSwitcher.tsx +3 -3
- package/src/client/canvas/CanvasNode.tsx +211 -197
- package/src/client/canvas/CanvasViewport.tsx +6 -0
- package/src/client/canvas/ChoiceMenu.tsx +122 -0
- package/src/client/canvas/CommandBar.tsx +27 -25
- package/src/client/canvas/ContextPinBar.tsx +3 -1
- package/src/client/canvas/ExpandedNodeOverlay.tsx +3 -3
- package/src/client/canvas/ExternalWriters.tsx +4 -3
- package/src/client/canvas/FeedbackDialog.tsx +12 -9
- package/src/client/canvas/IntentLayer.tsx +2 -2
- package/src/client/canvas/SelectionBar.tsx +2 -2
- package/src/client/canvas/SessionPanel.tsx +6 -4
- package/src/client/canvas/SessionReceipt.tsx +2 -1
- package/src/client/canvas/SnapshotPanel.tsx +3 -2
- package/src/client/canvas/ToolRail.tsx +48 -100
- package/src/client/canvas/TopBar.tsx +2 -2
- package/src/client/icons.tsx +118 -1
- package/src/client/nodes/BoardNode.tsx +2 -1
- package/src/client/nodes/ExtAppFrame.tsx +3 -1
- package/src/client/nodes/FileNode.tsx +4 -78
- package/src/client/nodes/ImageNode.tsx +2 -1
- package/src/client/nodes/MarkdownNode.tsx +2 -1
- package/src/client/nodes/McpAppNode.tsx +16 -0
- package/src/client/nodes/PromptNode.tsx +3 -1
- package/src/client/nodes/use-ax-surface-bridge.ts +4 -2
- package/src/client/nodes/viewer-runtime-state.ts +78 -0
- package/src/client/state/canvas-store.ts +3 -0
- package/src/client/state/intent-bridge.ts +14 -2
- package/src/client/theme/global.css +144 -45
- package/src/json-render/renderer/index.tsx +58 -38
- package/src/json-render/server.ts +8 -1
- package/src/mcp/server.ts +2 -2
- package/src/server/agent-presence.ts +17 -11
- package/src/server/board-export.ts +97 -32
- package/src/server/index.ts +9 -7
- package/src/server/operations/composites.ts +3 -2
- package/src/server/operations/ops/ax-presence.ts +7 -3
- package/src/server/operations/ops/boards.ts +12 -5
- package/src/server/operations/registry.ts +4 -13
- package/src/server/server.ts +13 -4
- package/src/shared/ax-surface-protocol.ts +3 -0
- package/src/shared/delimited-text.ts +75 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Themed single-choice menu shared by the feedback form and steering composer. */
|
|
2
|
+
export declare function ChoiceMenu({ label, value, options, onChange, className, above, }: {
|
|
3
|
+
label: string;
|
|
4
|
+
value: string;
|
|
5
|
+
options: Array<{
|
|
6
|
+
value: string;
|
|
7
|
+
label: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
}>;
|
|
10
|
+
onChange: (value: string) => void;
|
|
11
|
+
className?: string;
|
|
12
|
+
above?: boolean;
|
|
13
|
+
}): import("preact/src").JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Top-bar indicator: avatar cluster, "3 writers", op count, pulsing dot. */
|
|
2
|
-
export declare function ExternalWriterIndicator(): import("preact/
|
|
3
|
-
export declare function ActivityFeed(): import("preact/
|
|
2
|
+
export declare function ExternalWriterIndicator(): import("preact/src").JSX.Element | null;
|
|
3
|
+
export declare function ActivityFeed(): import("preact/src").JSX.Element | null;
|
|
4
4
|
/** Connected writers — pure visibility, no permissions. */
|
|
5
|
-
export declare function WritersSheet(): import("preact/
|
|
5
|
+
export declare function WritersSheet(): import("preact/src").JSX.Element | null;
|
|
@@ -3,6 +3,21 @@ interface IconProps {
|
|
|
3
3
|
size?: number;
|
|
4
4
|
class?: string;
|
|
5
5
|
}
|
|
6
|
+
export declare function IconClose(p: IconProps): JSX.Element;
|
|
7
|
+
export declare function IconPlus(p: IconProps): JSX.Element;
|
|
8
|
+
export declare function IconPlusCircle(p: IconProps): JSX.Element;
|
|
9
|
+
export declare function IconChevronRight(p: IconProps): JSX.Element;
|
|
10
|
+
export declare function IconMore(p: IconProps): JSX.Element;
|
|
11
|
+
export declare function IconExternalLink(p: IconProps): JSX.Element;
|
|
12
|
+
export declare function IconExpand(p: IconProps): JSX.Element;
|
|
13
|
+
export declare function IconArrowLeft(p: IconProps): JSX.Element;
|
|
14
|
+
export declare function IconRefresh(p: IconProps): JSX.Element;
|
|
15
|
+
export declare function IconUndo(p: IconProps): JSX.Element;
|
|
16
|
+
export declare function IconSparkle(p: IconProps): JSX.Element;
|
|
17
|
+
/** Upload into a tray — attach a document. */
|
|
18
|
+
export declare function IconUpload(p: IconProps): JSX.Element;
|
|
19
|
+
/** Dropdown disclosure, sized like the other toolbar icons. */
|
|
20
|
+
export declare function IconChevronDown(p: IconProps): JSX.Element;
|
|
6
21
|
/** Expand-arrows — fit all nodes */
|
|
7
22
|
export declare function IconFitAll(p: IconProps): JSX.Element;
|
|
8
23
|
/** Magnifier with + */
|
|
@@ -106,5 +106,5 @@ export declare function resolveExtAppInlineFrameHeight(appHeight: number, hostHe
|
|
|
106
106
|
export declare function ExtAppFrame({ node, expanded }: {
|
|
107
107
|
node: CanvasNodeState;
|
|
108
108
|
expanded?: boolean;
|
|
109
|
-
}): import("preact/
|
|
109
|
+
}): import("preact/src").JSX.Element;
|
|
110
110
|
export {};
|
|
@@ -1,16 +1,4 @@
|
|
|
1
1
|
import type { CanvasNodeState } from '../types';
|
|
2
|
-
export interface DelimitedTable {
|
|
3
|
-
header: string[];
|
|
4
|
-
/** Rectangular: every row padded (short) or truncated (long) to the header width. */
|
|
5
|
-
rows: string[][];
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* RFC4180-ish delimited-text parser. Quoted fields may contain the delimiter,
|
|
9
|
-
* newlines, and `""` escapes; CRLF, LF and lone CR all end a record; a trailing
|
|
10
|
-
* newline produces no phantom empty row. Ragged records are squared off against
|
|
11
|
-
* the header width instead of throwing.
|
|
12
|
-
*/
|
|
13
|
-
export declare function parseDelimitedText(text: string, delimiter: string): DelimitedTable;
|
|
14
2
|
/** Human-readable byte size, e.g. `4.2 MB`. */
|
|
15
3
|
export declare function formatBytes(bytes: number): string;
|
|
16
4
|
export declare function FileNode({ node, expanded }: {
|
|
@@ -12,10 +12,11 @@ type ViewerFrameSource = {
|
|
|
12
12
|
* white navigation paint. Two animation frames after load gives the new document
|
|
13
13
|
* a composited paint before the old one is removed.
|
|
14
14
|
*/
|
|
15
|
-
export declare function RefreshingViewerFrame({ source, iframeRef, onLoad, title, className, sandbox, allow, tabIndex, }: {
|
|
15
|
+
export declare function RefreshingViewerFrame({ source, iframeRef, onLoad, onDocumentLoad, title, className, sandbox, allow, tabIndex, }: {
|
|
16
16
|
source: ViewerFrameSource;
|
|
17
17
|
iframeRef: RefObject<HTMLIFrameElement>;
|
|
18
18
|
onLoad: () => void;
|
|
19
|
+
onDocumentLoad?: (frame: HTMLIFrameElement) => void;
|
|
19
20
|
title: string;
|
|
20
21
|
className?: string;
|
|
21
22
|
sandbox?: string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function restoreViewerRuntimeState(nodeId: string, token: string, source: Window): void;
|
|
@@ -25,6 +25,8 @@ export declare const pendingExpandedNodeCloseId: import("@preact/signals-core").
|
|
|
25
25
|
export declare const pendingConnection: import("@preact/signals-core").Signal<{
|
|
26
26
|
from: string;
|
|
27
27
|
} | null>;
|
|
28
|
+
/** Invalidates in-flight interaction replies when board navigation resets the canvas. */
|
|
29
|
+
export declare const canvasInteractionEpoch: import("@preact/signals-core").Signal<number>;
|
|
28
30
|
export declare const draggingEdge: import("@preact/signals-core").Signal<{
|
|
29
31
|
fromId: string;
|
|
30
32
|
fromX: number;
|
|
@@ -84,7 +84,7 @@ export declare function pushCanvasUpdate(updates: Array<{
|
|
|
84
84
|
}>, options?: {
|
|
85
85
|
recordHistory?: boolean;
|
|
86
86
|
}): Promise<void>;
|
|
87
|
-
/**
|
|
87
|
+
/** Render the server-confirmed edge immediately, without waiting for SSE/polling. */
|
|
88
88
|
export declare function createEdgeFromClient(from: string, to: string, type: string, label?: string): Promise<{
|
|
89
89
|
ok: boolean;
|
|
90
90
|
id?: string;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Runs inside a pmx-canvas iframe and reads the normalized json-render spec
|
|
6
6
|
* from an inline global injected by the server-side viewer route.
|
|
7
7
|
*/
|
|
8
|
-
import type
|
|
8
|
+
import { type Spec } from '@json-render/core';
|
|
9
9
|
declare global {
|
|
10
10
|
interface Window {
|
|
11
11
|
__PMX_CANVAS_JSON_RENDER_SPEC__?: Spec & {
|
|
@@ -17,5 +17,6 @@ declare global {
|
|
|
17
17
|
__PMX_CANVAS_JSON_RENDER_NODE_ID__?: string;
|
|
18
18
|
__PMX_CANVAS_AX_TOKEN__?: string;
|
|
19
19
|
__PMX_CANVAS_AX_STATE__?: unknown;
|
|
20
|
+
__PMX_CANVAS_UI_STATE_TOKEN__?: string;
|
|
20
21
|
}
|
|
21
22
|
}
|
|
@@ -101,6 +101,8 @@ export declare function buildJsonRenderViewerHtml(options: {
|
|
|
101
101
|
nodeId?: string;
|
|
102
102
|
axToken?: string;
|
|
103
103
|
axState?: unknown;
|
|
104
|
+
/** Independent nonce for tab-local runtime state across focus transitions. */
|
|
105
|
+
stateToken?: string;
|
|
104
106
|
/** Nonce for the content-height reporter so the node can grow to fit the chart. */
|
|
105
107
|
frameToken?: string;
|
|
106
108
|
/** When true, charts render at their natural (intrinsic) height instead of
|
|
@@ -14,6 +14,8 @@ export declare const AX_SURFACE_EMIT_SOURCE = "pmx-canvas-ax";
|
|
|
14
14
|
export declare const AX_SURFACE_ACK_SOURCE = "pmx-canvas-ax-ack";
|
|
15
15
|
/** Parent→surface pushes (theme updates, live AX state) into html/viewer iframes. */
|
|
16
16
|
export declare const HTML_SURFACE_PUSH_SOURCE = "pmx-canvas-html-node";
|
|
17
|
+
/** Tab-local JSON viewer state handoff, independent of AX capabilities. */
|
|
18
|
+
export declare const JSON_VIEWER_STATE_SOURCE = "pmx-canvas-json-state";
|
|
17
19
|
/** Ext-app boot beacon: the iframe's scripts executed (WebKit watchdog liveness). */
|
|
18
20
|
export declare const EXT_APP_BOOT_BEACON_SOURCE = "pmx-canvas-ext-app-alive";
|
|
19
21
|
/**
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface DelimitedTable {
|
|
2
|
+
header: string[];
|
|
3
|
+
/** Rectangular: every row padded (short) or truncated (long) to the header width. */
|
|
4
|
+
rows: string[][];
|
|
5
|
+
}
|
|
6
|
+
/** Column delimiter for a delimited-data path, or null when the file is not one. */
|
|
7
|
+
export declare function delimiterForPath(path: string): string | null;
|
|
8
|
+
/**
|
|
9
|
+
* RFC4180-ish delimited-text parser. Quoted fields may contain the delimiter,
|
|
10
|
+
* newlines, and `""` escapes; CRLF, LF and lone CR all end a record; a trailing
|
|
11
|
+
* newline produces no phantom empty row. Ragged records are squared off against
|
|
12
|
+
* the header width instead of throwing.
|
|
13
|
+
*/
|
|
14
|
+
export declare function parseDelimitedText(text: string, delimiter: string): DelimitedTable;
|
|
@@ -6,12 +6,23 @@ capabilities, and the tool/prompt policy — over HTTP and MCP. What makes AX
|
|
|
6
6
|
*interactive* on a given coding harness (GitHub Copilot, Codex, Claude Code, …) is
|
|
7
7
|
a thin **adapter** that wires PMX's neutral surfaces to that harness's lifecycle.
|
|
8
8
|
|
|
9
|
-
"Agnostic" means a documented interface plus PMX-side behavior
|
|
10
|
-
|
|
9
|
+
"Agnostic" means a documented interface plus PMX-side behavior that a host-specific
|
|
10
|
+
adapter can use — not a shipped adapter for every harness or zero-adapter magic. The genuinely harness-owned
|
|
11
11
|
acts (waking a turn, per-turn context injection, forwarding native tool hooks,
|
|
12
12
|
native modals) still need a per-harness adapter; PMX owns everything on its side of
|
|
13
13
|
the line (queues, endpoints, schemas, the canvas-surface fallback).
|
|
14
14
|
|
|
15
|
+
### Amp uses native MCP, not a bundled lifecycle adapter
|
|
16
|
+
|
|
17
|
+
Amp's portal and agent connection are separate. Configure `amp.mcpServers` in the
|
|
18
|
+
executing workspace's `.amp/settings.json`, align its root/port with the portal service,
|
|
19
|
+
and have the human approve a new workspace command before reloading MCP. See the
|
|
20
|
+
[Amp setup guide](../skills/pmx-canvas/references/installing-pmx-canvas.md#amp-connect-the-agent-separately-from-the-portal).
|
|
21
|
+
An active Amp agent can read pinned context, claim addressed steering, edit, verify the
|
|
22
|
+
visible result and acknowledge. PMX ships no Amp lifecycle adapter that automatically
|
|
23
|
+
injects context, forwards tool hooks, or wakes idle threads. The injection/push behavior
|
|
24
|
+
below requires an adapter implementing it; merely configuring MCP does not provide it.
|
|
25
|
+
|
|
15
26
|
## The interface
|
|
16
27
|
|
|
17
28
|
Every adapter implements as much of this as its host allows; PMX provides the
|
package/docs/cli.md
CHANGED
|
@@ -280,6 +280,8 @@ node or fit the new ids — auto-placement is board-relative, not camera-relativ
|
|
|
280
280
|
```bash
|
|
281
281
|
pmx-canvas board list # most recently opened first; activeBoardId null = Home
|
|
282
282
|
pmx-canvas board create "Q4 planning" # created, not opened
|
|
283
|
+
pmx-canvas board open <board-id> # open the returned ID before writing
|
|
284
|
+
pmx-canvas board open --home # return the shared workbench to Home
|
|
283
285
|
pmx-canvas board rename <board-id> "New name"
|
|
284
286
|
pmx-canvas board create "Q4 planning" --category Planning
|
|
285
287
|
pmx-canvas board category <board-id> "Planning/Quarterly" # nested folders; --clear unfiles it
|
|
@@ -287,8 +289,10 @@ pmx-canvas board from <source-id> "Follow-up" --preview
|
|
|
287
289
|
pmx-canvas board from <source-id> "Follow-up" --nodes id1,id2 --readme --structure
|
|
288
290
|
```
|
|
289
291
|
|
|
290
|
-
Commands write to the board open in the workbench.
|
|
291
|
-
|
|
292
|
+
Commands write to the board open in the workbench. `board open` switches the
|
|
293
|
+
shared visible board, not a private CLI target. Verify its returned
|
|
294
|
+
`activeBoardId` before writing; another participant can switch it later.
|
|
295
|
+
Deletion remains human-only. `board from`
|
|
292
296
|
excludes `prompt`, `response`, `trace`, and `mcp-app` cards; selected reusable
|
|
293
297
|
cards get fresh IDs. `--structure` includes group frames; edges between copied
|
|
294
298
|
cards are retained automatically. `--readme` carries the designated README. The new board remains inactive and
|
|
@@ -300,6 +304,9 @@ Boards save automatically. Backups are separate recovery copies of the whole lib
|
|
|
300
304
|
configured through the CLI or HTTP API rather than the Home screen. Scheduled backups
|
|
301
305
|
run while the server is running; scheduling is off until configured.
|
|
302
306
|
|
|
307
|
+
Use `--to` for the destination folder. Backup and restore commands reject unknown
|
|
308
|
+
flags (including `--folder`) rather than silently using the configured default.
|
|
309
|
+
|
|
303
310
|
```bash
|
|
304
311
|
pmx-canvas backup # every board, now; into .pmx-canvas/backups
|
|
305
312
|
pmx-canvas backup --to ~/Backups/pmx --keep 14
|
package/docs/http-api.md
CHANGED
|
@@ -48,11 +48,13 @@ the CLI, not an HTTP job endpoint.
|
|
|
48
48
|
A workspace holds many named boards; one is open at a time. Agent writes go to
|
|
49
49
|
the open board. While no board is open (Home), the first write opens the most
|
|
50
50
|
recently opened board, or creates one when the workspace has none;
|
|
51
|
-
`canvas.clear` on Home opens nothing.
|
|
52
|
-
|
|
51
|
+
`canvas.clear` on Home opens nothing. Agents can explicitly open a board using
|
|
52
|
+
`POST /api/canvas/boards/open`; verify the returned `activeBoardId` before writing.
|
|
53
|
+
This changes the shared workbench, not a private per-agent target. Only the
|
|
54
|
+
trusted workbench deletes a board — deletion requires both `x-pmx-workbench: 1` and the
|
|
53
55
|
per-boot `x-pmx-workbench-token` embedded in the served `/workbench` HTML;
|
|
54
|
-
the marker alone is not authority. Public HTTP integrations must not
|
|
55
|
-
|
|
56
|
+
the marker alone is not authority. Public HTTP integrations must not delete
|
|
57
|
+
boards. In-process SDK embedding hosts retain deletion authority.
|
|
56
58
|
Every change emits a
|
|
57
59
|
`boards-changed` SSE frame with `{ activeBoardId, boards }`.
|
|
58
60
|
|
|
@@ -87,10 +89,9 @@ curl -X PATCH http://localhost:4313/api/canvas/boards/<board-id> \
|
|
|
87
89
|
curl -X PATCH http://localhost:4313/api/canvas/boards/<board-id> \
|
|
88
90
|
-H "Content-Type: application/json" -d '{"readmeNodeId":"<markdown-node-id>"}'
|
|
89
91
|
|
|
90
|
-
# Open a board, or Home with {"id":null} —
|
|
92
|
+
# Open a board, or Home with {"id":null} — also available to agents
|
|
91
93
|
curl -X POST http://localhost:4313/api/canvas/boards/open \
|
|
92
|
-
-H "Content-Type: application/json" -
|
|
93
|
-
-H "x-pmx-workbench-token: <token-from-served-workbench>" -d '{"id":"<board-id>"}'
|
|
94
|
+
-H "Content-Type: application/json" -d '{"id":"<board-id>"}'
|
|
94
95
|
|
|
95
96
|
# Delete a board and its snapshots — workbench only; deleting the open board returns to Home
|
|
96
97
|
curl -X DELETE http://localhost:4313/api/canvas/boards/<board-id> \
|
|
@@ -158,7 +159,10 @@ curl "http://localhost:4313/api/canvas/exports/<file>.html?download=1" -o board.
|
|
|
158
159
|
|
|
159
160
|
PDF and Office uploads are raw bytes, not JSON. Upload requires the trusted
|
|
160
161
|
workbench token, an active captured board, a non-empty body of at most 20 MiB,
|
|
161
|
-
and creates both an original file card and an `attached` import.
|
|
162
|
+
and creates both an original file card and an `attached` import. Requested x/y
|
|
163
|
+
coordinates are preserved when free; otherwise the original card is placed in
|
|
164
|
+
open space with a 48-unit gap, without moving existing cards. The import's
|
|
165
|
+
initial anchor follows the original card's actual position. The original
|
|
162
166
|
remains board-owned until board deletion; snapshot restore does not remove it,
|
|
163
167
|
and whole-database backups include it.
|
|
164
168
|
|
package/docs/mcp.md
CHANGED
|
@@ -65,7 +65,7 @@ its `action` to the same operation the legacy tool used, so results are identica
|
|
|
65
65
|
| `canvas_ax_timeline` | `read` · `record-event` · `add-evidence` · `send-steering` · `reads` | `canvas_get_ax_timeline`, `canvas_record_ax_event`, `canvas_add_evidence`, `canvas_send_steering` (`reads` is new — the context read log) |
|
|
66
66
|
| `canvas_ax_delivery` | `claim` (long-polls with `timeoutMs`) · `mark` | `canvas_claim_ax_delivery`, `canvas_mark_ax_delivery` |
|
|
67
67
|
| `canvas_intent` | `signal` · `update` · `clear` | _(new — Ghost Cursor of Intent; no legacy standalone tool)_ |
|
|
68
|
-
| `canvas_board` | `list` · `get` · `create` · `create-from` · `update` | Boards, README designation, and transactional inactive copies |
|
|
68
|
+
| `canvas_board` | `list` · `get` · `create` · `create-from` · `open` · `update` | Boards, explicit shared-workbench switching, README designation, and transactional inactive copies |
|
|
69
69
|
| `canvas_snapshot` | `save` · `list` · `restore` · `delete` · `gc` · `diff` | `canvas_snapshot` (legacy save tool), `canvas_list_snapshots`, `canvas_restore`, `canvas_delete_snapshot`, `canvas_gc_snapshots`, `canvas_diff` — removed in v0.4.0 after one deprecated minor |
|
|
70
70
|
|
|
71
71
|
Board categories are nested folder paths: `canvas_board { action: "update", id:
|
|
@@ -91,6 +91,13 @@ state, or history. Groups and the designated README are opt-in.
|
|
|
91
91
|
Library search is `canvas_query { action: "search", query: "auth", scope:
|
|
92
92
|
"library" }`. It returns board/card IDs and never switches the active board.
|
|
93
93
|
|
|
94
|
+
To author a new board, create it, then call `canvas_board { action: "open",
|
|
95
|
+
id: "<returned-board-id>" }` and verify `activeBoardId` in the response before
|
|
96
|
+
writing. Opening changes the shared workbench the human sees and the target of
|
|
97
|
+
subsequent writes; it is not a private agent selection. Another participant can
|
|
98
|
+
switch it later, so recheck after interruptions. `id: null` opens Home. Deletion
|
|
99
|
+
remains human-only.
|
|
100
|
+
|
|
94
101
|
### `canvas_import` — agent-assisted document extraction
|
|
95
102
|
|
|
96
103
|
A PDF/Office drop retains the original attachment (maximum 20 MiB), but does
|
package/docs/sdk.md
CHANGED
|
@@ -91,8 +91,8 @@ canvas.setAxFocus([n1], { source: 'sdk' });
|
|
|
91
91
|
console.log(canvas.getAxState());
|
|
92
92
|
console.log(canvas.getAxContext());
|
|
93
93
|
|
|
94
|
-
// Boards —
|
|
95
|
-
//
|
|
94
|
+
// Boards — agents may open boards; deletion is reserved for trusted embedding hosts.
|
|
95
|
+
// Open the intended board before writing: this also switches the shared workbench.
|
|
96
96
|
const board = await canvas.createBoard('Q4 planning', 'Planning'); // optional category
|
|
97
97
|
await canvas.updateBoard(board.id, { category: 'Planning/Quarterly' }); // nested folders; null unfiles it
|
|
98
98
|
await canvas.openBoard(board.id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmx-canvas",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "Spatial canvas workbench for coding agents — infinite 2D canvas with agent-native CLI, MCP integration, nodes, edges, file watching, and snapshots",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/server/index.ts",
|
|
@@ -43,11 +43,14 @@ for the authoritative stop, viewport, persistence, reset, group-order, and captu
|
|
|
43
43
|
|
|
44
44
|
## Runtime prerequisites
|
|
45
45
|
|
|
46
|
-
PMX Canvas 0.7.
|
|
46
|
+
PMX Canvas 0.7.3 requires **Bun >=1.4.2**, including when installed through npm.
|
|
47
47
|
Check `bun --version` and ensure Bun is on the MCP host's PATH, not just your shell's;
|
|
48
48
|
use an absolute executable path if needed. Install/configure only when requested.
|
|
49
49
|
For pinned installation, MCP configuration, managed services, and disposable verification,
|
|
50
50
|
read [Installing PMX Canvas](references/installing-pmx-canvas.md).
|
|
51
|
+
In Amp, follow that reference's [Amp setup](references/installing-pmx-canvas.md#amp-connect-the-agent-separately-from-the-portal):
|
|
52
|
+
portal access is separate from native MCP, workspace commands need human trust approval,
|
|
53
|
+
and a connected MCP server does not install an idle-thread wake-up adapter.
|
|
51
54
|
|
|
52
55
|
## Reading bundled skills over MCP
|
|
53
56
|
|
|
@@ -58,7 +61,8 @@ files (for example `skill://pmx-canvas/references/installing-pmx-canvas.md`) thr
|
|
|
58
61
|
`resources/read`, fetching only what is needed. Hosts without extension support can still read
|
|
59
62
|
ordinary resources or use installed filesystem skills; `canvas://skills` remains available.
|
|
60
63
|
Resource reads do not activate skills or authorize executing their scripts. The host must
|
|
61
|
-
preserve MCP origin and apply integrity/approval checks.
|
|
64
|
+
preserve MCP origin and apply integrity/approval checks. Native Canvas tool discovery alone
|
|
65
|
+
does not establish native MCP Skills-extension support. Restart MCP after upgrading the package;
|
|
62
66
|
the served catalog is a startup snapshot. Local mirrors still use `skills sync` below.
|
|
63
67
|
|
|
64
68
|
## Required Operating Sequence
|
|
@@ -95,12 +99,16 @@ the served catalog is a startup snapshot. Local mirrors still use `skills sync`
|
|
|
95
99
|
floor, not a replacement: only an explicit signal gives the human a real pre-mutation veto
|
|
96
100
|
window, your reasoning (`reason`), and staged multi-step previews. Batch and browser-human
|
|
97
101
|
actions never auto-ghost.
|
|
98
|
-
6. **
|
|
99
|
-
`canvas_ax_state { action: "
|
|
100
|
-
|
|
102
|
+
6. **Reuse your host session, or attach one if needed.** First read
|
|
103
|
+
`canvas_ax_state { action: "presence" }`. Copilot's extension already attaches as
|
|
104
|
+
`source: "copilot"` with consumer/session ID `copilot`: reuse that identity, do not create a
|
|
105
|
+
task-named agent. Put task descriptions in `detail`, not a new `agentId` or source. Only if
|
|
106
|
+
your host has no session, attach with `canvas_ax_state { action: "set-presence",
|
|
107
|
+
attached: true, label: "<who you are>" }` and detach that self-owned session when done.
|
|
108
|
+
Do not detach a host-owned session; its adapter manages its lifetime. Everything the session
|
|
101
109
|
gives you and asks of you (cursor + phase chip, the session panel, steering, the scope fence's
|
|
102
110
|
403s, the human edit lock's 409s, unattended approvals) is in **Sessions & the human** below.
|
|
103
|
-
7. **Mutate through current composites.** Prefer the
|
|
111
|
+
7. **Mutate through current composites.** Prefer the 18 composite MCP tools below.
|
|
104
112
|
7. **Arrange and validate.** After batch changes, use `canvas_view { action: "arrange" }` when
|
|
105
113
|
appropriate and always finish with `canvas_query { action: "validate" }`.
|
|
106
114
|
7b. **Show the human.** After creating user-facing output, bring the camera to it: a single node
|
|
@@ -200,7 +208,7 @@ always authoritative, independent of the installed release version.
|
|
|
200
208
|
|-----------|---------|
|
|
201
209
|
| `canvas_import` | `list`, `read`, `submit`, `unavailable` |
|
|
202
210
|
| `canvas_node` | `add`, `get`, `update`, `remove` |
|
|
203
|
-
| `canvas_render` | `describe-schema`, `validate`, `add-json-render`, `stream-json-render`, `add-graph` |
|
|
211
|
+
| `canvas_render` | `describe-schema`, `validate`, `add-json-render`, `stream-json-render`, `add-graph`, `workboard` |
|
|
204
212
|
| `canvas_edge` | `add`, `update`, `remove` |
|
|
205
213
|
| `canvas_group` | `create`, `add`, `ungroup` |
|
|
206
214
|
| `canvas_history` | `undo`, `redo` |
|
|
@@ -213,7 +221,7 @@ always authoritative, independent of the installed release version.
|
|
|
213
221
|
| `canvas_ax_gate` | `request`, `resolve`, `await` with `approval`, `elicitation`, or `mode` |
|
|
214
222
|
| `canvas_ax_timeline` | `read`, `record-event`, `add-evidence`, `send-steering`, `reads` |
|
|
215
223
|
| `canvas_ax_delivery` | `claim`, `mark` |
|
|
216
|
-
| `canvas_board` | `list`, `get`, `create`, `create-from`, `update` |
|
|
224
|
+
| `canvas_board` | `list`, `get`, `create`, `create-from`, `open`, `update` |
|
|
217
225
|
| `canvas_snapshot` | `save`, `list`, `restore`, `delete`, `gc`, `diff` |
|
|
218
226
|
| `canvas_intent` | `signal`, `update`, `clear` |
|
|
219
227
|
|
|
@@ -296,7 +304,12 @@ as layout work left to do.
|
|
|
296
304
|
Creation responses may include `sizeAdjustment` (`defaulted`, `clamped-to-minimum`, or
|
|
297
305
|
`fit-to-children`); use its `applied` geometry rather than assuming the request won. Json-render
|
|
298
306
|
validation/creation may also return warnings for form controls whose value is not bound with
|
|
299
|
-
`$bindState`; the panel can render, but edits
|
|
307
|
+
`$bindState`; the panel can render, but edits do not update its runtime state until the binding is fixed.
|
|
308
|
+
Bound JSON input and dynamic lists are local UI state, not saved canvas data. Focus-mode
|
|
309
|
+
expand/close preserves that state for the same spec in the current tab; an authored spec update,
|
|
310
|
+
board switch or page reload resets it. Persist important edits explicitly through the app's
|
|
311
|
+
save/AX actions. Published 0.7.2 loses this state on expand/close too (AK): avoid changing views
|
|
312
|
+
with an unsaved JSON form on that release. This contract does not extend to HTML or external apps.
|
|
300
313
|
If the documented receipts or warnings are unexpectedly absent, do not guess around them with raw
|
|
301
314
|
payload reads: verify that the CLI/MCP host and daemon use the same 0.7 installation and workspace,
|
|
302
315
|
restart the matching daemon after upgrade, and re-check `/health`.
|
|
@@ -360,7 +373,10 @@ The board has three modes, all gated on one fact — whether a session is attach
|
|
|
360
373
|
|
|
361
374
|
What the session asks of you:
|
|
362
375
|
|
|
363
|
-
- **Detach explicitly** (`attached: false` or a `session-end` activity).
|
|
376
|
+
- **Detach self-owned sessions explicitly** (`attached: false` or a `session-end` activity).
|
|
377
|
+
Reuse an adapter-owned session without attaching/detaching another; for Copilot, update
|
|
378
|
+
`{ source: "copilot", detail: "Team overview" }` without inventing an `agentId`. Separate
|
|
379
|
+
`agentId` values are for actual independent subagents, not steps in one chat. Attaching over a
|
|
364
380
|
non-empty board saved a `Before session · …` snapshot; detaching emits the receipt (items
|
|
365
381
|
done / vetoed, a diff against that snapshot, one-click restore) — an idle timeout delays it.
|
|
366
382
|
- **403 = outside the scope fence.** The human may fence you to a region (`policy.scope`): writes
|
|
@@ -421,15 +437,18 @@ Use the visible workbench when the human is actively curating layout:
|
|
|
421
437
|
- Other open tabs appear as green cursors with a name tag (`/workbench?name=mia` sets yours);
|
|
422
438
|
a node you drag is locked for agents until you release it.
|
|
423
439
|
- The chrome is a persistent 52px left tool rail plus a slim 44px top bar (0.4.8+). The rail
|
|
424
|
-
carries the tools (Select V, Pan Space, Connect C),
|
|
425
|
-
|
|
440
|
+
carries the tools (Select V, Pan Space, Connect C), Markdown note M, Attach files, webpage W,
|
|
441
|
+
group G, and separate Draw A, Text note and Eraser buttons for board feedback,
|
|
426
442
|
and utilities: search & commands (Cmd+K — actions with shortcuts, then jump-to-node), arrange,
|
|
427
443
|
trace, minimap, history, the theme picker (nine themes: dark, light, high-contrast, midnight,
|
|
428
444
|
sepia, arctic, ember, forest, volt), and shortcuts (?). The top bar holds the connection dot,
|
|
429
445
|
workspace title, the session chip / gate badge / context meter while a session is attached
|
|
430
446
|
(or the external-writers indicator and *Start agent session* otherwise), and the zoom cluster
|
|
431
447
|
(zoom out, % label = reset, zoom in, fit F). Hovering a rail button shows a tooltip with its
|
|
432
|
-
shortcut.
|
|
448
|
+
shortcut or supported file types. Attach files shares the drop workflow: images, Markdown,
|
|
449
|
+
text/code and PDF/Office originals; PDF/Office extraction still requires the import request
|
|
450
|
+
and human review. Workspace file paths (Shift+F) and image URLs (I) remain in the canvas
|
|
451
|
+
menu and shortcuts; HTML surfaces remain available through H and agent tools.
|
|
433
452
|
On viewports ≤1180px the top bar drops its meta text and the session panel becomes a drawer;
|
|
434
453
|
every control stays in the rail at any width — there is no separate mobile menu.
|
|
435
454
|
- Keyboard: nodes are focusable — arrow keys move to the nearest node, Enter opens it in focus
|
|
@@ -525,7 +544,13 @@ are text-only links through `data.boardId`; they are not direct cross-board card
|
|
|
525
544
|
edges. `create-from` should be previewed first. It excludes prompt, response,
|
|
526
545
|
trace, and MCP-app cards; groups and README are optional; creation assigns fresh
|
|
527
546
|
IDs, copies no pins/AX/history, preserves the original, and leaves the new board
|
|
528
|
-
inactive.
|
|
547
|
+
inactive. Agents can open/switch boards; only the human deletes them. To create
|
|
548
|
+
and populate a board, call `canvas_board { action: "create", name: "Planning" }`,
|
|
549
|
+
then `canvas_board { action: "open", id: "<returned-board-id>" }` (CLI:
|
|
550
|
+
`pmx-canvas board open <board-id>`). Verify the returned `activeBoardId` before
|
|
551
|
+
writing. Opening changes the shared visible workbench and subsequent write
|
|
552
|
+
target, not a private agent selection. Recheck after interruptions or another
|
|
553
|
+
participant's navigation. Use `id: null` or CLI `board open --home` for Home.
|
|
529
554
|
|
|
530
555
|
Attribution is descriptive, not authority. `requestedBy`, `resolvedBy`, and
|
|
531
556
|
`selfAnswer` tell you who wrote an ask or answer; an agent answer is not human
|
|
@@ -640,5 +665,5 @@ Load only the reference relevant to the task:
|
|
|
640
665
|
- [GitHub Copilot adapter](references/github-copilot-app-adapter.md)
|
|
641
666
|
- [Codex app adapter](references/codex-app-adapter.md)
|
|
642
667
|
|
|
643
|
-
|
|
644
|
-
[
|
|
668
|
+
Use fresh MCP `tools/list` for the installed runtime's authoritative inventory and action schemas;
|
|
669
|
+
the [bundled full reference](references/full-reference.md) remains available in mirrored skills.
|
|
@@ -565,11 +565,11 @@
|
|
|
565
565
|
"id": 23,
|
|
566
566
|
"name": "multi-board-targeting",
|
|
567
567
|
"prompt": "I have Roadmap and Incident Review boards. Add a decision note only to Incident Review and set its tour to the Summary group. Do not disturb Roadmap.",
|
|
568
|
-
"expected_output": "Lists/gets boards, resolves the exact Incident Review board and
|
|
568
|
+
"expected_output": "Lists/gets boards, resolves the exact Incident Review board and calls canvas_board action open or CLI board open with its ID, verifies the returned activeBoardId, searches for the Summary group there, then adds the note and sets that board's tour. It explains that opening switches the shared view but preserves Roadmap content. It does not infer IDs from titles, mutate Roadmap, invent inactive-board write targeting, or assume that a board switch succeeded. It validates Incident Review and reports board/node IDs.",
|
|
569
569
|
"assertions": [
|
|
570
570
|
{
|
|
571
571
|
"name": "resolves-and-verifies-target-board",
|
|
572
|
-
"description": "
|
|
572
|
+
"description": "Resolves Incident Review with board list/get, explicitly opens its ID and verifies activeBoardId before writing; does not confuse read-only board targeting with write targeting",
|
|
573
573
|
"type": "output_check"
|
|
574
574
|
},
|
|
575
575
|
{
|
|
@@ -662,6 +662,116 @@
|
|
|
662
662
|
"type": "output_check"
|
|
663
663
|
}
|
|
664
664
|
]
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
"id": 27,
|
|
668
|
+
"name": "create-open-and-populate-board",
|
|
669
|
+
"prompt": "Create a new Release checklist board, open it for me, and add a checklist. Keep the content on my current board intact. Do this yourself without asking me to navigate.",
|
|
670
|
+
"expected_output": "Creates Release checklist, captures its returned ID, explicitly opens it with canvas_board action open or CLI board open, verifies activeBoardId, and only then adds the checklist. Creation alone does not switch boards. It verifies the checklist on the new board and leaves the original board content intact. It explains that the shared workbench now shows the new board, without asking the human to open it or claiming private per-agent targeting. Deletion authority remains human-only.",
|
|
671
|
+
"assertions": [
|
|
672
|
+
{
|
|
673
|
+
"name": "opens-created-id-before-authoring",
|
|
674
|
+
"description": "Uses the returned creation ID in an explicit open call and verifies activeBoardId before adding content",
|
|
675
|
+
"type": "output_check"
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
"name": "preserves-original-board",
|
|
679
|
+
"description": "Does not clear, delete, or add the checklist to the previously open board; verifies the new board's content",
|
|
680
|
+
"type": "output_check"
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
"name": "no-human-navigation-handoff",
|
|
684
|
+
"description": "Performs opening itself and describes the shared visible switch rather than requesting a human click or claiming a private target",
|
|
685
|
+
"type": "output_check"
|
|
686
|
+
}
|
|
687
|
+
]
|
|
688
|
+
},
|
|
689
|
+
{
|
|
690
|
+
"id": 28,
|
|
691
|
+
"name": "reuse-copilot-adapter-presence",
|
|
692
|
+
"prompt": "You are the main Copilot agent. The canvas presence list already has source copilot, sessionId copilot, label GitHub Copilot, attached true, with recent polling. Create a team overview on this board and show that you are working. You are not launching a subagent.",
|
|
693
|
+
"expected_output": "Reuses the existing adapter-owned copilot session. Reports task detail and phase on source copilot without creating a task-specific agentId or a second attached MCP session. Keeps steering addressed to consumer copilot. Does not detach the adapter-owned session after finishing; separate IDs are only for actual independent agents.",
|
|
694
|
+
"assertions": [
|
|
695
|
+
{
|
|
696
|
+
"name": "reuses-host-identity",
|
|
697
|
+
"description": "Updates source copilot with task detail instead of creating a second task-named identity or attaching a new session",
|
|
698
|
+
"type": "output_check"
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
"name": "preserves-delivery-and-lifetime",
|
|
702
|
+
"description": "Uses consumer copilot for addressed steering and leaves attach/detach lifecycle to the host adapter",
|
|
703
|
+
"type": "output_check"
|
|
704
|
+
}
|
|
705
|
+
]
|
|
706
|
+
},
|
|
707
|
+
{
|
|
708
|
+
"id": 29,
|
|
709
|
+
"name": "amp-workspace-trust-and-target",
|
|
710
|
+
"prompt": "The Canvas portal is working in this Amp orb, but tool_search finds no Canvas tools. The workspace MCP server is named review-canvas and amp mcp doctor says awaiting approval. The portal service health says workspace /work/review on port 4361; MCP is configured for /work/other on 4313. Fix the setup; can you just approve it or use global config?",
|
|
711
|
+
"expected_output": "Aligns the workspace amp.mcpServers entry with /work/review and port 4361, clearing stale PMX_CANVAS_URL, without altering unrelated settings or stopping the other workspace. Explains that workspace execution trust belongs to the human and gives amp mcp approve review-canvas for the orb workspace terminal after review. Does not execute approval or bypass it through global config/--mcp-config. After human approval, reloads MCP, requires connected and native tool discovery, verifies health identity and active board, and only then permits writes. Does not equate portal access or standalone MCP smoke with native integration.",
|
|
712
|
+
"assertions": [
|
|
713
|
+
{
|
|
714
|
+
"name": "preserves-human-trust-boundary",
|
|
715
|
+
"description": "Requests human review and amp mcp approve review-canvas in the executing workspace; neither self-approves nor changes configuration scope to bypass trust",
|
|
716
|
+
"type": "output_check"
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
"name": "aligns-actual-target",
|
|
720
|
+
"description": "Uses the observed /work/review workspace and 4361 local port, clears stale URL targeting and preserves unrelated settings/listeners",
|
|
721
|
+
"type": "output_check"
|
|
722
|
+
},
|
|
723
|
+
{
|
|
724
|
+
"name": "verifies-native-connection-after-approval",
|
|
725
|
+
"description": "Requires reload, connected status, actual Amp Canvas tools and matching health/board before writes; does not treat portal or standalone smoke as native proof",
|
|
726
|
+
"type": "output_check"
|
|
727
|
+
}
|
|
728
|
+
]
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
"id": 30,
|
|
732
|
+
"name": "amp-native-steering-evidence-boundary",
|
|
733
|
+
"prompt": "On this disposable board, native Amp MCP is connected and your source/consumer is amp-review. The browser pinned the Review card and sent amp-review a fresh instruction to append receipt CHECK-30. Prove the round trip, then confirm that an idle Amp thread will now wake automatically and that native Skills discovery works.",
|
|
734
|
+
"expected_output": "Uses actual Amp Canvas tools to read the pinned card, claims only as amp-review, signals intent and appends the requested receipt without losing existing content. Verifies the rendered receipt before marking that delivery for amp-review and confirms it is no longer pending. Uses a different recipient as a negative control and detaches only self-owned test sessions. Reports active-agent polling as verified but declines to infer idle-thread wake-up, persistent pumping or native MCP Skills discovery from this evidence.",
|
|
735
|
+
"assertions": [
|
|
736
|
+
{
|
|
737
|
+
"name": "checks-pin-and-addressed-delivery",
|
|
738
|
+
"description": "Reads the browser-pinned card via native Amp tools, claims using amp-review, and verifies a different recipient does not receive the addressed test message",
|
|
739
|
+
"type": "output_check"
|
|
740
|
+
},
|
|
741
|
+
{
|
|
742
|
+
"name": "verifies-edit-before-ack",
|
|
743
|
+
"description": "Signals intent, preserves card content, verifies CHECK-30 in the browser before marking delivery, then confirms no pending test message remains",
|
|
744
|
+
"type": "output_check"
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
"name": "does-not-overclaim-host-support",
|
|
748
|
+
"description": "Distinguishes active-agent polling from idle wake-up/background pumps and tools/resources from native Skills discovery; cleans up only test-owned sessions",
|
|
749
|
+
"type": "output_check"
|
|
750
|
+
}
|
|
751
|
+
]
|
|
752
|
+
},
|
|
753
|
+
{
|
|
754
|
+
"id": 31,
|
|
755
|
+
"name": "json-focus-runtime-state-continuity",
|
|
756
|
+
"prompt": "On a disposable board, verify that an unsaved JSON form and dynamic list survive inline-to-focus and focus-to-inline transitions. Seed draft Initial and one item; use the browser to enter DRAFT-MUST-SURVIVE and load 16 items, expand, then enter EXPANDED-MUST-SURVIVE and replace the list with three distinct items, and close. Does this also prove the draft survives a reload or spec replacement?",
|
|
757
|
+
"expected_output": "Runs the browser regression with bound input and echo plus runtime list values in both directions, without saving or updating the spec between transitions. Verifies exact values and counts, not only node size or server spec. Exercises src and srcdoc including AX-disabled JSON. Proves the regression fails with the old behavior before trusting green. Separately confirms an authored spec revision resets to the new seed and the server spec was not silently changed by runtime edits. Reports local focus continuity separately from durable AX state, page reload, other hosts, HTML and external apps; a defined eval is not a completed benchmark.",
|
|
758
|
+
"assertions": [
|
|
759
|
+
{
|
|
760
|
+
"name": "verifies-both-directions-in-browser",
|
|
761
|
+
"description": "Checks exact bound input, echo and asymmetric list contents/counts inline to focus and back without an intervening save or spec edit",
|
|
762
|
+
"type": "output_check"
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
"name": "checks-transport-and-revision-boundaries",
|
|
766
|
+
"description": "Runs src and srcdoc including AX-disabled JSON, verifies authored spec replacement resets runtime state and ordinary input edits leave server spec unchanged",
|
|
767
|
+
"type": "output_check"
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
"name": "discriminates-and-limits-claims",
|
|
771
|
+
"description": "Records a failure against old behavior and a passing fixed run; does not infer persistence across reloads, native-host support or scored model quality",
|
|
772
|
+
"type": "output_check"
|
|
773
|
+
}
|
|
774
|
+
]
|
|
665
775
|
}
|
|
666
776
|
]
|
|
667
777
|
}
|