| name | deck |
| description | General-purpose presentation/deck slide generation as gpt-image renders via the codex CLI — any brand, any theme, KO / EN / mixed language. Theme-intake from user assets with a neutral zero-config fallback, language-aware quality gates, numbers whitelist, and deterministic Pillow post-processing. Triggers - "deck", "slides", "presentation images", "pitch deck", "make slides for", /deck. For the IntuneLabs IR house style specifically, use /ppt instead. |
deck — brand-agnostic slide generation (codex CLI, gpt-image)
General-purpose sibling of ppt. ppt is the mature, frozen IntuneLabs-KO preset;
this skill is the canonical home of the shared mechanics (whitelist, gate, routing,
compositing). If a mechanic improves, change it HERE first and backport deliberately —
two drifting copies is the known failure mode (fresh-eyes 2026-07-08).
When to use
- Anyone (not just IntuneLabs) asks for presentation/IR/pitch slides as rendered images.
- The deck is English, Korean, or mixed.
- The user has their own brand (logo/palette/reference slide) — or no brand at all.
Interface: asset-first, default-heavy
Ask the user for AT MOST four things; default everything else structurally:
- Language — auto-detect from their content; ask only when ambiguous. Values:
ko / en / mixed.
- Brand assets — logo file, palette, an approved reference slide, real screenshots. None → neutral theme.
- Deck intent — audience, slide count, source content/facts.
- Strict strings & numbers — names, dates, figures, quotes that must appear verbatim.
Never run a long questionnaire. Extract, don't interrogate.
Step 0 — Theme contract (before any render)
Fill templates/theme-contract-template.yaml and show it to the user for a quick OK.
Sources, in order of preference:
- Reference slide provided → extract palette hexes, wordmark position, title grammar, density by inspection. Best signal; skip most questions.
- Logo + palette only → adopt the neutral layout grammar with their colors/wordmark.
- Nothing → the built-in neutral theme: off-white bg
#F5F6F8, white panels with slate-navy header bars #3D5A73, charcoal text #2D3436, one accent #C0392B used only for the single emphasis phrase per slide, no wordmark required, page number bottom-center.
Intake validations (each one is a known silent breaker):
- Contrast check — panel-header text vs bar color, body vs bg ≥ WCAG-ish 4.5:1. Dark/low-contrast palettes also break the
--dark-bbox phone detection in compositing → note screenshot_policy.patch_mode: quad in the contract when bg is dark.
- Wordmark polarity — white-on-transparent logos vanish on light bg; check the alpha channel (glyphs may live ONLY in alpha; never
convert('L') blindly) and pick/request the dark variant.
- Font reality — the image model cannot load brand fonts. Record a nearest describable family mapping (e.g. "Pretendard-like clean sans", "Georgia-like serif") in
typography, and the REAL font file only for Pillow micro-text patches.
- Layout grammar is brand identity — the two-line arrow title / takeaway bar grammar is a preset, not a universal. If the reference slide shows a different grammar, encode THEIRS in
title_grammar / layout_slots; don't ship someone a recolored IntuneLabs deck.
Escape hatch: if the brand requires pixel-exact identity (strict brand guidelines, legal
marks), genAI rendering is the wrong tool — offer deterministic HTML→PNG instead.
Language handling (the "full bilingual" contract)
mixed is NOT a vague third mode — every critical string carries a language tag; each
string gets its own language's gate. Structural defaults per language:
| concern | ko | en | mixed |
|---|
| prototype label | 시제품 | PROTOTYPE | both, per surface language |
| spell gate | Hangul char-by-char + KO killer list | word-by-word + EN killer list + casing check | per-string by tag |
| patch font | AppleSDGothicNeo.ttc | Helvetica/SF (or contract font) | per-string |
| footnote prefix | 1) … | 1) … | shared |
KO killer list (seed; grow per project): 코드로≠크드로 · 웹훅≠웹혹 · 플러그인≠풀러그인 ·
통계청≠통례청 · 캡처≠캡쳐 · 점수화≠잠수화 · 언제나≠연제나 · 친화≠천화.
EN failure modes & killer list (seed): pseudo-words and dropped/doubled letters
(Recommendation, Infrastructure, Institution, Governance, Efficiency, Acquisition,
Comparative, Loneliness), casing drift (Title Case↔ALL CAPS↔sentence case must match the
anchor), fake ligatures/kerning artifacts, confusables l/1 · e/o · rn/m, apostrophes
(it's/its), broken hyphenation, invented abbreviations. Put CRITICAL SPELLING callouts
in the prompt for any killer word that appears; for EN also pin the exact casing:
render exactly "Global Launchpad" — capital G, capital L, no hyphen.
Mixed-only gate items: (a) no script swap — a string tagged KO must not come back in
English or vice versa; (b) terminology-pair consistency — a KO term and its EN partner
(e.g. 시제품/PROTOTYPE) stay paired identically across all slides; (c) font harmony —
Hangul and Latin must read as one family, call it out in the style paragraph.
Generation mechanics (inherited, proven — see ppt for war stories)
Codex CLI on the user's ChatGPT subscription (never export/print OPENAI_API_KEY):
codex exec --skip-git-repo-check -i "<anchor.png>" -i "<wordmark.png>" [-i "<shot.png>"] < prompt.md
- Prompt via stdin;
-i order matters (prompt says "FIRST/SECOND/THIRD attached image").
- Output:
~/.codex/generated_images/<session>/ig_*.png at 1672×941. codex LIES about
paths — accept only paths under ~/.codex/generated_images/, fallback find -newermt.
- ~5–10 min/render;
run_in_background: true, timeout ≥ 1200000; ≤4 concurrent; >8 slides
→ Workflow (author → generate → gate, batches of 4).
- Prompt skeleton:
templates/slide-prompt-template.md (parameterized by the theme
contract — fixed frame, title grammar, density rule, spelling callouts, numbers whitelist).
- Anchor discipline: render slide 1, get user approval, then attach it as ref 1 to
every later render. The anchor IS the theme contract made visible.
The gate (run on EVERY render, viewed with Read)
- Spelling/casing, language-aware — per-string by language tag (see above), 0 defects.
- Numbers whitelist — every visible digit ⊆ the prompt's exhaustive allowed list.
- Screenshot preservation — real screenshots never redrawn; garbled → composite queue.
- Layout/density — matches the theme contract's grammar and density rule; no stray top numerals.
- Honesty guardrails — no unverified claims; prototypes labeled (
시제품/PROTOTYPE);
in-progress infra described as in-progress; no fake facility/team photos.
- Anchor consistency — palette/typography/frame vs the approved anchor.
- (mixed only) Script-swap & terminology-pair check.
Retry ≤4 with targeted feedback; same fix fails twice → stop, best render + deterministic patch.
Deterministic post-processing
Scripts are shared with ppt via the scripts/ symlink (single canonical copy):
composite_screenshot.py — real pixels into rendered frames (--quad / --dark-bbox /
--flood). Dark-theme caveat: --dark-bbox assumes dark phone on light bg; on dark
themes use --quad with crop-verify.
contact_sheet.py — N-up review montage.
- Micro text / verbatim-critical strings (dates, names, quotes, legal lines) are ALWAYS
typeset deterministically with the per-language patch font — genAI garbles them plausibly.
- Frame normalization at merge: stamp wordmark + page numeral at fixed coords on all
slides, then resize 1672×941 → 1920×1080.
Loud/silent routing (unchanged, load-bearing)
- genAI: layout, diagrams, display-size text — failures are loud, the gate sees them.
- Pillow: micro text, verbatim-critical strings, anything that failed the gate twice.
- Real faces: only with the person's real photo attached as ref, rendered large, plus a
same-person gate item; drift or small faces → composite real pixels. Tell the user which
faces are genAI-passed.
Success criteria
File layout
~/.claude/skills/deck/
├── SKILL.md
├── templates/
│ ├── theme-contract-template.yaml
│ └── slide-prompt-template.md
└── scripts -> ../ppt/scripts # single canonical copy, do not fork