Expands the slice from a single 300x250 banner to four IAB sizes (300x600, 300x250, 728x90, 160x600) driven by a designer-authored TypeSystem and a per-row strip review surface. Layout engine - TypeSystem with role-based typography (headline/subheadline/cta/legal) and piecewise size-class derivation: half_page / rectangle / leaderboard / skyscraper / mobile_banner. - resolveLayout now derives per-size font/leading from the role + artboard size, then clamps to a legibility floor and emits a constraint_signal when copy does not fit at the floor. - Four reference templates with character constraints per size. AI pipeline (Shape B) - One extract + one generate per feed row; generate returns per-size copy keyed by artboard_id plus a shared rationale block. - Constraint-signal retry: orchestrator tightens per-(artboard, field) limits and re-calls generate before giving up. - orchestrateRow returns specs[] + rationale + constraint_signals. Review UI - /review renders one strip per feed row, all four sizes side-by-side at true pixel dimensions, synced on a single GSAP master timeline. - AiReasoningDrawer shows a per-size copy table, shared rationale, and any constraint signals that fired. - /api/generate response grouped by row; /api/export accepts the same shape and writes exports/row-N/artboard_id.zip. Render worker - render-to-zip / render-many accept optional subdir + filename overrides so multi-size exports can be grouped by feed row. Docs - VERTICAL_SLICE and BUILD_SEQUENCE updated for the multi-size scope. - RESOLVED_FEED.md documents the V1 Resolved Creative Feed proposal. - SLICE_DEVIATIONS.md records where the slice diverges from V1. Tests: 56 pass (28 layout-engine + 14 api-lib + 14 render-worker). Web app: tsc clean, next build succeeds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
12 KiB
SLICE SCOPE — read this first
You are building a vertical slice of the platform, not V1. The architecture document and BUILD_SEQUENCE.md describe the full V1. This slice is deliberately narrower.
What's in the slice: one template containing four artboards (300x600 reference, 300x250, 728x90, 160x600), authored against a designer-defined TypeSystem; one feed format (CSV); one AI pipeline (four agents, real Claude calls, size-aware copy adaptation); one review grid (per-row strip showing all four sizes at true pixel dimensions); one render path (Playwright local, four zips per row); one ad server profile (IAB).
What's out: template builder UI, asset library, variant groups, version control, human overrides, conflict resolution, approval workflow, auth, Docker, CM360, trafficking sheet, brief mode, Figma. None of these exist in the slice.
The TypeSystem is a slice-originated artifact that lives on the Template and is consumed by packages/layout-engine/src/type-scale.ts. Per-size font sizes are derived, not hand-authored, for non-reference artboards. See SLICE_DEVIATIONS.md entry #10 for the full rationale.
When in doubt: build less, ship the demo, extend after. If you find yourself adding a feature not in VERTICAL_SLICE.md, stop and confirm.
The full V1 plan is preserved in BUILD_SEQUENCE.md for after the slice ships.
CLAUDE.md
You are working on an agentic HTML5 banner production platform. Read this file at the start of every session before writing any code. It tells you what we are building, what is already decided, and what not to do.
What this product is
A banner production platform where humans design templates and AI does the scaling. The split is sharp:
- Humans design. Templates, character constraints, layout rules, approved crops per artboard size, brand voice profiles.
- AI scales. Generates copy and selects assets against templates, either driven by a locked data feed or a structured brief.
- Humans review and approve. Every override is logged. Re-generation never destroys human edits.
- Output is production-ready HTML5 conforming to IAB and ad-server-specific specs (CM360 first, others later).
The product is not a Figma plugin. Figma is a future bridge for clients with existing design investment, not a dependency. Build everything assuming Figma does not exist. The data model has hooks for Figma sync (V2), but no V1 code path requires it.
What is locked
These decisions are made. Do not re-litigate them when working on a task. If you believe a decision is wrong, surface it explicitly — do not silently substitute alternatives.
Stack
- Monorepo: Turborepo. Shared TypeScript types across all packages.
- Frontend: Next.js 14 (App Router). Zustand for state. react-konva for canvas.
- Layout engine: Dropflow (WebAssembly). Runs identically in browser and Node render workers. This is the keystone of the whole product — see [Text Group System] below.
- Backend: Node.js + TypeScript. tRPC for end-to-end type safety.
- Database: PostgreSQL 16 with JSONB columns. jsondiffpatch for version deltas.
- AI: Anthropic Claude API. Multi-step decoupled agents, never monolithic prompting.
- Orchestration: claude-opus-4
- Generation: claude-sonnet-4
- Validation: claude-haiku-4
- Render workers: Playwright in Docker. BullMQ + Redis queue. Fonts baked into image at build time.
- Animation: GSAP (GreenSock). Mandatory — it is whitelisted by ad servers and excluded from weight calculations.
- Assets: S3 + CloudFront. Sharp for processing.
- Auth: Clerk. Roles: Designer, Producer, Creative Director, Trafficker.
Architectural principles
- The BannerSpec is the contract. A single TypeScript interface that flows from AI output through the version service, the render worker, and the export pipeline. Every service uses the exact same shape. Defined in
packages/types. - Services own their domain entirely. No service reaches into another's data store. The boundaries are listed in [Service Map] below.
- AI never writes to the database. The orchestration service returns a typed spec. Database writes happen in the version service.
- Validation is programmatic, not AI. Character counts, asset rights, weight estimation, schema conformance — all checked by code after AI returns. AI output is never trusted.
- Every prompt is versioned in
/prompts/, never hardcoded. When a prompt changes, the version is logged with the generation event. This enables A/B testing and rollback. - Event sourcing for versions. A campaign is a sequence of versions: full snapshots on AI generation, deltas on human edits. Never destructive overwrites.
- Human overrides survive regeneration. They live in a separate table with
field_pathreferences. The merge algorithm is in Part 7 of the architecture doc. V1 direction (seeRESOLVED_FEED.md): copy edits collapse into a sparse per-product-per-size resolved creative feed that lives upstream of generation, eliminating the copy-override conflict-resolution problem entirely. Non-copy overrides (visual, layout, animation, asset selection) remain on a smaller patch layer per Part 7. The slice does not implement the resolved feed but persists per-size editorial decisions inBannerSpec.ai_reasoning.per_size_decisionsas a forward-pointer (see SLICE_DEVIATIONS.md entry #11).
What not to do
- Do not use Fabric.js. Konva.js is the choice. Memory issues at scale.
- Do not use Puppeteer. Playwright supports isolated browser contexts in one instance — required for concurrent renders.
- Do not use CSS animations or anime.js for ad output. GSAP only. Ad servers whitelist it; nothing else gets the weight exemption.
- Do not embed prompt templates in application code. They live in
/prompts/, loaded at runtime. - Do not let AI make asset selection decisions creatively. Asset selection is a deterministic metadata query against the variant group's selection rules. AI never "chooses" a logo or hero shot.
- Do not run AI in a single monolithic prompt. The pipeline is four discrete stages with typed interfaces between them.
- Do not write the canvas first. The build sequence is data layer → types → layout engine → canvas. The canvas depends on the layout engine.
- Do not destructively overwrite versions on regeneration. All history is preserved.
- Do not trust character counts from AI output. Validate programmatically after each generation. Retry once, then flag for human.
- Do not deploy render workers to serverless. Playwright requires persistent processes. ECS with dedicated containers.
- Do not skip the 5% internal padding on text containers. This absorbs sub-pixel font rendering drift between browser preview and Playwright render.
- Do not hand-author per-layer font sizes in non-reference templates. Sizes derive from the TypeSystem on the reference artboard via
deriveTypeSpec. Templates declare layout, layer composition, hero treatment, and animation; typography is the system's job. Per-template overrides exist for legitimate surface reactions (color on a light vs. dark hero, for example) but base sizes are formula-derived.
The Text Group System — the defining technical area
Every other piece of the product is solvable with known patterns. This one is the differentiator and the risk.
The problem: Variable-length copy in absolutely-positioned canvas layouts. When a 30-character headline becomes a 65-character headline, siblings must move predictably and the preview must match the final render exactly.
The approach:
- Designer creates a
GroupLayerwithgroup_behavior: 'flex_vertical'. - Each text layer inside has
push_siblingsrules: which sibling moves, in what direction, with what minimum gap, up to what max push. - When copy is set, the headless Dropflow WASM engine is called with the text string and typography spec.
- Dropflow returns intrinsic dimensions. If text overflows, font shrinks 1px at a time until it fits or hits
min_font_size. - If
min_font_sizeis hit before fit, container expands andpush_siblingscascade kicks in. - Every layout decision is logged in
layout_logon the resolved layer (font size reduced, siblings pushed, overflow triggered).
Why this is the keystone: Same Dropflow instance runs in the browser preview AND in the Node render worker. This is the only way to guarantee what the reviewer sees is what ships.
Full spec in Part 5 of the architecture document.
Service Map
| Service | Owns | Never Touches |
|---|---|---|
| Template Service | Template specs, constraint definitions, artboard configs | Asset binaries, campaign data |
| Asset Service | Binary storage, metadata, crop definitions, rights | Template structure, copy |
| Brief/Feed Service | Campaign intake, feed validation, dead letter queue | Rendering, AI calls |
| AI Orchestration | Claude API calls, prompt templates, spec assembly | Database writes (returns spec only) |
| Version Service | Snapshots, delta patches, conflict detection | Rendering, export |
| Render Service | Playwright execution, image encoding, HTML5 compilation | AI, database |
| QA Service | Automated gate checks, compliance validation | Rendering, AI |
| Export Service | ZIP assembly, ad server packaging, format conversion | Template editing |
Where things live
/apps
/web Next.js client (template builder, review UI, asset library)
/api tRPC server, service layer
/render-worker Playwright + BullMQ consumer
/packages
/types BannerSpec, Template, Campaign — the contract
/layout-engine Dropflow WASM wrapper + push_siblings logic
/ad-server-profiles IAB, CM360, etc. — click tag implementations
/qa-gates Programmatic QA checks
/prompts Versioned prompt templates (TS files exporting strings + schemas)
/infra
/docker Render worker image with fonts baked in
/db Postgres migrations
Full structure in PROJECT_STRUCTURE.md.
V1 scope — locked
In scope, in detail, in BUILD_SEQUENCE.md. Out of scope highlights so you don't accidentally build them:
- No Figma plugin or webhook
- No vision model for focal point detection
- No video or animated GIF assets
- No social formats
- No Amazon DSP / Xandr / Trade Desk profiles (IAB + CM360 only)
- No multi-client workspaces
- No comments or annotations (approvals only)
- No keyframe animation editor (presets only)
The data model has Figma hooks (figma_node_id, figma_file_key, figma_sync_events table) but they are nullable and unused in V1. They exist so V2 has zero migration cost.
How to work
- When starting a phase, read
BUILD_SEQUENCE.mdfor that phase's acceptance criteria. - When working on the text group system, the layout engine, or the version service, re-read the relevant Part of the architecture document — these areas have specific algorithms that must be implemented as written.
- When working on AI orchestration, the prompt templates are first-class artifacts. Treat them with the same care as code.
- When in doubt about a decision: this file is the source of truth, then the architecture document. If neither answers it, ask before guessing.
- If you find an inconsistency between this file and the architecture document, surface it. Do not silently choose one over the other.
Reference documents
ARCHITECTURE.md— full architecture spec (uploaded asV1_Architecture_Document_Agentic_HTML5_Banner_Prod)PROJECT_STRUCTURE.md— monorepo layout, file-by-filePHASE_1_BRIEF.md— first phase, what to build, in what orderBUILD_SEQUENCE.md— all 14 phases with acceptance criteriaRESEARCH.md— research backing the locked decisions (uploaded asResearch)RESOLVED_FEED.md— V1 proposal for the copy-override architecture; the resolved creative feed model