Skip to content

feat(tooling): canvas-shots — screenshot design-prototype routes and gate on a render verdict#778

Open
rickylabs wants to merge 2 commits into
feat/beta10-integrationfrom
feat/canvas-shots-tool
Open

feat(tooling): canvas-shots — screenshot design-prototype routes and gate on a render verdict#778
rickylabs wants to merge 2 commits into
feat/beta10-integrationfrom
feat/canvas-shots-tool

Conversation

@rickylabs

Copy link
Copy Markdown
Owner

Summary

deno task canvas:shots — a reusable Deno tool that screenshots every route of a Claude Design prototype in both themes and returns a verdict, not just a PNG.

Why a verdict, not a screenshot

A screenshot of a broken page is still a valid PNG. This tool exists because a render can look fine while being defective, so it reports per shot:

  • unresolvedHoles — occurrences of {{ … }} surviving into the rendered DOM. The Design Components runtime does not fill template holes inside SVG subtrees, so <path d="{{ k.fill }}"> reaches the browser verbatim and throws Expected moveto path command. Found live in the current prototype (ns-kpi's sparkline, ns-stackmap's edge layer).
  • consoleErrors, failedRequests — deduped.
  • windowNSOne — reported, not asserted (see below).

It exits non-zero on a defective render unless --allow-defects is passed. A screenshot tool that always exits 0 is a tool that lets broken screens through review.

Theme handling

Light is the unthemed default; dark is data-theme="dark" on the root. The tool sets both colorScheme and the attribute — it does not rely on prefers-color-scheme alone, because NS One does not.

Browser resolution

Playwright's bundled-browser version and the installed build disagree on this machine (chromium-1232 cached, npm playwright wants 1228). Resolution order: CANVAS_SHOTS_CHROMIUM env → newest cached chromium-* → Playwright's own, with an actionable error naming the mismatch. It never silently downloads a browser.

Security

A serve_url is a *.claudeusercontent.com URL carrying a project-scoped token and it expires. The tool redacts it — never logged in full, never written to a file, never baked into a fixture.

A correction, recorded

My first review of this branch asserted window.NSOne must be defined. That was wrong. The prototype renders raw ns-* CSS classes rather than React components — deliberately, because class markup round-trips into @netscript/fresh-ui Preact source unchanged, which is the entire point of the sync-back loop. So window.NSOne being undefined is architecture, not a defect, and the tool reports it rather than asserting on it. Enforcing my assertion would have damaged the sync-back path.

Validation

  • deno test .llm/tools/canvas-shots/9 passed, 0 failed (pure parts: arg parsing, slugging, theme script, defect classifier, browser-path resolver — no network, no live canvas)
  • deno check / lint / fmt clean
  • Exercised end-to-end against the live prototype; the two defects above are its own findings

⚠️ CI note (see #774)

ci.yml only triggers on PRs into main; check-test / quality do not run here.

Harness

Codex slice. Its first attempt (Sol · low) died silently after adding the dependency — zero code, no task_complete, 52 minutes. The identical brief succeeded on Sol · medium. Recorded as a routing data point: "fully specified" and "mechanically easy" are different axes — this slice integrates an npm dependency, resolves a version-mismatched browser binary, and designs a defect classifier.

The lock commit is included: the tool shipped without it initially, so its npm:playwright import would not have resolved for anyone else. The three lines that vanish from deno.lock are a re-organisation within a member's dependency list, not a drop — jsr:@std/path@1 remains (deno.lock:76 → 1.1.5) and deno check passes.

Run dir: .llm/runs/beta10--orchestrator/, slice canvas-shots-tool. Not self-certified.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HTiQfrNFCVhjQFqbLKm5xo

rickylabs and others added 2 commits July 13, 2026 02:44
The canvas-shots tool landed without its lock entry, so its `npm:playwright`
import would not resolve for anyone else checking out the branch.

Verified the delta is scoped: playwright + playwright-core + fsevents added;
the three removed lines are a re-organization within a member's dependency
list, not a drop — `jsr:@std/path@1` is still present (deno.lock:76 → 1.1.5)
and `deno check` on the tool passes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HTiQfrNFCVhjQFqbLKm5xo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant