docs: record plan canvas loading fix evidence

This commit is contained in:
haelyra
2026-08-27 20:02:55 -04:00
parent 7fc312541e
commit bb4adc6462
2 changed files with 86 additions and 4 deletions
+5 -4
View File
@@ -41,7 +41,7 @@ A loopback-only web editor for plan artifacts (and any local HTML artifact):
| Server | `scripts/lib/plan-canvas/server.js` | control-pane loopback server, host-header + Origin allowlist (DNS-rebinding guard) |
| Editor chrome | `scripts/lib/plan-canvas/ui.js` | `scripts/lib/control-pane/ui.js`, tokens from `scripts/dashboard-web.js` |
| Markdown plan renderer | `scripts/lib/plan-canvas/markdown.js` | zero new deps; renders the `commands/plan.md` artifact schema (tables, tasks, code fences, Mermaid blocks) |
| Mermaid diagrams | `scripts/lib/plan-canvas/ui.js` | ` ```mermaid ` blocks render in the browser, themed to ECC; pinned CDN with offline fallback (`ECC_PLAN_CANVAS_MERMAID_URL` for a local mirror) |
| Mermaid diagrams | `scripts/lib/plan-canvas/ui.js` | ` ```mermaid ` blocks render in the browser after page load, themed to ECC; pinned CDN with non-blocking fallback (`ECC_PLAN_CANVAS_MERMAID_URL` for a local mirror) |
| Session state | `scripts/lib/plan-canvas/sessions.js` | file-path-keyed sessions, state under `~/.claude/plan-canvas/` (`ECC_PLAN_CANVAS_STATE_DIR` override) |
| Skill | `skills/plan-canvas/SKILL.md` | skills-first surface; teaches the open → await → reply loop; defers visual guidance to `frontend-design-direction`, `artifact-design`, `dataviz` |
| Command shim | `commands/plan-canvas.md` | legacy parity surface, points at the skill |
@@ -81,7 +81,7 @@ after 30 min, `ECC_PLAN_CANVAS_IDLE_MS`). Feedback is deliver-and-drain: queued
handed to exactly one `await` call and persisted to disk until then, so nothing is lost if
the poll is interrupted.
- `GET /health` — `{ok, app: "ecc-plan-canvas", version}` (CLI/server version handshake)
- `GET /health` — `{ok, app, version, protocolVersion, runtimeId}`; the CLI reuses a detached server only when its package, protocol, and Canvas-module fingerprint match, preventing an older same-version worktree from serving stale browser code
- `GET /` — session list (ECC chrome)
- `POST /api/sessions` `{file, reopen?}` — open/resume; `409 user-ended` unless `reopen`
- `GET /canvas/<key>` — editor chrome; `GET /artifact/<key>/` — rendered artifact
@@ -115,6 +115,7 @@ sibling-asset access confined to the artifact directory; state dir is user-local
never executes artifact content — it only serves it to the browser.
The one optional outbound request is the pinned Mermaid library, fetched by the browser only
for artifacts that contain a diagram; it renders with `securityLevel: 'strict'`, degrades to
showing diagram source if unavailable, and can be repointed at a local mirror via
after an artifact containing a diagram has loaded; it renders with `securityLevel: 'strict'`,
cannot hold the Canvas page in a loading state, degrades to showing diagram source if
unavailable, and can be repointed at a local mirror via
`ECC_PLAN_CANVAS_MERMAID_URL`. The server itself still makes no network calls.
@@ -0,0 +1,81 @@
# Plan Canvas loading hang TDD evidence
## Source
No implementation plan was supplied. The journeys and guarantees below were
derived from the reported intermittent localhost loading hang, especially when
opening a second Plan Canvas in one agent chat.
## User journeys
1. As a reviewer opening plans from multiple ECC worktrees, I want every new
Canvas to use the current server code so an older same-version process cannot
disable or stall the page.
2. As a reviewer opening a plan with Mermaid diagrams, I want the Canvas page to
finish loading even when the Mermaid CDN is slow or unavailable.
## Task report
### Replace a stale same-version detached server
- RED: `node tests/integration/plan-canvas-e2e.test.js` produced 9 passes and
1 failure. The current CLI reused a fake legacy server that reported the same
package version and sent the session-open request to it.
- RED checkpoint: `5dc6d85c test: add reproducer for stale plan canvas server`.
- GREEN: the health handshake now carries a protocol version and a SHA-256
fingerprint of every module loaded into the detached Canvas server. The CLI
retires any process whose package, protocol, or runtime fingerprint differs.
- GREEN: `node tests/integration/plan-canvas-e2e.test.js` produced 10 passes and
0 failures.
- GREEN checkpoint: `7e0d115e fix: restart stale plan canvas servers`.
### Keep Mermaid enhancement from blocking page load
- RED: `node tests/scripts/plan-canvas.test.js` produced 27 passes and 1
failure. The generated artifact loaded Mermaid with top-level `await`, which
allowed an unresolved remote import to hold the document load event open.
- RED checkpoint: `ca0d0415 test: reproduce mermaid page load stall`.
- GREEN: the dynamic Mermaid import now starts from an async `load` listener.
Browsers do not await that listener, so diagrams remain progressive
enhancement and the raw Mermaid source remains available during a network
stall.
- GREEN: `node tests/scripts/plan-canvas.test.js` produced 28 passes and 0
failures.
- GREEN checkpoint: `7fc31254 fix: keep mermaid from blocking canvas load`.
## Test specification
| # | What is guaranteed | Test target | Type | Result |
| --- | --- | --- | --- | --- |
| 1 | A legacy server with the same package version is shut down before the current CLI opens a plan | `same-version legacy server is replaced before a canvas opens` | End to end | PASS |
| 2 | Health exposes package, protocol, and exact Canvas runtime identity | `GET /health identifies the app and version` | Integration | PASS |
| 3 | Mermaid remote enhancement starts only after document load | `a plan containing mermaid serves the themed Mermaid loader` | Integration | PASS |
| 4 | Open, browser load, await, feedback, reply, approval, reopen, end, and stop still work together | `tests/integration/plan-canvas-e2e.test.js` | End to end | PASS |
| 5 | The large ECC 2 to ECC 3 master plan bootstraps under blocked local storage | Headless Chrome DOM and screenshot check against `/canvas/24af75d4c4fe` | Browser | PASS |
## Coverage and full-suite evidence
`npm run coverage` passed all 3,993 discovered tests with these project totals:
- Statements: 88.98%
- Branches: 80.66%
- Functions: 94.32%
- Lines: 88.98%
The `scripts/lib/plan-canvas` group reached 98.38% statements, 88.2% branches,
98.64% functions, and 98.38% lines. Focused ESLint checks passed for every
modified JavaScript test and production file.
## Browser evidence and known gaps
The live shared server was initially process `15351`, started from the older
`ecc-tiered-sandbox` worktree, and served an unguarded `localStorage` client even
when invoked from current main. The patched CLI replaced it with the isolated
worktree server and restored persisted sessions. Two real plan sessions then
opened successfully. Headless Chrome with local storage disabled completed the
large master-plan DOM load in about two seconds and produced a rendered Canvas
screenshot.
Chrome was exercised directly on macOS. Safari and Firefox were not run. The
fix relies only on standard health JSON, dynamic `import()`, and the standard
window `load` event.