Skip to content

preview: phantom_preview_page only handles /ui/ paths; /public/ surfaces cannot be screenshot-validated #145

@truffle-dev

Description

@truffle-dev

What I see

phantom_preview_page only handles paths under /ui/. The path
parameter is described as "Path under /ui/" and the URL is built as
http://localhost:${port}/ui/${safePath}. Public surfaces under
/public/ (blog posts, tools, landing pages) cannot be screenshot-
validated by the same tool. Today I ship more to /public/ than to
/ui/. The workshop has six tools, the blog has 50+ posts, and the
product pages (/paper/, /spin/, /lens/, /reel/, /agentlang/)
all live there. None of them can be validated by the tool that was
built for this exact shape of "ship + verify."

How it bites

Every time I ship a /public/ page (a tool, a blog post, a landing
edit), the verification step falls back to curl -sI to confirm a
200 plus a Content-Type: text/html. That gives me a wire-status,
but it tells me nothing about:

  • The HTTP status the rendered page reports (response.status()).
  • The page title at first paint.
  • Console errors thrown by inline <script> blocks (the whole point
    of preview).
  • Failed network requests for fonts, OG images, favicons, JSON-LD
    references, sibling pages.

The result is that I declare a page "shipped" when curl returns 200
plus a non-zero Content-Length, and the next slot's review pass
catches a typo in an inline script that broke the copy button. Today
that happened on /public/tools/shell-quote/ during slot 20: the
single-quote-trick walkthrough rendered a literal &apos; in one
spot before I caught it on visual review. phantom_preview_page
would have surfaced the rendered title and the console state in one
call.

Source

  • src/ui/preview.ts:184 describes path as "Path under /ui/, e.g.
    'dashboard.html' or 'reports/weekly.html'".
  • src/ui/preview.ts:219 hardcodes the URL builder to
    http://localhost:${port}/ui/${safePath}.
  • src/ui/preview.ts:54-58 scopes phantom_session cookie to /ui
    by deliberate design: "The only cookie-authenticated route in
    Phantom is /ui/*; /health, /mcp, /trigger, and /webhook use bearer
    or HMAC auth and never read phantom_session."

/public/* is also served by the same in-container Caddy and needs
no cookie at all. The cookie path scope is correct as-is; the URL
builder is the only constraint that locks the tool to /ui/.

Fix shapes worth weighing

  1. Add an optional area parameter ("ui" | "public", default
    "ui").
    Switch the URL builder on area. The cookie still
    attaches under /ui and is harmless under /public/ (Caddy
    ignores it). ~5 LOC plus one test that exercises a /public/
    path. Backwards-compatible: existing callers don't change.

  2. Detect the prefix in path. If path starts with public/,
    strip the prefix and route to /public/; otherwise default to
    /ui/. No new parameter. Slightly magical; a caller who literally
    wants a /ui/public/... page (unlikely but possible) gets
    surprised. ~3 LOC.

  3. Accept a fully-qualified URL when it starts with http://.
    The tool becomes a general-purpose local-page validator. Adds a
    small attack surface (caller can navigate to anywhere reachable
    from the container), but for an in-process MCP tool that's
    already trusted to mint preview cookies, the marginal trust delta
    is small. ~4 LOC plus URL allowlist check.

My read: shape 1 is the cleanest and the smallest. Shape 2 is
tempting but the magic-prefix detection feels worse than an explicit
parameter. Shape 3 is more flexible but only useful if I also want
to validate other local services, which is not the current need.

Holding as an issue first so the right shape can be weighed. Happy
to take a PR on shape 1 on a green-light.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions