From 03751cd152917409252636c8d6e2765067c8da46 Mon Sep 17 00:00:00 2001 From: Somansh Reddy Satish Date: Fri, 17 Jul 2026 16:39:33 +0000 Subject: [PATCH 1/3] docs(guides): resolve the fidelity contradiction (preserve substance, adapt form) The guide called the rebuild 'lossy by nature' while the checklist demanded 'content match the brief exactly', with no rule for what to preserve vs adapt. On a live import CD kept fonts/palette/hero but genericized the source's real figures (2.4M signals/sec, +240% spike) into vague phrases. Resolve around one principle: substance (real copy, exact palette/fonts, distinctive figures/data, product names, signature visuals) is preserved verbatim; only the form (static page to timed multi-scene motion) adapts. --- docs/guides/claude-design-send-to-hyperframes.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/guides/claude-design-send-to-hyperframes.md b/docs/guides/claude-design-send-to-hyperframes.md index ba94a346e3..54cc549fbd 100644 --- a/docs/guides/claude-design-send-to-hyperframes.md +++ b/docs/guides/claude-design-send-to-hyperframes.md @@ -86,6 +86,7 @@ Gotchas learned from real imports: - **Prefer inline; never a dead or expiring ref.** Inline images/logos as `data:` URIs when practical — the only fully self-contained form. If an asset is too large to inline, use a **publicly-fetchable absolute URL**; never a relative path, a local-file variable, or a **short-lived/private host** (a signed URL that expires, `s3://`/presigned URLs) — those 404 by render time. (The runtime/GSAP/shader scripts are the only network fetches by design.) - **Watch for injected loader cruft.** If assets are fetched through a proxy/CDN that injects its own beacon scripts (e.g. a Cloudflare `cdn-cgi/challenge-platform` snippet), strip them — they 404 in the renderer and add noise. - **No placeholder assets** (`placehold.co`, lorem-ipsum). Ship real brand content. +- **Preserve substance; adapt form.** A composition is a *rebuild* of the design into a timed video, so the FORM changes: a static page becomes multi-scene motion, page nav/footer chrome is dropped or folded in, and a single hero may expand into a short narrative arc. But the brand's SUBSTANCE must survive verbatim: the real headline and copy, the exact palette and fonts, named products and features, real metrics and data points, and signature visual elements (a chart, a waveform, a product shot). Keep distinctive specifics *specific*: do NOT genericize them into vague phrases (e.g. turning a real metric like `2.4M signals/sec` into "streaming now", or a named feature into a generic label), and do NOT invent copy or numbers the brand never provided. When you expand a hero into extra scenes, build them only from the brand's own language and facts. --- @@ -382,7 +383,7 @@ Note: this is the one place the download-ZIP guidance is **reversed** — for Se ## Keep the original design as a reference -The single-file import is lossy by nature (a composition is a *rebuild* of your design, not the design itself). Preserve the original design **as a separate reference** (not as the composition that drives the video) so a later agent can pull back any content or styling the composition dropped. This turns a lossy first draft into a fixable one rather than a dead end. +A composition is a *rebuild* of your design into a timed video, so it adapts the **form** (see *Preserve substance; adapt form* above), but it should never lose the brand's substance. Preserve the original design **as a separate reference** (not as the composition that drives the video) so a later agent can pull back any distinctive content or styling the rebuild dropped despite that rule. This turns a first draft into a fixable one rather than a dead end. --- @@ -423,4 +424,4 @@ You don't author any of this. Produce a clean, on-brand, correctly-timed silent **Renders correctly:** - [ ] No `Date.now()`, unseeded `Math.random()`, `setInterval/Timeout`, `requestAnimationFrame`, `repeat: -1` - [ ] Timeline built synchronously; paused; framework owns playback -- [ ] Colors, fonts, content match the brief exactly; no placeholders; ≥2 animation patterns + mid-scene activity per scene +- [ ] Colors and fonts exact; brand substance preserved: real headline/copy, distinctive figures/stats/data points, product names, and signature visuals carried over verbatim (form adapted, never genericized or invented); no placeholders; ≥2 animation patterns + mid-scene activity per scene From ecc26263f83a1707d76728420e569d42778f1e00 Mon Sep 17 00:00:00 2001 From: Somansh Reddy Satish Date: Fri, 17 Jul 2026 17:43:59 +0000 Subject: [PATCH 2/3] test(guides): pin the Send-to fidelity contract against silent regression Address review on #2620: the resolved 'preserve substance, adapt form' instruction is load-bearing LLM-facing prompt text, but validate-docs only proves syntax. Add a semantic pin (mirroring packages/cli figma skillContent.test.ts) asserting the three positive concepts are present and the two retired contradictory phrases ('lossy by nature', 'content match the brief exactly') cannot silently return. --- .../claude-design/sendToGuideContract.test.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 packages/cli/src/commands/claude-design/sendToGuideContract.test.ts diff --git a/packages/cli/src/commands/claude-design/sendToGuideContract.test.ts b/packages/cli/src/commands/claude-design/sendToGuideContract.test.ts new file mode 100644 index 0000000000..58efeada16 --- /dev/null +++ b/packages/cli/src/commands/claude-design/sendToGuideContract.test.ts @@ -0,0 +1,28 @@ +// @vitest-environment node +import { describe, expect, it } from "vitest"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import { fileURLToPath } from "node:url"; + +// Semantic pin for the Claude Design "Send to HyperFrames" authoring guide. The guide is +// LLM-facing prompt text, so a wording regression silently reintroduces a real failure mode. +// A live Send-to import genericized a source design's concrete figures ("2.4M signals/sec" -> +// "streaming now") because the guide both called the rebuild "lossy by nature" AND demanded +// "content match the brief exactly" — a contradiction with no rule for what to preserve. This +// pins the resolved "preserve substance, adapt form" instruction and asserts the two retired +// contradictory phrases cannot silently return. validate-docs proves syntax, not intent. +const REPO_ROOT = join(fileURLToPath(new URL(".", import.meta.url)), "..", "..", "..", "..", ".."); +const GUIDE = readFileSync(join(REPO_ROOT, "docs", "guides", "claude-design-send-to-hyperframes.md"), "utf8"); + +describe("Send-to guide fidelity contract", () => { + it("carries the resolved 'preserve substance, adapt form' instruction", () => { + expect(GUIDE).toContain("Preserve substance; adapt form"); + expect(GUIDE).toContain("do NOT genericize"); + expect(GUIDE).toContain("do NOT invent copy or numbers"); + }); + + it("does not restore the retired contradictory fidelity phrasing", () => { + expect(GUIDE).not.toContain("lossy by nature"); + expect(GUIDE).not.toContain("content match the brief exactly"); + }); +}); From b62bed5885f4f88fb9e607545013770d70191723 Mon Sep 17 00:00:00 2001 From: Somansh Reddy Satish Date: Fri, 17 Jul 2026 18:34:01 +0000 Subject: [PATCH 3/3] style(guides): oxfmt the Send-to fidelity-contract test Wrap the readFileSync call to oxfmt form (printWidth 100); unblocks required Format + its preflight/preview-regression cascade on #2620. --- .../src/commands/claude-design/sendToGuideContract.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/claude-design/sendToGuideContract.test.ts b/packages/cli/src/commands/claude-design/sendToGuideContract.test.ts index 58efeada16..494e58d32a 100644 --- a/packages/cli/src/commands/claude-design/sendToGuideContract.test.ts +++ b/packages/cli/src/commands/claude-design/sendToGuideContract.test.ts @@ -12,7 +12,10 @@ import { fileURLToPath } from "node:url"; // pins the resolved "preserve substance, adapt form" instruction and asserts the two retired // contradictory phrases cannot silently return. validate-docs proves syntax, not intent. const REPO_ROOT = join(fileURLToPath(new URL(".", import.meta.url)), "..", "..", "..", "..", ".."); -const GUIDE = readFileSync(join(REPO_ROOT, "docs", "guides", "claude-design-send-to-hyperframes.md"), "utf8"); +const GUIDE = readFileSync( + join(REPO_ROOT, "docs", "guides", "claude-design-send-to-hyperframes.md"), + "utf8", +); describe("Send-to guide fidelity contract", () => { it("carries the resolved 'preserve substance, adapt form' instruction", () => {