Preview Portal Audit
The review and delivery portal standardizes the separate HTML files that were previously generated by project-specific scripts.
| Example | Surface | Controls | Lightbox | Downloads | Notes |
|---|---|---|---|---|---|
guardians-of-the-dawn/review.html | editor review | yes | yes | partial | Source for editor approve/regenerate controls and copyable agent handoff. |
guardians-of-the-dawn/client-review.html | client review | simplified | yes | partial | Source for client approve/decline/comment mode. |
mirchi-mode/preview.html | final preview | no | no | yes | Source for final showcase and download behavior. |
dhuaan-music-video/preview.html | project preview | no | no | no | Should migrate to generated music-video preview. |
Standard Surfaces
The portal generates three surfaces per project by default: a tabbed Review (the feedback loop), a clean Preview (the deliverable), and a live Run dashboard (the operations view). Review and Preview are opposite jobs — Review exists to change the output (feedback → regenerate, via the copy→paste-back-to-Claude protocol); Preview exists to show the finished output for upload/delivery; Run exists to watch the render in flight. The former edit / client-review / compare pages are consolidated into Review (a Decide/Compare tab set + an editor↔client mode toggle); they remain valid --surface aliases for back-compat but are no longer generated by default.
| File | Audience | Purpose |
|---|---|---|
review.html | operator ↔ Claude (+ client mode) | The single feedback surface. Decide tab: approve/regenerate per unit → VIDEOCLAW_REVIEW_DECISIONS copy. Compare tab: run/version comparison. A low-key client mode toggle swaps controls to approve/decline/comment → VIDEOCLAW_CLIENT_FEEDBACK copy. Absorbs the old edit/client-review/compare surfaces. |
preview.html | final viewer | Polished, controls-free deliverable: click-to-fullscreen lightbox on every production image, a soundtrack <audio controls preload="none"> player (when a soundtrack is discovered), aspect-aware sizing, and asset downloads. The upload artifact. |
run.html | operator | The live run dashboard: one card per generation (storyboard scene) with a STATUS badge (done/rendering/pending/failed), the provider job id + error, the input keyframe, a playable in-progress clip (outputs/scene-N.mp4), the exact submit prompt + contract, a spend estimate chip, an event log (events/events.jsonl), per-card copy-command buttons, and a Show › Episode header from show-bible.json. Paints a RED diff-vs-contract alarm when the submitted payload diverged from the current contract (the @tag-hijacks-references bug), backed by the frozen artifacts/run-contract.json snapshot. Auto-refreshes via <meta http-equiv="refresh">, and is regenerated automatically on every produce/execute and execute-status poll (VCLAW_NO_RUN_SURFACE=1 opts out). |
projects/index.html | operator | Portfolio index across generated projects (links to each project's Review + Preview). |
projects/clients/<client>/index.html | client/operator | Client-filtered index across that client's projects. |
Aliases:
--surface editand--surface reviewboth renderreview.html(editor mode default);--surface client-reviewrenders it with client mode default;--surface comparerenders the standalone compare page;--surface runrenders the live run dashboard. Default generation is['review','preview','run'].
Template Registry
The renderer keeps the HTML structure consistent while adapting section labels and ordering for music-video, story-film, documentary, product-ad, sports-recap, and generic-video. Projects can set template or previewTemplate in project.json; unknown values fall back to generic-video.
The portal also reads artifacts/asset-manifest.json for image assets used by generation routes. Local project-scoped image inputs are rendered in a dedicated generation-input section; for Seedance-backed assets this appears as Seedance Input Frames in the music-video template. This makes the exact start-frame/upscaled image being sent into Seedance visible beside the resulting clip, instead of relying only on the generic images/ folder.
When artifacts/filmmaking-prompts.json exists, the portal exposes it under the prompt-packet section, labelled Seedance Prompt Packets for music-video projects. That gives reviewers one place to inspect the generated @image reference map, character-sheet prompts, 9-panel storyboard-grid prompt, and Seedance packet text before or after video generation.
Per-Scene Submit Contract
Each storyboard scene card renders the exact contract that will be submitted for that scene, expanded inline (no click-to-reveal), so an operator can confirm "exactly what you're going to get" before any render spend:
- Submit prompt — what the model receives. The resolved provider submit text is the
seedancePackets[].promptText(the 10-block master prompt) fromartifacts/filmmaking-prompts.json, matched to the scene bysceneIndex. The lighter storyboardscenePromptfields (imagePrompt,animationPrompt,styleFooter) are shown alongside it. This is the convergence point where the multi-shot plan, the cinematography registers, and the story-bible continuity all land in one packet. - References — identity lock. Each
seedancePackets[].referencesslot is rendered with its role, aready/pendingbadge, and the binding. For character-sheet slots the binding is theAsset://URI resolved fromartifacts/seedance-assets.jsonby character name (preferringintlAssetUri); an unregistered character renders as<name> · unregistered. Discovered character reference thumbnails are shown beneath the slots. This lets a reviewer confirm which avatar locks which scene, and that every required reference is ready, before submitting.
The data is sourced entirely from on-disk artifacts (storyboard.json, filmmaking-prompts.json, seedance-assets.json); the card falls back gracefully (omitting the missing block) when an artifact is absent.
Soundtrack Player
The preview showcase discovers a project soundtrack and, when one exists, renders a <audio controls preload="none"> player in the header strip. Discovery prefers an explicit soundtrack or audio field in project.json that resolves to an existing project-local audio file, then falls back to the first discovered audio asset (.mp3/.m4a/.wav/.aac). When no soundtrack is found, no <audio> element is emitted (no broken/empty player). The player is preview-only; the editor/review/client-review decision surfaces are unchanged.
Lightbox on Production Images
Every production image rendered by the showcase carries data-lightbox-group (and data-lb-caption where a caption exists) so the shared initLightbox makes each image click-to-fullscreen with caption and Escape-to-close, with no per-project hand-wiring.
Empty-preview guardrail
When a project has no renderable media, generatePreviewPortalSurfaces returns a non-empty warnings array (echoed to stderr by the vclaw video portal handler) instead of a silently empty page. The usual cause is that the finished cut was staged at the workspace root rather than under projects/<slug>/final/{videos,images,audio} — the only location the portal scans. The pure, exported previewPortalMediaWarnings() computes the list; warnings is always present (empty when media exists) and the stdout JSON stays machine-readable.
Publish Contract
vclaw video publish-preview builds a deterministic R2 upload plan from the HTML file and its local src/href references. vclaw video publish-portal-index uploads a client or global index whose links point into the published run folders.
vclaw video publish-preview \
--project <slug> \
--client <name> \
--bucket <bucket> \
[--run <id>] \
[--surface edit|review|client-review|preview|compare|index] \
[--public-base-url <url>] \
[--wrangler-bin <path>] \
[--dry-run]--project, --client, and --bucket are all required — the handler throws if any is missing. --surface defaults to preview. The client, project, and run path segments are slugified before forming the R2 key, so the upload prefix is clients/<slugified-client>/<slugified-project>/runs/<slugified-run>/.
Each plan item includes:
- local file path
- remote R2 key under
clients/<client>/<project>/runs/<run>/ - content type
- SHA-256 hash
- public URL when
--public-base-urlis provided
Use --dry-run first to inspect the plan. Running without --dry-run executes wrangler r2 object put for each item and appends a surface.published event to project-audit.jsonl. Use --wrangler-bin <path> to pin a specific Wrangler executable in CI or agent automation.
Published project pages live under clients/<client>/<project>/runs/<run>/. Published client indexes live at clients/<client>/index.html and link to those project/run pages.
