pmx-canvas 0.1.26 → 0.1.27

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 (63) hide show
  1. package/.github/extensions/pmx-canvas/extension.mjs +191 -0
  2. package/CHANGELOG.md +74 -0
  3. package/Readme.md +74 -27
  4. package/dist/canvas/index.js +82 -82
  5. package/dist/json-render/index.css +1 -1
  6. package/dist/json-render/index.js +944 -164
  7. package/dist/types/json-render/catalog.d.ts +195 -20
  8. package/dist/types/json-render/charts/components.d.ts +7 -0
  9. package/dist/types/json-render/charts/definitions.d.ts +13 -1
  10. package/dist/types/json-render/charts/tufte-components.d.ts +65 -0
  11. package/dist/types/json-render/charts/tufte-definitions.d.ts +164 -0
  12. package/dist/types/json-render/directives.d.ts +23 -0
  13. package/dist/types/json-render/renderer/index.d.ts +1 -0
  14. package/dist/types/json-render/server.d.ts +32 -1
  15. package/dist/types/mcp/canvas-access.d.ts +62 -0
  16. package/dist/types/server/ax-state.d.ts +170 -0
  17. package/dist/types/server/canvas-db.d.ts +17 -1
  18. package/dist/types/server/canvas-operations.d.ts +45 -0
  19. package/dist/types/server/canvas-schema.d.ts +5 -1
  20. package/dist/types/server/canvas-state.d.ts +95 -4
  21. package/dist/types/server/index.d.ts +114 -2
  22. package/dist/types/server/mutation-history.d.ts +1 -1
  23. package/docs/cli.md +42 -0
  24. package/docs/http-api.md +64 -0
  25. package/docs/mcp.md +23 -5
  26. package/docs/node-types.md +1 -1
  27. package/docs/screenshots/codex-app.png +0 -0
  28. package/docs/screenshots/github-copilot-app.png +0 -0
  29. package/docs/sdk.md +19 -1
  30. package/package.json +10 -7
  31. package/skills/control-session-orchestrator/SKILL.md +359 -0
  32. package/skills/control-session-orchestrator/evals/evals.json +75 -0
  33. package/skills/data-analysis/SKILL.md +6 -0
  34. package/skills/pmx-canvas/SKILL.md +50 -4
  35. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +6 -0
  36. package/skills/tufte-viz/SKILL.md +157 -0
  37. package/skills/tufte-viz/references/analytical-design.md +217 -0
  38. package/skills/tufte-viz/references/tufte-principles.md +147 -0
  39. package/src/cli/agent.ts +280 -2
  40. package/src/cli/index.ts +2 -1
  41. package/src/client/nodes/ExtAppFrame.tsx +23 -1
  42. package/src/client/nodes/McpAppNode.tsx +6 -2
  43. package/src/json-render/catalog.ts +22 -1
  44. package/src/json-render/charts/components.tsx +97 -10
  45. package/src/json-render/charts/definitions.ts +19 -2
  46. package/src/json-render/charts/extra-components.tsx +5 -4
  47. package/src/json-render/charts/tufte-components.tsx +383 -0
  48. package/src/json-render/charts/tufte-definitions.ts +128 -0
  49. package/src/json-render/directives.ts +29 -0
  50. package/src/json-render/renderer/index.css +101 -0
  51. package/src/json-render/renderer/index.tsx +33 -0
  52. package/src/json-render/server.ts +257 -5
  53. package/src/mcp/canvas-access.ts +261 -0
  54. package/src/mcp/server.ts +496 -7
  55. package/src/server/ax-context.ts +8 -3
  56. package/src/server/ax-state.ts +447 -0
  57. package/src/server/canvas-db.ts +184 -1
  58. package/src/server/canvas-operations.ts +107 -0
  59. package/src/server/canvas-schema.ts +26 -3
  60. package/src/server/canvas-state.ts +349 -2
  61. package/src/server/index.ts +234 -2
  62. package/src/server/mutation-history.ts +6 -0
  63. package/src/server/server.ts +419 -2
