--- phase: 43-documents-branding plan: 01 type: execute wave: 1 depends_on: [] files_modified: - server/src/services/renderers/types.ts - server/src/services/renderers/pdf-renderer.ts - server/src/services/content-job-runner.ts - server/src/__tests__/pdf-renderer.test.ts - server/package.json autonomous: true requirements: [DOC-01, DOC-02, DOC-03] must_haves: truths: - "renderPdfDocument produces a pdf-document-bundle with non-empty pdfBase64 for report docType" - "renderPdfDocument produces a pdf-document-bundle for invoice docType with line items" - "renderPdfDocument produces a pdf-document-bundle for api-docs docType" - "content-job-runner dispatches pdf-document jobType to pdf-renderer" - "PdfDocumentBundle and BrandKitBundle types exported from types.ts" artifacts: - path: "server/src/services/renderers/pdf-renderer.ts" provides: "PDF rendering via Playwright HTML-to-PDF" exports: ["renderPdfDocument"] - path: "server/src/services/renderers/types.ts" provides: "PdfDocumentBundle + BrandKitBundle type definitions" contains: "PdfDocumentBundle" - path: "server/src/__tests__/pdf-renderer.test.ts" provides: "Unit tests for PDF renderer" min_lines: 40 key_links: - from: "server/src/services/content-job-runner.ts" to: "server/src/services/renderers/pdf-renderer.ts" via: "dynamic import in renderContent switch" pattern: 'case "pdf-document"' - from: "server/src/services/renderers/pdf-renderer.ts" to: "server/src/services/renderers/diagram-renderer.js" via: "resolveBrowserPath import" pattern: "resolveBrowserPath" --- PDF renderer and shared types for Phase 43 document generation. Purpose: Enable PDF report, invoice, one-pager, and API documentation generation via Playwright HTML-to-PDF. Also installs `archiver` and defines both bundle types needed by Plans 02-03. Output: Working pdf-renderer.ts, updated types.ts, job-runner wiring, archiver installed. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/43-documents-branding/43-RESEARCH.md @server/src/services/renderers/types.ts @server/src/services/content-job-runner.ts @server/src/services/renderers/diagram-renderer.ts @server/src/services/puter-inference.ts From server/src/services/renderers/types.ts: ```typescript export interface RenderResult { filename: string; contentType: string; buffer: Buffer; } export type ContentBundle = DiagramBundle | IconSetBundle | ThemePaletteBundle | WallpaperBundle | AppIconBundle | SocialPostBundle | ConvertBundle; ``` From server/src/services/renderers/diagram-renderer.ts: ```typescript export function resolveBrowserPath(): string; // resolves Playwright Chromium binary path ``` From server/src/services/puter-inference.ts: ```typescript export interface ChatMessage { role: "system" | "user" | "assistant"; content: string; } export async function puterChatComplete(messages: ChatMessage[], model?: string): Promise; ``` From server/src/services/content-job-runner.ts: ```typescript // renderContent() switch — add new case for "pdf-document" export async function renderContent(jobType: string, input: Record): Promise; ``` Task 1: Install archiver, add bundle types, create pdf-renderer with tests server/package.json, server/src/services/renderers/types.ts, server/src/services/renderers/pdf-renderer.ts, server/src/__tests__/pdf-renderer.test.ts - server/src/services/renderers/types.ts (current bundle types and ContentBundle union) - server/src/services/renderers/diagram-renderer.ts (resolveBrowserPath, Playwright launch pattern, stripUnsafeDirectives) - server/src/services/renderers/wallpaper-renderer.ts (stripMarkdownFences helper — lines 42-50) - server/src/services/puter-inference.ts (puterChatComplete signature) - server/src/__tests__/diagram-renderer.test.ts (test mock pattern for playwright-core and puter-inference) - Test 1: renderPdfDocument with docType="report" returns { filename: "document-report.json", contentType: "application/json" } and buffer parses to PdfDocumentBundle with type "pdf-document-bundle" and non-empty pdfBase64 - Test 2: renderPdfDocument with docType="invoice" returns bundle with docType "invoice" - Test 3: renderPdfDocument with docType="api-docs" returns bundle with docType "api-docs" - Test 4: renderPdfDocument with docType="one-pager" returns bundle with docType "one-pager" - Test 5: LLM system prompt varies by docType (report vs invoice vs api-docs vs one-pager) 1. Install archiver: `pnpm --filter @paperclipai/server add archiver && pnpm --filter @paperclipai/server add -D @types/archiver` 2. Add to server/src/services/renderers/types.ts — append BEFORE the ContentBundle union: ```typescript export interface PdfDocumentBundle { type: "pdf-document-bundle"; docType: string; title: string; pdfBase64: string; } export interface BrandKitBundle { type: "brand-kit-bundle"; spec: { name: string; tagline: string; primaryColor: string; secondaryColor: string; fontStyle: string; industry: string; }; logoSvgBase64: string; avatarPngs: Record; socialImages: Record; signatureHtml: string; letterheadHtml: string; guidelinesPdfBase64: string; zipBase64: string; } ``` Update ContentBundle union to include `| PdfDocumentBundle | BrandKitBundle`. 3. Create server/src/services/renderers/pdf-renderer.ts: - Import `chromium` from playwright-core, `resolveBrowserPath` from diagram-renderer, `puterChatComplete` from puter-inference, `RenderResult` and `PdfDocumentBundle` from types - Create a local `stripMarkdownFences(raw: string): string` helper that removes ```html and ``` fences (same pattern as wallpaper-renderer line 42-50 — do NOT import from wallpaper-renderer as it is not exported) - Create `buildPdfSystemPrompt(docType: string): string` with type-specific instructions for "report", "invoice", "one-pager", "api-docs" — each variant has different structural instructions. CRITICAL: system prompt must say "Use only inline CSS in a