---
phase: 42-wallpapers-social-format-conversion-voice
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - server/src/services/renderers/types.ts
  - server/src/services/content-job-runner.ts
  - server/src/services/converter-capabilities.ts
  - server/src/services/renderers/wallpaper-renderer.ts
  - server/src/services/renderers/social-renderer.ts
  - server/src/services/renderers/convert-renderer.ts
  - server/package.json
autonomous: true
requirements: [CONV-08]
must_haves:
  truths:
    - "New npm dependencies (file-type, xlsx, csv-parse) are installed and importable"
    - "Bundle types (WallpaperBundle, SocialPostBundle, ConvertBundle) exist with correct shapes"
    - "content-job-runner switch handles wallpaper, social-post, and convert job types"
    - "Converter capabilities service probes pandoc/libreoffice at startup and caches result"
  artifacts:
    - path: "server/src/services/renderers/types.ts"
      provides: "WallpaperBundle, SocialPostBundle, ConvertBundle type definitions"
      contains: "WallpaperBundle"
    - path: "server/src/services/content-job-runner.ts"
      provides: "wallpaper, social-post, convert cases in renderContent switch"
      contains: "case \"wallpaper\""
    - path: "server/src/services/converter-capabilities.ts"
      provides: "Startup probe for pandoc/libreoffice, cached capability map"
      contains: "converterCapabilitiesService"
  key_links:
    - from: "server/src/services/content-job-runner.ts"
      to: "server/src/services/renderers/wallpaper-renderer.ts"
      via: "dynamic import in case wallpaper"
      pattern: "wallpaper-renderer"
    - from: "server/src/services/content-job-runner.ts"
      to: "server/src/services/renderers/social-renderer.ts"
      via: "dynamic import in case social-post"
      pattern: "social-renderer"
    - from: "server/src/services/content-job-runner.ts"
      to: "server/src/services/renderers/convert-renderer.ts"
      via: "dynamic import in case convert"
      pattern: "convert-renderer"
---

<objective>
Install Phase 42 dependencies, define shared bundle types, wire content-job-runner switch for three new job types, and create the converter capabilities probe service.

Purpose: Every subsequent plan depends on these types, job runner cases, and dependency availability. This is the foundation layer.
Output: Updated types.ts, content-job-runner.ts, new converter-capabilities.ts, installed packages.
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/42-wallpapers-social-format-conversion-voice/42-RESEARCH.md

@server/src/services/renderers/types.ts
@server/src/services/content-job-runner.ts
@server/package.json
</context>

<interfaces>
<!-- Key types and contracts the executor needs -->
From server/src/services/renderers/types.ts:
```typescript
export interface RenderResult {
  filename: string;
  contentType: string;
  buffer: Buffer;
}
export type ContentBundle = DiagramBundle | IconSetBundle | ThemePaletteBundle;
```

From server/src/services/content-job-runner.ts:
```typescript
export async function renderContent(jobType: string, input: Record<string, unknown>): Promise<RenderResult> {
  switch (jobType) {
    case "diagram": { ... }
    case "icon-set": { ... }
    case "theme-palette": { ... }
  }
}
```
</interfaces>

<tasks>

<task type="auto">
  <name>Task 1: Install dependencies and define bundle types</name>
  <files>server/package.json, server/src/services/renderers/types.ts</files>
  <read_first>server/src/services/renderers/types.ts, server/package.json</read_first>
  <action>
1. Install new dependencies in server/:
   ```bash
   cd /opt/nexus/server && pnpm add file-type@22.0.0 xlsx@0.18.5 csv-parse@6.2.1
   ```
   Do NOT install @types/xlsx — xlsx ships its own types.

2. Add these new bundle types to server/src/services/renderers/types.ts AFTER the existing ThemePaletteBundle:

   ```typescript
   export interface WallpaperBundle {
     type: "wallpaper-bundle";
     platform: string;
     width: number;
     height: number;
     pngBase64: string;
     prompt: string;
   }

   export interface AppIconBundle {
     type: "app-icon-bundle";
     sizes: Array<{ size: number; pngBase64: string }>;
     prompt: string;
   }

   export interface SocialPostBundle {
     type: "social-post-bundle";
     platform: string;
     post: string;
     hashtags: string[];
     slides?: string[];
     charLimit: number;
   }

   export interface ConvertBundle {
     type: "convert-bundle";
     outputFilename: string;
     outputMime: string;
     outputBase64: string;
     method: "direct" | "ai-bridge";
   }
   ```

3. Update the ContentBundle union type to include the new bundles:
   ```typescript
   export type ContentBundle = DiagramBundle | IconSetBundle | ThemePaletteBundle | WallpaperBundle | AppIconBundle | SocialPostBundle | ConvertBundle;
   ```
  </action>
  <verify>
    <automated>cd /opt/nexus/server && npx tsc --noEmit 2>&1 | head -20</automated>
  </verify>
  <acceptance_criteria>
    - grep "WallpaperBundle" server/src/services/renderers/types.ts
    - grep "SocialPostBundle" server/src/services/renderers/types.ts
    - grep "ConvertBundle" server/src/services/renderers/types.ts
    - grep "AppIconBundle" server/src/services/renderers/types.ts
    - grep "file-type" server/package.json
    - grep "xlsx" server/package.json
    - grep "csv-parse" server/package.json
  </acceptance_criteria>
  <done>All four bundle types exported from types.ts. file-type, xlsx, csv-parse installed in server/package.json.</done>
</task>

<task type="auto">
  <name>Task 2: Wire content-job-runner switch and create converter capabilities service</name>
  <files>server/src/services/content-job-runner.ts, server/src/services/converter-capabilities.ts, server/src/services/renderers/wallpaper-renderer.ts, server/src/services/renderers/social-renderer.ts, server/src/services/renderers/convert-renderer.ts</files>
  <read_first>server/src/services/content-job-runner.ts, server/src/utils/execFileNoThrow.ts</read_first>
  <action>
1. Add three new cases to the `renderContent` switch in content-job-runner.ts, following the exact existing pattern (dynamic import of renderer, call render function, return result). For now, create stub renderer imports that will be implemented in Plans 02-04:

   ```typescript
   case "wallpaper": {
     const { renderWallpaper } = await import("./renderers/wallpaper-renderer.js");
     return renderWallpaper(input);
   }
   case "social-post": {
     const { renderSocialPost } = await import("./renderers/social-renderer.js");
     return renderSocialPost(input);
   }
   case "convert": {
     const { renderConvert } = await import("./renderers/convert-renderer.js");
     return renderConvert(input);
   }
   ```

2. Create stub renderer files so tsc resolves the imports (Plans 02-04 replace with real implementations):
   - server/src/services/renderers/wallpaper-renderer.ts: export async function renderWallpaper that throws "Not implemented"
   - server/src/services/renderers/social-renderer.ts: export async function renderSocialPost that throws "Not implemented"
   - server/src/services/renderers/convert-renderer.ts: export async function renderConvert that throws "Not implemented"
   Each stub must import and return RenderResult type.

3. Create server/src/services/converter-capabilities.ts implementing the startup probe pattern:
   - Export interface ConverterCapabilities { imageConverter: boolean; audioVideoConverter: boolean; docConverter: boolean; dataConverter: boolean }
   - Export function converterCapabilitiesService() with a get() method
   - Use execFileNoThrow from src/utils/execFileNoThrow.ts (project standard — do NOT use child_process.exec directly) to probe "pandoc" and "libreoffice" with ["--version"] argument
   - imageConverter, audioVideoConverter, dataConverter always true (npm deps)
   - docConverter true only if pandoc or libreoffice binary responds with status 0
   - Cache result after first probe in module-level variable
  </action>
  <verify>
    <automated>cd /opt/nexus/server && npx tsc --noEmit 2>&1 | head -20</automated>
  </verify>
  <acceptance_criteria>
    - grep "case \"wallpaper\"" server/src/services/content-job-runner.ts
    - grep "case \"social-post\"" server/src/services/content-job-runner.ts
    - grep "case \"convert\"" server/src/services/content-job-runner.ts
    - grep "converterCapabilitiesService" server/src/services/converter-capabilities.ts
    - grep "ConverterCapabilities" server/src/services/converter-capabilities.ts
    - grep "execFileNoThrow" server/src/services/converter-capabilities.ts
    - grep "renderWallpaper" server/src/services/renderers/wallpaper-renderer.ts
    - grep "renderSocialPost" server/src/services/renderers/social-renderer.ts
    - grep "renderConvert" server/src/services/renderers/convert-renderer.ts
  </acceptance_criteria>
  <done>content-job-runner handles wallpaper, social-post, convert job types. Stub renderers satisfy tsc. Converter capabilities service probes and caches binary availability using execFileNoThrow.</done>
</task>

</tasks>

<verification>
- `cd /opt/nexus/server && npx tsc --noEmit` passes with zero errors
- All new types are exported from types.ts
- Three new cases exist in content-job-runner switch
</verification>

<success_criteria>
- Three new bundle type interfaces exported from types.ts
- Three new job type cases in content-job-runner.ts switch
- Stub renderer files exist for wallpaper, social, convert
- converter-capabilities.ts probes pandoc/libreoffice using execFileNoThrow and caches
- tsc compiles cleanly
</success_criteria>

<output>
After completion, create `.planning/phases/42-wallpapers-social-format-conversion-voice/42-01-SUMMARY.md`
</output>