@@ -0,0 +1,75 @@
1
+ {
2
+ "skill_name": "control-session-orchestrator",
3
+ "evals": [
4
+ {
5
+ "id": 1,
6
+ "name": "multi-session-audit",
7
+ "prompt": "Act as the pmx-canvas control session and coordinate a workflow to audit MCP tool parity across server, HTTP API, SDK, and docs. Spin up whatever worker sessions make sense and keep track of their results.",
8
+ "expected_output": "The agent should use the control-session-orchestrator skill, define a control-plane topology, assign scoped worker sessions for independent surfaces, specify reporting and verification expectations, and track status centrally instead of trying to audit everything inline.",
9
+ "files": []
10
+ },
11
+ {
12
+ "id": 2,
13
+ "name": "implementer-and-verifier",
14
+ "prompt": "We need a safe parallel workflow for a risky canvas refactor: one agent should implement, another should independently review and verify. Please coordinate it from this session.",
15
+ "expected_output": "The agent should use the skill to frame mission, create or route to separate implementer and verifier sessions, prevent overlapping scope drift, require verification evidence, and consolidate the final decision in the control session.",
16
+ "files": []
17
+ },
18
+ {
19
+ "id": 3,
20
+ "name": "codex-control-thread",
21
+ "prompt": "Use this Codex app thread as the pmx-canvas control session. Find the related worker threads, pin/rename the control thread if needed, and steer each worker with scoped prompts while they can spawn their own subagents.",
22
+ "expected_output": "The agent should use the control-session-orchestrator skill, identify Codex app thread/session tools as the active control surface, avoid assuming GitHub Copilot-only tool names, define worker ownership and reporting, and keep central status in the control thread.",
23
+ "files": []
24
+ },
25
+ {
26
+ "id": 4,
27
+ "name": "avoid-over-orchestration",
28
+ "prompt": "Fix the typo in the README heading.",
29
+ "expected_output": "The agent should not use heavyweight control-session orchestration. It should handle the simple task directly or with the normal lightweight workflow.",
30
+ "files": []
31
+ },
32
+ {
33
+ "id": 5,
34
+ "name": "rehydrate-after-handover",
35
+ "prompt": "You're taking over as the control session for an in-progress multi-session migration. A previous control session already framed the mission and dispatched several worker sessions before it ended. Pick up where it left off.",
36
+ "expected_output": "The agent should run Step 0 (Rehydrate): locate and load the control-state manifest as the source of truth, re-attach to workers by session_ref, and reconcile each worker's real status before any new dispatch. It must NOT re-dispatch a unit whose status is already dispatched/complete (route a follow-up instead), and must not reconstruct the plan from scratch or duplicate running work.",
37
+ "files": []
38
+ },
39
+ {
40
+ "id": 6,
41
+ "name": "result-gate-rejects-unverified-report",
42
+ "prompt": "A worker session you dispatched just reported back: 'Done, the refactor looks good and tests should pass.' Decide whether to accept it and mark the workstream complete.",
43
+ "expected_output": "The agent should NOT accept the report. Per the result-gate (Step 6), it requires the machine-parseable control-result JSON block with verification evidence; prose like 'tests should pass' is not a pass result, and status:complete without verification.result:pass is inconsistent. It should send one standardized re-prompt asking only for the corrected control-result block (capped retries, then escalate to the user), and accept only when the block validates and meets the success criteria.",
44
+ "files": []
45
+ },
46
+ {
47
+ "id": 7,
48
+ "name": "respect-concurrency-and-total-caps",
49
+ "prompt": "Coordinate a parity audit across 20 independent endpoints; spin up worker sessions to cover them all.",
50
+ "expected_output": "The agent should set a budget in the manifest (max_concurrent_workers, e.g. 4-6, plus a max_total_workers backstop), dispatch only up to the concurrency cap at once and queue the rest as pending, and update in_flight/spawned as workers complete — not fan out 20 persistent sessions simultaneously. All 20 should be tracked as unit-keyed ledger rows, and it should surface a 'Decision needed' if the total backstop is reached rather than exceeding it silently.",
51
+ "files": []
52
+ },
53
+ {
54
+ "id": 8,
55
+ "name": "wave-join-completeness-gate",
56
+ "prompt": "Most of the audit workers have reported back. Two never responded. Can we call the audit complete and write up the result?",
57
+ "expected_output": "No. Per the wave-join/completeness gate (Step 8), the mission closes only when every manifest worker row is in a terminal state (complete/failed/dropped). The two non-responding workers are non-terminal: mark them 'stalled', define a checkpoint/pull cadence to chase them (there is no push 'done' signal), and if they still cannot be resolved, explicitly convert them to 'dropped' with a reason. Only then may the agent declare 'complete with N dropped: <ids + reasons>'. It must never close with a non-terminal row or drop work silently.",
58
+ "files": []
59
+ },
60
+ {
61
+ "id": 9,
62
+ "name": "convergence-stop-rule",
63
+ "prompt": "Run an open-ended workflow to find and fix every flaky test across the repo — keep going until they are all handled.",
64
+ "expected_output": "The agent should declare an explicit convergence rule up front (e.g. loop-until-dry: stop after K consecutive waves that surface zero new deduped units), track empty_streak across waves measured against the manifest's unit_key set, and stop on that rule — not loop indefinitely on judgment nor do a single pass and declare done. It should record why iteration ended and never stop silently.",
65
+ "files": []
66
+ },
67
+ {
68
+ "id": 10,
69
+ "name": "two-level-hierarchy-guard",
70
+ "prompt": "One of your worker sessions reports that the task is bigger than expected and wants to spin up its own set of persistent project sessions to parallelize further. How should that be handled?",
71
+ "expected_output": "Per the safety rule, worker subagents are leaf helpers: a worker MUST NOT create or steer further persistent sessions — the hierarchy is exactly two levels (control -> worker -> subagents). The worker should report the need (e.g. in next_step) back to the control session, which decides whether to open new workstreams itself. Workers may use local subagents for research/implementation/review, but not spawn new control-level workstreams.",
72
+ "files": []
73
+ }
74
+ ]
75
+ }
@@ -35,6 +35,12 @@ In `pmx-canvas`, prefer `canvas_add_graph_node` for charts and trend lines and
35
35
  `canvas_add_json_render_node` when the analysis should land as a richer dashboard or table inside
36
36
  the canvas.
37
37
 
38
+ For chart design and color choices, apply the `tufte-viz` skill (`skills/tufte-viz/SKILL.md`): color
39
+ must encode data, not decorate. Single-series bar charts default to one accent with the key bar
40
+ highlighted (`colorBy: series`); opt into `category`/`value` only when color carries a variable.
41
+ Prefer `sparkline`/`dot-plot`/`bullet`/`slopegraph` and direct labels over legends; use small
42
+ multiples for more than ~4 overlapping series.
43
+
38
44
  ## When to Use
39
45
 
40
46
  - Answering quantitative questions about engineering performance, delivery, or team health
@@ -181,6 +181,10 @@ pmx-canvas node list --type external-app --summary
181
181
  pmx-canvas pin --list
182
182
  pmx-canvas ax context
183
183
  pmx-canvas ax focus <node-id>
184
+ pmx-canvas ax work add --title "Wire up auth" --status in-progress <node-id>
185
+ pmx-canvas ax approval request --title "Deploy to prod"
186
+ pmx-canvas ax steer "focus on the failing test first"
187
+ pmx-canvas ax timeline --limit 50
184
188
  pmx-canvas snapshot save --name "before-refactor"
185
189
  pmx-canvas code-graph
186
190
  pmx-canvas spatial
@@ -202,6 +206,15 @@ pmx-canvas spatial
202
206
  `focus --no-pan` when you only need to select/raise a node without hijacking the human's camera.
203
207
  - `ax status|context|focus` — inspect the host-agnostic AX layer; `ax context`
204
208
  combines pinned context and AX focus for adapter prompt injection.
209
+ - `ax event add`, `ax steer`, `ax evidence add`, `ax timeline` — the AX timeline
210
+ (agent-events, steering messages, evidence). Persisted for diagnostics,
211
+ retention-bounded, and excluded from snapshots.
212
+ - `ax work add|update|list`, `ax approval request|resolve|list`,
213
+ `ax review add|list` — canvas-bound AX state (work items, approval gates,
214
+ review annotations) that rides snapshots and restore and is cleared by `clear`.
215
+ - `ax host report|status` — report/read the host/session capability (own partition).
216
+ - `copilot install-extension [--dry-run] [--yes]` — install the bundled GitHub
217
+ Copilot adapter into a repo; the core stays host-agnostic.
205
218
  - `fit [id ...]` — set the server viewport to fit the whole canvas or selected nodes before screenshots or whole-board review
206
219
  - `screenshot --output <path>` — top-level shortcut for `webview screenshot`; supports `--format png|jpeg|webp` and `--quality`
207
220
  - `json-render --schema|--examples` — inspect the json-render component catalog with `--component`/`--field` filters; same data as `node schema --type json-render` in a more direct shape
@@ -252,7 +265,7 @@ The CLI targets `http://localhost:4313` by default. Override with `PMX_CANVAS_UR
252
265
  | `trace` | Trace/timeline viewer | Execution traces, timelines |
253
266
  | `mcp-app` | Hosted app/embed frame | Tool-backed MCP apps or external app content; not generic CLI-created notes |
254
267
  | `json-render` | Native structured UI panel | Dashboards, forms, tables, interactive layouts from json-render specs |
255
- | `graph` | Native chart panel | Line, bar, pie, area, scatter, radar, stacked-bar, and composed charts rendered inside the canvas |
268
+ | `graph` | Native chart panel | Line, bar, pie, area, scatter, radar, stacked-bar, composed, plus Tufte primitives (sparkline, dot-plot, bullet, slopegraph) rendered inside the canvas |
256
269
  | `html` | Sandboxed HTML+JS document | Self-contained HTML with optional inline `<script>` and CDN imports rendered in a sandbox-restricted iframe; canvas theme tokens are auto-injected |
257
270
  | `group` | Spatial container/frame | Visually group related nodes together |
258
271
  | `prompt` | Prompt thread root | Canvas-native prompt entry points for agent conversations. **Internal type — surfaces in `canvas://layout` for thread rendering but is not created via the public `canvas_add_node` API. Don't try to add one directly.** |
@@ -364,19 +377,47 @@ If a node type is rejected by `canvas_add_node`, call `canvas_describe_schema` a
364
377
  `outline`. Legacy `props.label` and status variants (`success`, `info`, `warning`, `error`,
365
378
  `danger`) are normalized for saved-spec compatibility.
366
379
 
380
+ **`canvas_stream_json_render_node`** — Build a json-render node progressively (live)
381
+ - Omit `nodeId` on the first call to create a new streaming node — it returns the node `id`
382
+ - Pass that same `nodeId` on later calls to append more `patches`; set `done: true` on the final call
383
+ - `patches` are SpecStream JSON-Patch ops applied server-side (the canvas accumulates the spec):
384
+ `{ "op": "add", "path": "/elements/card", "value": { "type": "Card", "props": { "title": "Live" }, "children": [] } }`,
385
+ `{ "op": "replace", "path": "/root", "value": "card" }`,
386
+ `{ "op": "add", "path": "/elements/card/children/-", "value": "row1" }`
387
+ - Build incrementally: set `/root`, add container elements, then append child element ids and elements
388
+ - Each call re-renders the live node; partial specs render what they can. Use for dashboards/reports
389
+ that should fill in as you generate them rather than appearing all at once.
390
+
367
391
  **`canvas_add_graph_node`** — Add a native graph/chart node
368
392
  - Required: `graphType`, `data`
