Skip to content

feat: accept frontend-snapshot-live manifest kind for OD live-iframe ingest#2

Merged
amacsmith merged 1 commit intomainfrom
auto/frontend-snapshot-live-kind
May 4, 2026
Merged

feat: accept frontend-snapshot-live manifest kind for OD live-iframe ingest#2
amacsmith merged 1 commit intomainfrom
auto/frontend-snapshot-live-kind

Conversation

@amacsmith
Copy link
Copy Markdown

@amacsmith amacsmith commented May 4, 2026

Summary

Cycle 1 Half A of LOO-1186 (live-iframe rendering). Extends the OD daemon to accept a NEW manifest kind frontend-snapshot-live alongside the existing frontend-snapshot, and persists the liveUrl + viewport fields into project metadata so the OD viewer (cycle 2) can render the live dev-server iframe.

  • Linear: https://linear.app/looptech/issue/LOO-1186
  • Contract: lune/.claude/harness/contracts/frontend-snapshot-live-iframe.md
  • Half B (snapshot-package --live-url CLI flag) is blocked on looptech-ai/frontend-snapshot@v0.3.0 from P1 cycle 2.

Changes

  • apps/daemon/src/frontend-snapshot-import.ts
    • Replace EXPECTED_MANIFEST_KIND constant with ACCEPTED_MANIFEST_KINDS set (frontend-snapshot, frontend-snapshot-live).
    • New validateLiveMetadata() enforces:
      • liveUrl is a string and parses with new URL()
      • URL protocol is http: or https: only (rejects javascript:, file:, data:, ftp:, etc.)
      • viewport.width is finite number in [320, 3840]
      • viewport.height is finite number in [480, 2160]
    • Static frontend-snapshot path is unchanged (no behavior diff).
  • apps/daemon/src/server.ts
    • When manifest.metadata.kind === 'frontend-snapshot-live', persist liveUrl + viewport into the project metadata block.
    • Static kind path is unchanged.
  • apps/daemon/tests/frontend-snapshot-import.test.ts
    • 12 new tests covering live-kind positive (http + https), missing liveUrl, javascript: URL, file:// URL, malformed URL, missing viewport, negative width, non-number width, missing height, out-of-bounds width and height. Plus an updated bogus-kind test that asserts the error lists the accepted set.

Test plan

  • pnpm --filter @open-design/daemon test341 tests pass (was 329; +12 new), including 23 in frontend-snapshot-import.test.ts (was 11).
  • Stub-ZIP curl smoke against running daemon (pnpm tools-dev + manual node -e script that builds a zero-dep ZIP, no frontend-snapshot exporter dependency):
    • Live ZIP (http://localhost:3000, viewport 1440x900) -> HTTP 200, response payload includes project.metadata.liveUrl and project.metadata.viewport.
    • Live ZIP with liveUrl=javascript:alert(1) -> HTTP 400 with error manifest.json metadata.liveUrl must use http: or https: (got javascript:).
    • Static frontend-snapshot regression -> HTTP 200, metadata kind=frontend-snapshot, no liveUrl/viewport fields (unchanged shape).

Notes for reviewer

  • This PR is the OD-side preparation for Phase 2 of the live-iframe contract. It does not render the iframe — that lands in cycle 2 once Half B (the exporter CLI flag) ships.
  • The viewport bounds (320-3840 x 480-2160) cover phone-portrait through 4K-landscape; outside that range almost certainly indicates a malformed or hostile manifest.
  • validateLiveMetadata() is deliberately strict on URL protocol because the value flows directly into an <iframe src> in cycle 2; allowing javascript: or data: would create a stored-XSS vector via the manifest.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Frontend snapshot imports now support "live" snapshots with dynamic URLs and custom viewport dimensions.
    • Enhanced validation ensures imported snapshots meet required standards for metadata, URLs, and display settings.

Cycle 1 Half A of LOO-1186 (live-iframe rendering). The OD daemon now
accepts a NEW manifest kind `frontend-snapshot-live` alongside the
existing `frontend-snapshot`, and persists the `liveUrl` + `viewport`
fields into project metadata so the OD viewer (cycle 2) can render
the live dev-server iframe.

Changes:
- `frontend-snapshot-import.ts`: replace single-kind constant with
  `ACCEPTED_MANIFEST_KINDS` set; new `validateLiveMetadata()`
  enforces http/https-only `liveUrl` (rejects javascript:, file:,
  data:, etc.) and `viewport.{width,height}` inside 320-3840 x
  480-2160. Static `frontend-snapshot` path is unchanged.
- `server.ts`: when manifest kind is `frontend-snapshot-live`,
  persist `liveUrl` + `viewport` into the project metadata block.
  Static kind path is unchanged.
- `tests/frontend-snapshot-import.test.ts`: 12 new tests covering
  positive live ingest, http+https acceptance, and rejection paths
  for missing/malformed liveUrl, malicious URL schemes
  (javascript:, file:), missing/non-numeric/out-of-bounds viewport,
  plus an updated bogus-kind test that asserts the error lists the
  accepted set.

Test plan: 23 frontend-snapshot-import tests pass; full daemon
suite 341 tests pass. Stub-ZIP curl smoke verified locally:
- live ZIP -> HTTP 200, project.metadata.liveUrl + viewport persisted
- liveUrl=javascript:alert(1) -> HTTP 400 with descriptive error
- static frontend-snapshot regression -> HTTP 200, no behavior change

Half B (snapshot-package `--live-url` CLI flag) is blocked on
`looptech-ai/frontend-snapshot@v0.3.0` from P1 cycle 2.

Linear: https://linear.app/looptech/issue/LOO-1186
Contract: lune/.claude/harness/contracts/frontend-snapshot-live-iframe.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 4, 2026 06:16
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 4, 2026

📝 Walkthrough

Walkthrough

The pull request extends the manifest importer to support a new frontend-snapshot-live kind alongside the existing frontend-snapshot kind. It adds validation for required liveUrl and viewport fields in live manifests, updates project creation to conditionally include these fields based on kind, and covers both kinds with comprehensive tests.

Changes

Frontend Snapshot Live Support

Layer / File(s) Summary
Manifest Kind Constants & Bounds
apps/daemon/src/frontend-snapshot-import.ts (lines 39–57)
Introduces ACCEPTED_MANIFEST_KINDS array (frontend-snapshot, frontend-snapshot-live) and viewport constraint constants (MIN_VIEWPORT_WIDTH, MAX_VIEWPORT_WIDTH, MIN_VIEWPORT_HEIGHT, MAX_VIEWPORT_HEIGHT).
Manifest Validation
apps/daemon/src/frontend-snapshot-import.ts (lines 140–206)
Updates parseManifest to validate metadata.kind against the allowlist and conditionally invoke validateLiveMetadata for live snapshots. New validateLiveMetadata helper enforces required liveUrl (valid HTTP/HTTPS URL), required viewport object with numeric finite width/height within defined bounds.
Project Metadata Binding
apps/daemon/src/server.ts (lines 855–881)
Conditionally builds projectMetadata based on manifest kind: for live snapshots includes kind, liveUrl, and viewport; for static snapshots preserves existing fields. Updates insertProject to use computed metadata instead of hard-coded static shape.
Test Coverage
apps/daemon/tests/frontend-snapshot-import.test.ts (lines 152–471)
Updates existing kind validation test to expect both accepted kinds in error message. Adds comprehensive frontend-snapshot-live test suite covering successful import scenarios and validation failures for missing/invalid liveUrl (scheme, malformed URL) and viewport (missing, non-numeric, out-of-bounds).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

🐰 A snapshot so live, it leaps off the screen!
With URLs that dance and viewports so keen,
Validation bounds keep the dreams in between—
Now static and live, a rabbit's dream scene!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding support for a new 'frontend-snapshot-live' manifest kind to enable live-iframe ingest for the OD viewer.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch auto/frontend-snapshot-live-kind

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/daemon/src/server.ts`:
- Around line 855-881: The project persists liveUrl and viewport for
'frontend-snapshot-live' via the projectMetadata object (see projectMetadata and
insertProject) but the TypeScript contracts (ProjectKind and ProjectMetadata)
are still out of sync; add a TODO comment near the projectMetadata construction
referencing LOO-1186 cycle 2 to record that the contracts must be updated to
include 'frontend-snapshot-live' in ProjectKind and add liveUrl/viewport fields
to ProjectMetadata so the mismatch is tracked and not silently ignored by
`@ts-nocheck`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 22203c92-aa4e-45f2-987f-54b3bccfc1f2

📥 Commits

Reviewing files that changed from the base of the PR and between 455ca83 and 1fb2778.

📒 Files selected for processing (3)
  • apps/daemon/src/frontend-snapshot-import.ts
  • apps/daemon/src/server.ts
  • apps/daemon/tests/frontend-snapshot-import.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Agent
  • GitHub Check: Validate workspace
🔇 Additional comments (5)
apps/daemon/src/frontend-snapshot-import.ts (2)

39-57: LGTM — Clean expansion of accepted manifest kinds with well-documented viewport bounds.

The Set-based allowlist approach is cleaner than a switch statement and makes it easy to add future kinds. The viewport bounds (320–3840 × 480–2160) covering phone-portrait through 4K are sensible, and the inline comment explains the rationale clearly.


154-206: Solid security validation for live iframe metadata.

The protocol allowlist (http: / https: only) correctly blocks dangerous schemes that could be injected into an iframe src. Using Number.isFinite() handles edge cases like Infinity and NaN cleanly.

apps/daemon/src/server.ts (1)

858-873: Clean conditional metadata construction.

The branching correctly relies on the upstream validation in importFrontendSnapshotZip, so liveUrl and viewport are guaranteed valid by the time they're persisted here.

apps/daemon/tests/frontend-snapshot-import.test.ts (2)

253-471: Comprehensive test coverage for the new live manifest kind.

The tests thoroughly cover the validation paths: valid http/https URLs, rejection of dangerous schemes (javascript:, file://), malformed URLs, and viewport bounds/type checking.

One minor gap: there's no test for viewport.height exceeding MAX_VIEWPORT_HEIGHT (2160), though the symmetric width test exists. This is low priority since the bounds logic is identical for both dimensions.


155-165: LGTM — Test updated to reflect expanded accepted kinds.

The assertion now correctly expects the error message to list both "frontend-snapshot" and "frontend-snapshot-live".

Comment thread apps/daemon/src/server.ts
Comment on lines +855 to +881
// For `frontend-snapshot-live` carry liveUrl + viewport into the
// project metadata so the OD viewer can render the live iframe (cycle
// 2 of LOO-1186). The static `frontend-snapshot` path is unchanged.
const isLive = manifestMetadata.kind === 'frontend-snapshot-live';
const projectMetadata = isLive
? {
kind: 'frontend-snapshot-live',
importedFrom: 'frontend-snapshot',
entryFile: imported.entryFile,
sourceFileName: originalName,
liveUrl: manifestMetadata.liveUrl,
viewport: manifestMetadata.viewport,
}
: {
kind: 'frontend-snapshot',
importedFrom: 'frontend-snapshot',
entryFile: imported.entryFile,
sourceFileName: originalName,
};

const project = insertProject(db, {
id,
name: declaredName,
skillId: null,
designSystemId: null,
pendingPrompt: `Imported from frontend snapshot: ${originalName}. Continue editing ${imported.entryFile}.`,
metadata: {
kind: 'frontend-snapshot',
importedFrom: 'frontend-snapshot',
entryFile: imported.entryFile,
sourceFileName: originalName,
},
metadata: projectMetadata,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if ProjectKind includes 'frontend-snapshot-live' and if ProjectMetadata has liveUrl/viewport

echo "=== Checking ProjectKind for 'frontend-snapshot-live' ==="
rg -n "frontend-snapshot-live" --type=ts packages/contracts/

echo ""
echo "=== Checking ProjectMetadata for liveUrl and viewport fields ==="
rg -n -A 30 "export interface ProjectMetadata" packages/contracts/src/api/projects.ts | head -40

Repository: looptech-ai/open-design

Length of output: 1198


🏁 Script executed:

#!/bin/bash
# Get the ProjectKind definition
echo "=== ProjectKind type definition ==="
rg -n -B 5 "type ProjectKind" packages/contracts/src/api/projects.ts | head -30

echo ""
echo "=== Checking for LOO-1186 references ==="
rg -n "LOO-1186" --type=ts .

echo ""
echo "=== Check for TODO comments related to snapshot types in contracts ==="
rg -n -B 2 -A 2 "snapshot" packages/contracts/src/api/projects.ts | head -40

Repository: looptech-ai/open-design

Length of output: 649


🏁 Script executed:

#!/bin/bash
# Get the actual code at lines 855-881 in server.ts to verify it matches the review snippet
echo "=== Actual server.ts lines 855-881 ==="
sed -n '855,881p' apps/daemon/src/server.ts | cat -n

Repository: looptech-ai/open-design

Length of output: 1408


🏁 Script executed:

#!/bin/bash
# Get the full ProjectKind type definition
echo "=== Full ProjectKind type definition ==="
rg -A 15 "^export type ProjectKind =" packages/contracts/src/api/projects.ts

echo ""
echo "=== Search for Cycle 2 or contracts update references ==="
rg -n "Cycle 2|cycle 2|contracts.*update|manifest.*update" --type=ts apps/daemon/src/server.ts

Repository: looptech-ai/open-design

Length of output: 483


🏁 Script executed:

#!/bin/bash
# Verify the mismatch: check what insertProject expects
echo "=== Checking insertProject signature ==="
ast-grep --pattern 'function insertProject($_) {
  $$$
}'

Repository: looptech-ai/open-design

Length of output: 108


Add a TODO comment to document the deferred contract update for 'frontend-snapshot-live'.

The code correctly persists liveUrl and viewport for live snapshots, but the type contracts are out of sync:

  • ProjectKind does not include 'frontend-snapshot-live'
  • ProjectMetadata does not include liveUrl or viewport fields

The @ts-nocheck directive hides this mismatch at compile time, but it creates a type contract gap. Since the comment references "cycle 2 of LOO-1186" and the test file mentions "cycle 1", add an explicit TODO referencing LOO-1186 to track the deferred contract update:

// TODO: Update contracts in LOO-1186 cycle 2 to include 'frontend-snapshot-live' 
// in ProjectKind and add liveUrl/viewport to ProjectMetadata
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/daemon/src/server.ts` around lines 855 - 881, The project persists
liveUrl and viewport for 'frontend-snapshot-live' via the projectMetadata object
(see projectMetadata and insertProject) but the TypeScript contracts
(ProjectKind and ProjectMetadata) are still out of sync; add a TODO comment near
the projectMetadata construction referencing LOO-1186 cycle 2 to record that the
contracts must be updated to include 'frontend-snapshot-live' in ProjectKind and
add liveUrl/viewport fields to ProjectMetadata so the mismatch is tracked and
not silently ignored by `@ts-nocheck`.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extends the OD daemon’s frontend-snapshot ZIP ingest to accept a new manifest kind (frontend-snapshot-live) and (for that kind) persist liveUrl + viewport into the created project’s metadata so the viewer can later render a live dev-server iframe.

Changes:

  • Accept frontend-snapshot-live in the frontend snapshot manifest parser and validate metadata.liveUrl (http/https only) plus bounded metadata.viewport.
  • Persist liveUrl and viewport into project.metadata when importing a live manifest kind.
  • Add importer tests covering the new kind’s validation rules and update the “bogus kind” error assertion.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
apps/daemon/src/frontend-snapshot-import.ts Accepts frontend-snapshot-live and validates liveUrl + viewport when present.
apps/daemon/src/server.ts Persists live import fields into project metadata when importing a live manifest.
apps/daemon/tests/frontend-snapshot-import.test.ts Adds importer unit tests for live-kind success and validation failures.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/daemon/src/server.ts
Comment on lines +855 to +873
// For `frontend-snapshot-live` carry liveUrl + viewport into the
// project metadata so the OD viewer can render the live iframe (cycle
// 2 of LOO-1186). The static `frontend-snapshot` path is unchanged.
const isLive = manifestMetadata.kind === 'frontend-snapshot-live';
const projectMetadata = isLive
? {
kind: 'frontend-snapshot-live',
importedFrom: 'frontend-snapshot',
entryFile: imported.entryFile,
sourceFileName: originalName,
liveUrl: manifestMetadata.liveUrl,
viewport: manifestMetadata.viewport,
}
: {
kind: 'frontend-snapshot',
importedFrom: 'frontend-snapshot',
entryFile: imported.entryFile,
sourceFileName: originalName,
};
Comment thread apps/daemon/src/server.ts
Comment on lines +855 to +867
// For `frontend-snapshot-live` carry liveUrl + viewport into the
// project metadata so the OD viewer can render the live iframe (cycle
// 2 of LOO-1186). The static `frontend-snapshot` path is unchanged.
const isLive = manifestMetadata.kind === 'frontend-snapshot-live';
const projectMetadata = isLive
? {
kind: 'frontend-snapshot-live',
importedFrom: 'frontend-snapshot',
entryFile: imported.entryFile,
sourceFileName: originalName,
liveUrl: manifestMetadata.liveUrl,
viewport: manifestMetadata.viewport,
}
@amacsmith amacsmith merged commit f14b4c4 into main May 4, 2026
8 checks passed
@amacsmith amacsmith deleted the auto/frontend-snapshot-live-kind branch May 4, 2026 06:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants