Review loop for plans and visual artifacts: you write the artifact, the human reviews it in the browser — annotating the exact element they mean, chatting, and delivering an Approve plan / Request changes verdict — while you block on a single CLI call that returns their feedback as JSON.
Inspired by lavish-axi; rebuilt
ECC-native around the /plan confirmation gate, with zero dependencies.
.claude/plans/*.plan.md from /plan) and
need the CONFIRM/approve decision — the canvas verdict replaces a typed
"yes/proceed"..md / .html artifact./plan-canvas, a visual review, or "open it in the browser".Do NOT use for: code review of diffs (/code-review), running web apps, or
remote URLs. The canvas serves local artifact files only.
Invoke the CLI as ecc-plan-canvas — the bin shipped by the ecc-universal
package (on PATH after a global/plugin install; node "$CLAUDE_PLUGIN_ROOT/scripts/plan-canvas.js"
also works for plugin installs). Run it from the project you are reviewing in;
it works from any working directory. It manages a detached loopback server
(127.0.0.1:4517) shared by all sessions, keyed by artifact path — no session
ids to track.
The workflow is a plain CLI-plus-JSON loop, so it is model- and harness-agnostic:
any agent that can run a shell command and read stdout drives it the same way
(Claude Code, Codex, Cursor, Gemini, OpenCode, Copilot). Trigger it however your
harness surfaces skills — e.g. /plan-canvas in Claude Code, $plan-canvas in
Codex — or just run the ecc-plan-canvas commands directly.
# 1. Open the artifact in the user's browser (returns immediately)
ecc-plan-canvas open .claude/plans/feature.plan.md
# 2. Block until the human responds. Leave running; re-run if interrupted —
# queued feedback is never lost. Run in the background if your harness
# time-limits foreground commands.
ecc-plan-canvas await .claude/plans/feature.plan.md
await prints JSON when the human acts:
{
"status": "feedback",
"items": [
{ "kind": "annotation", "text": "Split this into two phases",
"anchor": { "selector": "h2:nth-of-type(3)", "tag": "h2", "snippet": "Phase 2: Migration" } },
{ "kind": "verdict", "verdict": "request-changes" }
]
}
kind: "chat" — freeform message; answer in the canvas, not the terminal.kind: "annotation" — feedback anchored to an element (anchor.selector,
anchor.snippet show what they pointed at; anchor.textRange.text when
they highlighted a passage).kind: "verdict" — approve means the plan is CONFIRMED: stop polling,
end the session, and start implementing. request-changes means revise the
artifact (the canvas live-reloads it) and keep the loop going.3. Respond in the canvas, then keep listening — one command does both:
ecc-plan-canvas await <file> --reply "Split Phase 2 as requested — take a look."
4. End when review concludes: ecc-plan-canvas end <file>.
When part of the plan is a flow, architecture, sequence, state machine, ER
model, or dependency graph, author it as a fenced ```mermaid block instead
of ASCII art or a wall of prose — the canvas renders it as a themed diagram the
human can point at. Reach for it when a picture reads faster than a paragraph;
skip it for simple lists or tables.
```mermaid
flowchart LR
A[Market resolves] --> B{Watchers?}
B -->|yes| C[Enqueue jobs] --> D[Fan-out worker]
```
Diagrams render in the ECC dark theme with the accent palette. Mermaid loads in
the browser from a pinned CDN; if that is unavailable (offline), the block
degrades to showing its source, so the review is never blocked. Point a local
mirror at ECC_PLAN_CANVAS_MERMAID_URL for air-gapped use.
.html artifacts render as-is with the annotation layer injected. For HTML
authoring guidance use the frontend-design-direction and artifact-design
skills.open to refresh.{"status": "ended", "endedBy": "user"} (or sessionEnded: true on a
feedback batch) means the user closed the review: stop polling, deliver
remaining updates in chat, and do not reopen. A plain open on that
session is refused; pass --reopen only when the user asks to resume.ECC_PLAN_CANVAS_IDLE_MS); stop shuts it down explicitly. State lives
in ~/.claude/plan-canvas/ (ECC_PLAN_CANVAS_STATE_DIR).Plan approval flow — /plan writes
.claude/plans/notifications.plan.md and must WAIT for confirmation:
ecc-plan-canvas open .claude/plans/notifications.plan.md
ecc-plan-canvas await .claude/plans/notifications.plan.md
# → {"status":"feedback","items":[{"kind":"verdict","verdict":"approve"}]}
ecc-plan-canvas end .claude/plans/notifications.plan.md
# plan is confirmed — begin implementation
Revision loop — feedback arrives, you edit the file, reply, keep listening:
# await returned annotations → edit the .plan.md (canvas live-reloads)
ecc-plan-canvas await <file> --reply "Reworked the risk table."
# → blocks again until the next response
--timeout-ms in a loop — it exists for tests. Leave the
plain await running instead.await.