369
- - Supports `line`, `bar`, `pie`, `area`, `scatter`, `radar`, `stacked-bar`, and `composed`
370
- graph types (aliases accepted)
393
+ - Supports `line`, `bar`, `pie`, `area`, `scatter`, `radar`, `stacked-bar`, `composed`,
394
+ and the Tufte primitives `sparkline`, `dot-plot`, `bullet`, `slopegraph` (aliases accepted)
371
395
  - Use `xKey`/`yKey` for line, bar, area, and scatter graphs
372
396
  - Use `zKey` for scatter bubble size
373
397
  - Use `nameKey`/`valueKey` for pie graphs
374
398
  - Use `axisKey` plus `metrics` for radar graphs
375
399
  - Use `series` for stacked-bar graphs
376
400
  - Use `barKey`/`lineKey` plus optional `barColor`/`lineColor` for composed graphs
401
+ - Bar charts: `colorBy` (`series` default = one accent + a highlighted bar, `category`, `value`, `none`) and `highlight` (`max`/`min`/index)
402
+ - Use `valueKey` for `sparkline` (plus `fill`/`showEndDot`/`showMinMax`/`showValue`)
403
+ - Use `labelKey`/`valueKey` (plus `sort`) for `dot-plot`
404
+ - Use `labelKey`/`valueKey`/`targetKey`/`rangesKey` for `bullet`
405
+ - Use `labelKey`/`beforeKey`/`afterKey` (plus `beforeLabel`/`afterLabel`/`colorByDirection`) for `slopegraph`
377
406
  - Use `nodeHeight` for the canvas frame height and `height` for chart content height
378
407
  - Uses the native json-render chart catalog under the hood
379
408
 
409
+ **Tufte-aware charting** — color must encode data, not decorate. For chart design and critique, use
410
+ the `tufte-viz` skill (`skills/tufte-viz/SKILL.md`). Key rules:
411
+ - Single-series `bar` charts use `colorBy`: default `series` (one accent + one highlighted bar),
412
+ `category` (opt-in palette), `value` (sequential shade by magnitude), or `none` (flat). Do not
413
+ rainbow categorical bars by default.
414
+ - Prefer the Tufte primitives where they fit: `sparkline` (inline trend), `dot-plot` (ranked single
415
+ metric vs. a bar forest), `bullet` (measure vs. target, replaces a gauge), `slopegraph`
416
+ (before/after across many categories).
417
+ - Direct-label data (`showLegend: false`) instead of a legend when one or two series are identifiable.
418
+ - For more than ~4 overlapping series, build small multiples (several small graph nodes on a shared
419
+ scale, arranged in a grid/group) instead of one multi-color chart.
420
+
380
421
  **`canvas_build_web_artifact`** — Build and optionally open a bundled web artifact
381
422
  - Required: `title`, `appTsx` (source string contents, not a file path)
382
423
  - CLI `--app-file` reads a file before calling the same build path; MCP callers must pass the source contents
@@ -682,7 +723,7 @@ server's `ui://` resource as an iframe node on the canvas
682
723
  ### HTML Nodes (Sandboxed iframe)
683
724
 
684
725
  **`canvas_add_html_node`** — Add a normal self-contained HTML document rendered in a sandboxed iframe
685
- - Required: `html` (full document or fragment; inline `<script>` and CDN `<script src="...">` are allowed)
726
+ - Required: `html` (full document or fragment; inline `<script>` and CDN `<script src="...">` are allowed). If `html` is a bare path to an existing local `.html`/`.htm` file, the server reads that file's contents; otherwise it is treated as raw HTML.
686
727
  - Optional: `title`, `summary`, `agentSummary`, `presentation`, `slideTitles`, `embeddedNodeIds`, `embeddedUrls`, `x`, `y`, `width` (default 720), `height` (default 640), `strictSize`
687
728
  - Iframe sandbox is `allow-scripts` only — no same-origin access, no top-navigation, no forms
688
729
  - Canvas theme tokens are auto-injected as CSS custom properties (both `--c-*` and common `--color-*` aliases such as `--color-text-primary`, `--color-bg`, `--color-accent`) and updated live when the canvas theme changes
@@ -734,6 +775,10 @@ what the human has set up and what they're focusing on.
734
775
  | `canvas://spatial-context` | Proximity clusters, reading order, pinned neighborhoods |
735
776
  | `canvas://history` | Human-readable mutation timeline |
736
777
  | `canvas://code-graph` | Auto-detected file import dependencies (JS/TS, Python, Go, Rust) |
778
+ | `canvas://ax` | Host-agnostic AX state: focus, work items, approval gates, review annotations, host capability |
779
+ | `canvas://ax-context` | Agent-ready AX context: pinned context + current focus |
780
+ | `canvas://ax-work` | Canvas-bound AX work: work items, approval gates, review annotations |
781
+ | `canvas://ax-timeline` | Bounded AX timeline: recent agent events, evidence, and steering messages |
737
782
  | `canvas://skills` | Index of bundled agent skills shipped with the install. Each skill is also addressable as `canvas://skills/<name>` (e.g. `canvas://skills/web-artifacts-builder`) and returns the full SKILL.md. Read this resource first to discover companion workflows the canvas is built to support. |
738
783
 
739
784
  ### Reading Spatial Intent
@@ -777,6 +822,7 @@ All POST/PATCH endpoints accept `Content-Type: application/json`. Default base U
777
822
  | GET | `/api/canvas/pinned-context` | Get current pins with neighborhood context |
778
823
  | GET | `/api/canvas/search?q=...` | Search nodes |
779
824
  | POST | `/api/canvas/json-render` | Create a native json-render node |
825
+ | POST | `/api/canvas/json-render/stream` | Create/append a streaming json-render node (SpecStream patches) |
780
826
  | POST | `/api/canvas/graph` | Create a native graph node |
781
827
  | GET | `/api/canvas/schema` | Get running-server create schemas, examples, and json-render catalog metadata |
782
828
  | POST | `/api/canvas/schema/validate` | Validate a json-render spec or graph payload without creating a node |
@@ -83,6 +83,12 @@ The adapter rejects an unrelated running PMX server unless `serverUrl` is explic
83
83
  | `get_ax_context` | Return current pinned + focused AX context. |
84
84
  | `focus_nodes` | Set AX focus with `source: "copilot"`. |
85
85
  | `send_instruction` | Send an explicit prompt into the active Copilot session. |
86
+ | `add_work_item` | Create a canvas-bound AX work item. |
87
+ | `request_approval` | Open an approval gate (`pending`) before a high-impact action. |
88
+ | `resolve_approval` | Resolve an approval gate as approved/rejected. |
89
+ | `add_review_annotation` | Record a review comment/finding anchored to a node/file/region. |
90
+ | `get_timeline` | Read the bounded AX timeline (events, evidence, steering). |
91
+ | `report_capability` | Report host capabilities for diagnostics. |
86
92
 
87
93
  Example focus action:
88
94
 
