Issue 1 : Upload video/screenshot attachments, not just traces
Currently isTraceAttachment (packages/core/src/lib/ingest-client.ts:129-132) gates which attachment bodies get uploaded in both the reporter (packages/reporter/src/index.ts:140-151) and CLI (packages/cli/src/upload.ts:31-34) — only trace.zip is sent. Videos and screenshots are recorded in the run JSON as metadata only (name/contentType/path/hasBody), so their path is a local CI-runner path the server can never fetch, and the dashboard has nothing to link to.
Ask: widen the upload path so video/* and image/* attachments (not just zips) get uploaded via the existing client.uploadArtifact() call, storing each as an artifact row keyed by test — same mechanism traces already use.
Needs a decision before implementing:
- Size/quota impact — videos are much larger than traces and one can exist per retry; should this be opt-in (a reporter option, e.g.
uploadAttachments: ['video', 'screenshot']) or upload everything by default?
- Should this only fire
on-failure (matching Playwright's own default video/screenshot retention policy) or unconditionally per effectiveAttachments?
- Retention/storage cost implications for
purge-expired-runs and plan limits (packages/server/src/billing/retention.ts, usage.ts).
Touches: packages/core/src/lib/ingest-client.ts, packages/reporter/src/index.ts, packages/cli/src/upload.ts.
Issue 2 : Render video/screenshot attachments as links on the run page
RunPage.vue currently lists every attachment as an inert text badge (packages/web/src/pages/RunPage.vue) — only the trace gets a clickable "View trace" pill. Once an attachment has a resolved url (blocked on the upload work above landing), the badge should be a real link.
Ask: turn each attachment badge into an <a> pointing at a.url (opens the raw file in a new tab) when a URL is present; keep it inert text when it isn't (e.g. old runs ingested before that lands, or attachments still gated off by whatever opt-in that issue decides on).
Nice-to-have, not required for v1: an inline video player / image lightbox instead of a raw-file new tab.
Touches: packages/web/src/pages/RunPage.vue. Depends on the upload-attachments issue.

Issue 1 : Upload video/screenshot attachments, not just traces
Currently
isTraceAttachment(packages/core/src/lib/ingest-client.ts:129-132) gates which attachment bodies get uploaded in both the reporter (packages/reporter/src/index.ts:140-151) and CLI (packages/cli/src/upload.ts:31-34) — only trace.zip is sent. Videos and screenshots are recorded in the run JSON as metadata only (name/contentType/path/hasBody), so theirpathis a local CI-runner path the server can never fetch, and the dashboard has nothing to link to.Ask: widen the upload path so
video/*andimage/*attachments (not just zips) get uploaded via the existingclient.uploadArtifact()call, storing each as anartifactrow keyed by test — same mechanism traces already use.Needs a decision before implementing:
uploadAttachments: ['video', 'screenshot']) or upload everything by default?on-failure(matching Playwright's own default video/screenshot retention policy) or unconditionally pereffectiveAttachments?purge-expired-runsand plan limits (packages/server/src/billing/retention.ts,usage.ts).Touches:
packages/core/src/lib/ingest-client.ts,packages/reporter/src/index.ts,packages/cli/src/upload.ts.Issue 2 : Render video/screenshot attachments as links on the run page
RunPage.vuecurrently lists every attachment as an inert text badge (packages/web/src/pages/RunPage.vue) — only the trace gets a clickable "View trace" pill. Once an attachment has a resolvedurl(blocked on the upload work above landing), the badge should be a real link.Ask: turn each attachment badge into an
<a>pointing ata.url(opens the raw file in a new tab) when a URL is present; keep it inert text when it isn't (e.g. old runs ingested before that lands, or attachments still gated off by whatever opt-in that issue decides on).Nice-to-have, not required for v1: an inline video player / image lightbox instead of a raw-file new tab.
Touches:
packages/web/src/pages/RunPage.vue. Depends on the upload-attachments issue.