WIN · LAUNCH WORKBOOK pre-UAT → production · mined from Codex session 019f25cc · snapshot 2026-07-06 · origin/main 734eff41

Lane 6 · Product proposal

One home for live and static artifacts

Instead of deploying planning workbooks like this one to ad-hoc static hosts, make “publish a hosted page” a first-class WIN capability. WIN already persists job artifacts to MinIO/S3 and already renders live artifacts in an iframe shell — the missing half is immutable static pages, plus one naming umbrella so the Artifacts page can hold both without confusion.

Naming proposal

Recommended: “Gallery”, holding Exhibits

umbrella section · two kinds

The Artifacts page gains a Gallery section listing every hosted, viewable piece — each item an exhibit badged by kind: LIVE (backed by a running origin, may change under you) or PAGE (immutable static snapshot). “Live exhibit” and “static exhibit” both read naturally, and neither collides with existing WIN vocabulary (jobs, runs, workflows, skills, deliverables, canvas).

Alternative A: “Showcase”

marketing-flavored

Same structure, warmer word. Slightly weaker: “showcase” suggests curation/best-of, while agents will also publish mundane QA worksheets.

Alternative B: keep “Artifacts” + kind facet

lowest-change

No new noun; add kind: live | page to the artifact record and a filter chip row. Cheapest, but loses the chance to give hosted pages a memorable identity, and “artifact” keeps meaning both “output file” and “hosted page”.

The distinction that matters Users don’t care about hosting mechanics; they care about “can this change under me?” Badge semantics should encode exactly that: LIVE = a running surface (proxied origin, updates in place, may depend on a job/service being up) · PAGE = frozen bytes, content-addressed, safe to share and cite. A “Freeze to Page” action on any live exhibit bridges the two — snapshot a live artifact into an immutable page when a milestone is reached (that’s effectively what this workbook is).

Implementation sketch (small — rides on what exists)

PieceDesignRides on
StorageStatic exhibit = one HTML file or a directory tree, uploaded content-addressed to MinIO/S3 (exhibits/<hash>/…); record row: slug, title, kind, version list, owner, source job idMinIO already in compose; artifact persistence already exists
ServingGET /a/:slug[/path] → S3 object, Cache-Control: immutable per version; slug → latest version; directory-shaped exhibits get index.html resolution (multi-page workbooks like this one just work)Hono server
Security (the one hard requirement)Serve exhibits from a separate origin (e.g. pages.bigwinbeta.olelabs.xyz) or with a strict sandbox CSP so exhibit JS can never read the dashboard’s win_session cookie. Never serve user-authored HTML on the app origin.Cloudflare Tunnel already does host routing
Creation — agentswin artifacts publish ./output/report/ --title "…" --kind page from any job workspace; auto-registers the exhibit and returns the URL in the deliverablewin artifacts CLI namespace exists
Creation — users“Publish as page” on any HTML job artifact; drag-drop upload on the Gallery sectionArtifacts page UI
Creation — API/MCPPOST /api/me/exhibits (+ an MCP tool later) — keeps the three-audience principle honestme-routes pattern
VersioningEvery publish = new immutable version; version picker in the shell; “what changed” = diff of file listscontent addressing
Live kindExisting LiveArtifactShell items migrate into the Gallery with kind=LIVE unchangedLiveArtifactShell.tsx

Why this beats external static hosting for this workbook

Plan of attack

Implementation worksheet0/6

Agent handoff prompt

prompt · ART-1..3 exhibit MVP
Build the "static exhibits" MVP for WIN: first-class hosted static pages living beside the
existing live artifacts.

Scope (three thin slices, one PR each):
1. Spec: claim the next free spec number (bun run specs:numbers) for the exhibit model —
   record {slug, title, kind: "live"|"page", versions[{hash, createdAt, files[]}], ownerEmail,
   sourceJobId?}; hard requirement: exhibits are served from an isolated origin or sandbox CSP
   so exhibit JS can never access the dashboard's win_session cookie. Index the spec in
   docs/specs/README.md in the same change.
2. Serving: GET /a/:slug[/*path] on the server — resolves latest (or ?v=hash) version from
   MinIO/S3 (exhibits/<hash>/...), directory-shaped exhibits resolve index.html,
   Cache-Control immutable per version. Tests: slug resolution, version pinning, path
   traversal rejection, index resolution.
3. Publish: `win artifacts publish <file-or-dir> --title ... --kind page` (CLI) calling
   POST /api/me/exhibits (multipart or presigned-put) that content-addresses the upload and
   creates/updates the exhibit row. Tests: single-file, directory, re-publish appends version.

Defer the Gallery UI and Freeze-to-Page. Follow repo rails (PR records, cross-package
typecheck, no orphan modules — the routes must be mounted).