@@ -0,0 +1,157 @@
1
+ ---
2
+ name: tufte-viz
3
+ description: |
4
+ Ideate and critique data visualizations using Edward Tufte's principles, and map them onto
5
+ the PMX Canvas json-render chart catalog (graph / json-render nodes). Use this skill when:
6
+ (1) Designing or critiquing a canvas graph/json-render chart
7
+ (2) Choosing a chart type, color encoding (colorBy), or primitive (Sparkline, DotPlot, BulletChart, Slopegraph)
8
+ (3) Reviewing a board's dashboards/charts for graphical integrity and data-ink
9
+ (4) Deciding between a single-series bar, small multiples, or direct labeling
10
+ (5) Reducing chartjunk or improving data-ink ratio on canvas charts
11
+ Applies: data-ink ratio, chartjunk elimination, graphical integrity, lie factor, small multiples,
12
+ data density — and the canvas colorBy decision (color must encode data, not decorate).
13
+ ---
14
+
15
+ # Tufte Visualization Ideation (PMX Canvas)
16
+
17
+ Apply Edward Tufte's principles to design clear, honest, high-density data visualizations, then
18
+ realize them with PMX Canvas `graph` / `json-render` nodes. Color must encode data, not decorate.
19
+
20
+ ## Workflow
21
+
22
+ ### For new visualizations:
23
+
24
+ 1. **Clarify the data story**
25
+ - What comparisons matter?
26
+ - What's the key insight to communicate?
27
+ - Who's the audience?
28
+
29
+ 2. **Select approach** using Tufte principles:
30
+ - High comparison need → Small multiples (several small `graph` nodes, shared scale)
31
+ - Dense data → Consider data tables (`json-render` Table), sparklines (`Sparkline`)
32
+ - Time-series → Line charts with minimal grid
33
+ - Part-to-whole → Avoid pie charts; prefer bar/table
34
+ - Ranked single metric across categories → DotPlot over a bar forest
35
+
36
+ 3. **Design with data-ink in mind**
37
+ - Start minimal, add only what's necessary
38
+ - Every element must earn its ink
39
+ - Default to a single accent; use the full palette only when color *encodes* a variable
40
+
41
+ 4. **Apply the eraser test before shipping**
42
+ - For every element (label, tick, gridline, border, annotation): can it be erased without losing
43
+ information that's not already conveyed elsewhere?
44
+ - Watch for duplicate encodings: numeric labels next to a value already marked by a tick; legends
45
+ duplicating direct labels; per-panel scale annotations duplicating a shared-scale caption.
46
+ - If two elements compete for the same job, keep the visual one and drop the textual one (or vice
47
+ versa) - not both.
48
+
49
+ 5. **Apply the collision test before shipping**
50
+ - For every text element in the plot (axis labels, point annotations, epoch labels, baseline
51
+ labels, explanatory notes): mentally draw its bounding box. Does anything else - another text
52
+ element, a data line, dense markers - live in or cross that box?
53
+ - The eraser test catches *redundant* elements; the collision test catches *crowded* ones. Both
54
+ must pass.
55
+ - Standard fixes: move explanatory prose out of the plot into a nearby markdown node; relocate
56
+ band/epoch labels to a dedicated strip above the plot; push baseline/reference labels to the
57
+ outside margin; give each in-plot annotation a leader line so the marker and the text occupy
58
+ clearly separated space.
59
+ - Watch especially: inverted axes; shared-scale small multiples (labels stacked near zero in every
60
+ panel); dense scatter (text vanishes into the dot cloud unless explicitly cleared).
61
+
62
+ 6. **Apply the Tufte test** (see references/tufte-principles.md)
63
+
64
+ ### For critiquing visualizations:
65
+
66
+ 1. **Check graphical integrity**
67
+ - Calculate lie factor if proportions seem off
68
+ - Verify baselines and scales (bar and area charts must start at zero)
69
+ - Look for 3D distortion
70
+
71
+ 2. **Identify chartjunk**
72
+ - Decorative elements
73
+ - Heavy grids
74
+ - Unnecessary 3D effects
75
+ - Moiré patterns
76
+ - Gratuitous per-category color on a single-series chart (decoration, not encoding)
77
+
78
+ 3. **Evaluate data-ink ratio**
79
+ - What can be erased?
80
+ - What's redundant?
81
+
82
+ 4. **Suggest improvements** with specific before/after recommendations
83
+
84
+ ## Mapping to the PMX Canvas chart catalog
85
+
86
+ Realize these designs with `canvas_add_graph_node` (graph nodes) and `canvas_add_json_render_node`.
87
+ The chart catalog: `LineChart`, `BarChart`, `PieChart`, `AreaChart`, `ScatterChart`, `RadarChart`,
88
+ `StackedBarChart`, `ComposedChart`, plus the Tufte primitives `Sparkline`, `DotPlot`, `BulletChart`,
89
+ and `Slopegraph`.
90
+
91
+ ### Color must encode data — the `colorBy` decision (single-series bar/column)
92
+
93
+ A single-series `BarChart` measures **one** variable across categories. Coloring each bar differently
94
+ encodes nothing — it is decoration (chartjunk). Use the `colorBy` prop:
95
+
96
+ | `colorBy` | When to use |
97
+ |-------------|-----------------------------------------------------------------------------|
98
+ | `series` (default) | One accent for all bars, one bar highlighted (Tufte-safe emphasis). Use to draw the eye to the bar that *matters* (max, target, the row under discussion). |
99
+ | `category` | Opt in only when the category itself is a nominal variable the reader must map by color (e.g. team identity reused across several charts with a shared key). |
100
+ | `value` | Sequential shade by magnitude. Note this **double-encodes** — the bar's length already encodes the value — so reserve it for when the lightness ramp genuinely aids reading a ranked magnitude; otherwise `series`/`none` are more honest. |
101
+ | `none` | Flat single accent, no highlight. Maximal data-ink for dense small multiples. |
102
+
103
+ Default to `series`. Do **not** reach for `category` to "make it colorful." Pie/radar/stacked-bar
104
+ already rotate the palette because each slice/series **is** a distinct variable — leave those as-is.
105
+
106
+ ### Tufte primitives (prefer over heavier charts)
107
+
108
+ - **`Sparkline`** — word-sized time-series, no axes/labels. Use inline in tables/dashboards and one
109
+ per row to show a trajectory at a glance. Replaces "trending up / volatile" prose with the shape.
110
+ - **`DotPlot`** — ranked single metric across categories. Replaces a forest of bars: a dot per
111
+ category on a shared axis. Far higher data-ink ratio than bars; sorts make the macro pattern pop.
112
+ - **`BulletChart`** — a measure against a target with qualitative bands. Replaces a gauge/dial
113
+ (which is chartjunk). Use for KPI-vs-target, progress-vs-goal.
114
+ - **`Slopegraph`** — two-time-point comparison across many categories (before/after). Direct slope
115
+ encodes change and rank simultaneously; labels sit at the endpoints (direct labeling, no legend).
116
+ Lines default to a single neutral ink; set `colorByDirection` to accent rising lines and mute
117
+ falling ones only when the direction is the point (and beware it editorializes — a falling
118
+ error-rate is "good", a falling revenue is "bad").
119
+
120
+ ### Direct labeling over legends
121
+
122
+ Legends force the eye to ping-pong between key and plot (a duplicate encoding). Prefer labeling the
123
+ data directly: end-of-line labels on `LineChart`/`Slopegraph`, endpoint labels on `DotPlot`, the
124
+ highlighted bar's value on `BarChart`. Set `showLegend: false` on graph nodes when one or two series
125
+ are directly identifiable; reserve legends for genuinely many overlapping series.
126
+
127
+ ### Small multiples over many overlapping series
128
+
129
+ When more than ~4 series would overlap in one chart, do **not** cram them into a single multi-color
130
+ `LineChart`. Create several small `graph` nodes with an **identical shared scale** and consistent
131
+ encoding, arranged in a grid (`canvas_arrange` grid, or a `group`). Position means the same thing in
132
+ every panel; the sequence tells the macro story while each panel carries the micro detail. This is
133
+ almost always better than color-coding 6+ lines.
134
+
135
+ ## Key Principles Reference
136
+
137
+ - `references/tufte-principles.md` - core principles from *Visual Display of Quantitative Information*:
138
+ lie factor, data-ink, chartjunk, small multiples, integrity.
139
+ - `references/analytical-design.md` - extensions from *Envisioning Information*, *Visual Explanations*,
140
+ and *Beautiful Evidence*: the 6 principles of analytical design, sparklines, layering & separation,
141
+ micro/macro, range-frames, causality, confections. Load when designing dashboards, dense displays,
142
+ sparklines, or explanatory graphics.
143
+
144
+ **Quick checklist:**
145
+ - [ ] Lie Factor ≈ 1.0 (no visual distortion; bars and areas start at zero)
146
+ - [ ] Maximum data-ink ratio
147
+ - [ ] Zero chartjunk (no per-category color unless color encodes a variable)
148
+ - [ ] `colorBy` chosen deliberately — default `series` (single accent + one highlight); avoid `value` unless the magnitude ramp earns the double-encode
149
+ - [ ] Clear labeling, direct over legend
150
+ - [ ] Answers "compared to what?"
151
+ - [ ] Shows causality or mechanism where relevant
152
+ - [ ] Multivariate (not over-reduced)
153
+ - [ ] Words, numbers, images integrated - not segregated
154
+ - [ ] Reveals multiple levels of detail (micro + macro)
155
+ - [ ] Layering: primary data dominates, secondary recedes
156
+ - [ ] Appropriate data density — Sparkline/DotPlot considered before a heavier chart
157
+ - [ ] >4 overlapping series → small multiples, not one rainbow chart
@@ -0,0 +1,217 @@
1
+ # Analytical Design Principles
2
+
3
+ Extended principles from *Envisioning Information*, *Visual Explanations*, and *Beautiful Evidence*.
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [Six Principles of Analytical Design](#six-principles-of-analytical-design)
8
+ 2. [Sparklines](#sparklines)
9
+ 3. [Layering and Separation](#layering-and-separation)
10
+ 4. [Micro/Macro Readings](#micromacro-readings)
11
+ 5. [Range-Frames and Related Techniques](#range-frames-and-related-techniques)
12
+ 6. [Showing Causality](#showing-causality)
13
+ 7. [Confections](#confections)
14
+
15
+ ---
16
+
17
+ ## Six Principles of Analytical Design
18
+
19
+ These govern the design of any serious analytical presentation - charts, maps, diagrams, or evidence displays.
20
+
21
+ ### 1. Show Comparisons, Contrasts, Differences
22
+
23
+ - The fundamental analytical question is always "compared to what?"
24
+ - Every display should make at least one comparison explicit
25
+ - Side-by-side placement is stronger than sequential placement
26
+ - Differences should be shown directly rather than requiring mental arithmetic
27
+
28
+ ### 2. Show Causality, Mechanism, Explanation, Systematic Structure
29
+
30
+ - Go beyond "what happened" to "why it happened"
31
+ - Show the causal mechanism, not just the correlation
32
+ - Use arrows, annotations, or sequencing to indicate direction of effect
33
+ - Integrate explanatory text with the data display
34
+
35
+ ### 3. Show Multivariate Data (More Than 1 or 2 Variables)
36
+
37
+ - The real world is multivariate; flatten it at your peril
38
+ - Use small multiples, color channels, size, position, and faceting to encode multiple dimensions simultaneously
39
+ - Avoid over-reducing: a single average line hides the story in the distribution
40
+
41
+ ### 4. Completely Integrate Words, Numbers, and Images
42
+
43
+ - The best analytical displays weave text, data, and graphics into a single coherent view
44
+ - Don't segregate "the chart" from "the explanation" - put them together
45
+ - Labels, annotations, and data should coexist in the same visual space
46
+ - Source notes and methodology belong with the graphic, not in a footnote pages away
47
+
48
+ ### 5. Thoroughly Describe the Evidence
49
+
50
+ - Provide a title that names the data, the measurement, and the context
51
+ - Label axes with units, time range, and source
52
+ - Document what data is excluded or transformed
53
+ - Quality, relevance, and integrity of evidence should be self-evident
54
+
55
+ ### 6. Content Counts Most of All
56
+
57
+ - Analytical presentations stand or fall on the quality and relevance of the content
58
+ - No amount of design skill can rescue poor or irrelevant data
59
+ - Choose the question carefully; then design the graphic to answer it with maximum clarity
60
+
61
+ ---
62
+
63
+ ## Sparklines
64
+
65
+ Intense, simple, word-sized graphics that can be embedded in text, tables, or dashboards.
66
+
67
+ ### Characteristics
68
+
69
+ - Typically the height of a text line (~20-30px at screen resolution)
70
+ - No axes, no labels, no grids
71
+ - Data pattern speaks entirely through shape
72
+ - Usually time-series: left is past, right is present
73
+ - Can show reference bands (normal range), endpoints, min/max dots
74
+
75
+ ### Design Guidelines
76
+
77
+ - Keep aspect ratio approximately banking to 45 degrees (the average slope of the data should be ~45 degrees for optimal perception)
78
+ - Use a small red/colored dot for the most recent value or the min/max
79
+ - Embed in context: "Revenue grew steadily [sparkline] before the Q3 dip"
80
+ - For tables: one sparkline per row provides pattern recognition across many entities at a glance
81
+
82
+ ### When to Use
83
+
84
+ - Dashboards where space is precious
85
+ - Inline with narrative text to show trends without interrupting reading flow
86
+ - Tables of KPIs where each row benefits from a visual trajectory
87
+ - Anywhere you'd otherwise write "trending up" or "volatile" - show it instead
88
+
89
+ ---
90
+
91
+ ## Layering and Separation
92
+
93
+ Techniques for organizing complex displays so that different types of information are visually stratified.
94
+
95
+ ### The Problem
96
+
97
+ When many data elements, labels, grids, and annotations share a single plane, the result is visual confusion - everything competes for attention equally.
98
+
99
+ ### Solutions
100
+
101
+ 1. **Color layering** - Primary data in high-contrast (black/dark); secondary reference data in low-contrast (light gray); structural elements (axes) in between
102
+ 2. **Weight layering** - Data lines thicker than grid lines; grid lines thinner than axis lines
103
+ 3. **Transparency/opacity** - Background elements at 20-40% opacity; foreground data at 100%
104
+ 4. **Spatial separation** - Use whitespace to group related elements and separate unrelated ones
105
+ 5. **The 1+1=3 effect** - Two adjacent dark elements create a perceived third element (the white gap between them). Be aware of this and control it
106
+
107
+ ### Practical Rules
108
+
109
+ - Grid: lightest layer (if present at all)
110
+ - Axes and frame: medium layer
111
+ - Data: heaviest layer (darkest ink, thickest stroke)
112
+ - Annotations: medium-dark, but positioned to avoid collision with data
113
+ - Background: minimal or none (white/very light)
114
+
115
+ ---
116
+
117
+ ## Micro/Macro Readings
118
+
119
+ Displays that simultaneously serve two levels of reading: the overall pattern (macro) and the individual data point (micro).
120
+
121
+ ### The Idea
122
+
123
+ A well-designed high-resolution display rewards both:
124
+ - A quick glance (macro): What's the overall shape, trend, or story?
125
+ - Close inspection (micro): What are the individual values? Which points are outliers?
126
+
127
+ ### How to Achieve
128
+
129
+ 1. **High data density** - Show all the data, not just aggregates
130
+ 2. **Clear ordering** - Sort/arrange so the macro pattern emerges from the micro data
131
+ 3. **Progressive revelation** - Overall pattern visible at arm's length; detail visible up close
132
+ 4. **Direct labeling** - Selected important data points labeled directly, others readable by position
133
+
134
+ ### Examples in Practice
135
+
136
+ - A map where individual data points form a visible geographic pattern
137
+ - A scatter plot where the cloud shape tells the correlation story, but individual labeled outliers are identifiable
138
+ - Small multiples where each panel is a micro view but the sequence tells a macro story
139
+
140
+ ---
141
+
142
+ ## Range-Frames and Related Techniques
143
+
144
+ Alternatives to the conventional box axes that use less ink while conveying more information.
145
+
146
+ ### Range-Frame
147
+
148
+ Instead of drawing axes from arbitrary round numbers to round numbers, the axis line spans only the range of the data (from min to max). This encodes additional information (the data range) into an element that was previously just structural.
149
+
150
+ ### Dot-Dash Plot
151
+
152
+ Instead of tick marks at round intervals, place a tick mark at each actual data value along the axis. The distribution of ticks immediately shows data density and gaps.
153
+
154
+ ### Quarter-Frame
155
+
156
+ Only two sides of the frame are drawn (typically left and bottom), and only as far as the data extends.
157
+
158
+ ### When to Use
159
+
160
+ - Range-frames: almost always preferable to standard full-frame axes
161
+ - Dot-dash: when showing distribution along an axis matters (scatter plots, strip plots)
162
+ - Quarter-frame: when data doesn't approach all four edges of the plot area
163
+
164
+ ---
165
+
166
+ ## Showing Causality
167
+
168
+ Techniques for moving beyond correlation to communicate mechanism and cause-effect relationships.
169
+
170
+ ### Principles
171
+
172
+ 1. **Temporal sequence** - Cause precedes effect; arrange displays chronologically when causality is temporal
173
+ 2. **Mechanism diagrams** - Show the pathway from cause to effect, not just the endpoints
174
+ 3. **Counterfactual comparison** - Show what happened alongside what would have happened without the intervention
175
+ 4. **Confound acknowledgment** - Note or visualize potential confounders rather than ignoring them
176
+
177
+ ### Visual Techniques
178
+
179
+ - Before/after with a clear intervention marker
180
+ - Parallel time-series: treatment vs. control
181
+ - Flow diagrams showing causal chains
182
+ - Annotations on inflection points explaining what changed
183
+
184
+ ### Honesty Requirements
185
+
186
+ - Don't imply causation when you only have correlation
187
+ - Show uncertainty bands where appropriate
188
+ - If the causal mechanism is debated, note it
189
+ - Show the data that argues against your interpretation alongside the data that supports it
190
+
191
+ ---
192
+
193
+ ## Confections
194
+
195
+ Assemblages of many visual elements that together provide a richly informative, often explanatory, display.
196
+
197
+ ### What They Are
198
+
199
+ Confections combine multiple modes of information:
200
+ - Diagrams + data + annotations + comparisons in a single integrated display
201
+ - Often narrative: they tell a story with a beginning, middle, and end
202
+ - May mix scales, perspectives, or time periods in a single view
203
+
204
+ ### When to Use
205
+
206
+ - Explaining complex systems or processes
207
+ - Teaching: where understanding mechanism matters more than precision
208
+ - Summarizing research findings with their context
209
+ - Executive briefings that need to convey both "what" and "why"
210
+
211
+ ### Design Principles
212
+
213
+ 1. **Unity** - Despite multiple elements, the display should read as one coherent piece
214
+ 2. **Hierarchy** - The most important information is most prominent
215
+ 3. **Flow** - The reader's eye should move through the display in a logical sequence
216
+ 4. **Density** - Every region of the display should carry information; no dead zones
217
+ 5. **Integration** - Words and images work together; neither is redundant to the other