| 1 | > See [`image-base.md`](./image-base.md) for the common framework. For the web sourcing path, see [`image-searcher.md`](./image-searcher.md). |
| 2 | |
| 3 | # Image_Generator Reference Manual |
| 4 | |
| 5 | Role definition for the **AI image generation path**: convert each active `Acquire Via: ai` row into an optimized prompt, generate the image, and save it to `project/images/`; also defines the `slice` derivation path for AI-generated illustration sheets. |
| 6 | |
| 7 | **Trigger**: the Default Generate resource list contains `Acquire Via: ai` or `slice`, or Quick Generate has resolved a required AI/sliced image in active context. Load only when at least one such resource exists. |
| 8 | |
| 9 | --- |
| 10 | |
| 11 | ## 1. Core Principle — Maximize AI Image Capability in Service of the Deck |
| 12 | |
| 13 | AI images exist to serve the deck's communication goal. Pick whatever combination of `page_role` and `text_policy` makes the page work best. |
| 14 | |
| 15 | **Two page roles** (orthogonal to type): |
| 16 | |
| 17 | | `page_role` | Use | |
| 18 | |---|---| |
| 19 | | `local` | Image occupies a prepared SVG region. The AI composes inside that bitmap/container; it does not choose the page region or final SVG geometry | |
| 20 | | `hero_page` | Image is the page's main voice — cover, chapter divider, mood transition, single-number hero, closing quote. SVG above may be minimal or empty | |
| 21 | |
| 22 | **Two text policies** (orthogonal to page_role): |
| 23 | |
| 24 | | `text_policy` | Use | |
| 25 | |---|---| |
| 26 | | `none` | No text inside the image | |
| 27 | | `embedded` | Image contains stable text as part of the artwork — decorative lettering, artistic wordmarks, hand-lettered keywords, or figure-internal labels | |
| 28 | |
| 29 | **Hard rule — only what's actually hard**: |
| 30 | |
| 31 | - Same `deck_rendering` + same core deck color anchors/semantic behavior for every image in the deck |
| 32 | - HEX codes and color names are rendering guidance — never visible text in the image |
| 33 | - Long body copy / data points / bulleted lists / long quotes stay in SVG (improving them later means regenerating the image, which is expensive) |
| 34 | - **In-image text is only for words that will not need editing later** — visual keywords, decorative lettering, mood words. Editable text (titles that may be reworded, subtitles, dates, authors, captions, body) belongs in SVG. Changing one in-image word costs an image regeneration; one SVG word costs a keystroke. |
| 35 | - Prompts are one coherent prose paragraph, not tag soup (a model-output reality, not an aesthetic choice) |
| 36 | |
| 37 | Everything else inside the prepared bitmap is the AI's judgment per page. No mandated padding, no type-locked text_policy, no scenario whitelists for hero_page. |
| 38 | |
| 39 | --- |
| 40 | |
| 41 | ## 2. Style and Composition Inputs |
| 42 | |
| 43 | Every AI image uses one deck-wide rendering, the deck's stable color anchors/semantic behavior, and a per-image type / internal composition. Only rendering is a separate image-direction decision. |
| 44 | |
| 45 | | Dimension | Decides | When fixed | |
| 46 | |---|---|---| |
| 47 | | **Rendering** | Visual style family (vector / sketch-notes / 3d-isometric / corporate-photo / …) | Once per deck — every AI image in the deck shares one rendering | |
| 48 | | **Deck colors** | Core background / primary / accent / secondary-accent / text anchors from `spec_lock.md colors` in Default Generate, or from the active-context visual decisions in Quick Generate | Default: anchored after Stage 2; Quick: resolved before acquisition | |
| 49 | | **Type** | Optional recall for a local structural infographic's internal skeleton (infographic / flowchart / framework / matrix / cycle / funnel / pyramid / comparison / timeline / map / scene). Use it when one template fits; otherwise omit type and write the composition directly in §4.1 E prose. Local single-subject/portrait and `hero_page` images also omit type. | Per image | |
| 50 | |
| 51 | > Rendering decides *how the image is drawn* (line quality, texture, depth). Color instructions begin from the deck roles: background / secondary background usually dominate, primary carries main forms, and accents stay scarce. Adjust proportions and derive coherent lighting/material/tint transitions for the image context; do not replace the deck's identity with an unrelated image-only palette. |
| 52 | |
| 53 | ### 2.1 Where to find each dimension |
| 54 | |
| 55 | | Reference | Loaded | |
| 56 | |---|---| |
| 57 | | [`image-renderings/_index.md`](./image-renderings/_index.md) — rendering catalog + auto-selection table | Always (Step 1 below) | |
| 58 | | [`image-type-templates/_index.md`](./image-type-templates/_index.md) — type catalog + auto-selection table | Always (Step 1 below) | |
| 59 | | `image-renderings/<chosen>.md` | After Step 2 resolves the rendering — one preset file, or every exact reference listed for `custom` | |
| 60 | | `image-type-templates/<chosen>.md` | After Step 3 picks the type per image — only the types actually used | |
| 61 | |
| 62 | **Hard rule — on-demand loading**: |
| 63 | |
| 64 | - Read the rendering and type `_index.md` files once at role entry. |
| 65 | - After locking inputs, read **only** the specific preset rendering, custom rendering references, and type files selected. |
| 66 | - **Never** glob-read an entire subdirectory (`image-renderings/*.md` is forbidden). Token cost balloons and the AI loses focus. |
| 67 | |
| 68 | --- |
| 69 | |
| 70 | ## 3. Workflow |
| 71 | |
| 72 | ### Step 1 — Load the dimension indices |
| 73 | |
| 74 | Read the two index files that own user-visible image direction and per-image internal composition. |
| 75 | |
| 76 | ``` |
| 77 | read_file references/image-renderings/_index.md |
| 78 | read_file references/image-type-templates/_index.md |
| 79 | ``` |
| 80 | |
| 81 | ### Step 2 — Resolve deck-wide rendering + deck colors |
| 82 | |
| 83 | **Default Generate path — Strategist already recorded rendering and core deck color anchors in `spec_lock.md colors`**: |
| 84 | |
| 85 | ``` |
| 86 | image_rendering: vector-illustration |
| 87 | background: #F8F9FA |
| 88 | primary: #1E3A5F |
| 89 | accent: #D4AF37 |
| 90 | ``` |
| 91 | |
| 92 | Use them as identity anchors. Do not create another user-facing image-color choice. The rendering and image subject may derive coherent tonal transitions, material colors, lighting, and atmospheric hues when the context requires them, while the core roles keep their established meaning. |
| 93 | |
| 94 | **Quick Generate path**: the main agent resolves one active-context rendering/color set, honoring explicit user values and deciding the rest without interaction. Write it to `image_prompts.json`; create no planning artifacts. |
| 95 | |
| 96 | **Hard rule — `custom` catalog basis**: when `image_rendering` is `custom`, first inspect the optional `image_rendering_references` row. If present, read every exact `image-renderings/<id>.md` it lists and synthesize their line, texture, depth, material, and mood guidance under `image_rendering_behavior` before assembling prompts. If absent, the custom is genuinely novel: read no preset file and use `image_rendering_behavior` directly. Never infer or add adjacent references during execution. The deck color-role rows remain authoritative. |
| 97 | |
| 98 | **Declared-inference fallback — when an existing `spec_lock.md` omits the `image_rendering` key** (see [`failure-recovery.md`](../workflows/governance/failure-recovery.md) §2): |
| 99 | |
| 100 | This fallback covers a missing key only. An empty or invalid value stops for lock repair. Outside the active [`quick-generate`](../workflows/profiles/quick-generate.md) profile, if `spec_lock.md` itself is absent, stop at [`generate-pptx.md`](../workflows/generate-pptx.md) Step 5 before prompt assembly or image generation; do not use `design_spec.md` as a substitute. |
| 101 | |
| 102 | | Signal | Maps to | |
| 103 | |---|---| |
| 104 | | `design_spec.md d. Style` mode + descriptor | Rendering (consult renderings `_index.md` auto-selection table) | |
| 105 | | Existing `spec_lock.md colors` rows | Deck color anchors; interpret them with the completed `design_spec.md`, never replace confirmed identity from a second palette | |
| 106 | | Existing `spec_lock.md icons.library` | Sanity check: chosen rendering should be compatible with the icon library's visual weight | |
| 107 | |
| 108 | If rendering inference surfaces multiple candidates, pick the first; do not present another choice after confirmation. |
| 109 | |
| 110 | If the table returns `custom`, stop and repair the lock: authoring `image_rendering_behavior` is a planning decision this fallback cannot make, and the deck's SVG style prose is not an image-rendering description. |
| 111 | |
| 112 | > **Tell the user**: when falling back, print one line "spec_lock.md has no `image_rendering`—inferring `<X>` from design_spec; image colors still use the locked deck roles." Then proceed. |
| 113 | |
| 114 | Then read the **single resolved** rendering file. It gives you: |
| 115 | |
| 116 | - The 80-120 word style paragraph (rendering) |
| 117 | - Two ready-to-paste rendering snippets (fewshot) |
| 118 | |
| 119 | Derive color behavior from the available roles and image context: background / secondary background usually carry most of the field, primary carries main forms, and accent / secondary accent remain selective. A rendering may justify a different balance and coherent derived tones; decorative text colors must remain readable. Add a new lock role only when that derived color becomes a reusable cross-image semantic token. |
| 120 | |
| 121 | ### Step 3 — Per-image type + assembly |
| 122 | |
| 123 | For each `Acquire Via: ai` row, use Strategist-owned §VIII/lock by default or the main agent's active-context Quick resource decision. Explicit values remain binding; Quick resolves omissions automatically. |
| 124 | |
| 125 | `Layout pattern` is a page-realization preference and is not copied wholesale into the bitmap prompt. Any generation-time subject direction, focal placement, quiet region, or overlay-safety requirement must therefore be present in the row's `Reference`, the matching §IX block, or Quick's active-context visual intent. |
| 126 | |
| 127 | 1. **Determine `page_role`** — the owning row's explicit value wins; a blank or omitted value resolves to `local`. In Default Generate, `hero_page` must be Strategist-explicit; in Quick Generate, the main agent may resolve it before acquisition in active context. |
| 128 | 2. **Determine `text_policy`** — the owning row's value wins when set. **Declared-inference fallback for a blank or omitted value**: pick `none` or `embedded` from the row's `Purpose`, `Reference`, and page intent based on whether in-image text serves the page. Long body / data / lists stay in SVG. |
| 129 | 3. **Determine type or free composition** — an Illustration Sheet omits manifest `type` and follows §4.3's grid composition. For another local structural infographic, use one of the 11 types only when the `_index.md` offers a real match; otherwise omit type and author the intended structure directly with §4.1 E. A local single-subject/portrait image omits type and uses §4.1 A/B inside its actual region. A `hero_page` omits type and uses §4.1 A/B/C/D/E. |
| 130 | 4. `read_file references/image-type-templates/<type>.md` only when a type was selected (and only if not already read). |
| 131 | 5. **Assemble the prompt** by combining: |
| 132 | - The rendering's style paragraph (from Step 2) |
| 133 | - Color-role instructions anchored by the deck HEX values and refined for the image context (from Step 2) |
| 134 | - The selected type's structural layout, or the no-type composition prose (from Step 3) |
| 135 | - The image's specific `Reference` intent (from `design_spec.md §VIII` or the Quick Generate active-context decision) |
| 136 | - Container sizing from the selected type file, or the row's Dimensions for no-type prose |
| 137 | - The hard rules from §5 below (HEX-not-as-text, rendering-aligned human depiction and likeness authorization, text policy) |
| 138 | |
| 139 | The assembled prompt is **one cohesive paragraph**, not a bulleted list of tags. See §4 for the assembly template. |
| 140 | |
| 141 | ### Step 4 — Write the manifest and execute the selected path |
| 142 | |
| 143 | Write `project/images/image_prompts.json` per §6, then follow §7. Default uses its confirmed path; Quick uses an explicit active-context path or `auto` without asking. |
| 144 | |
| 145 | --- |
| 146 | |
| 147 | ## 4. Prompt Assembly Template |
| 148 | |
| 149 | Every assembled prompt follows this paragraph structure. **Write prose, not tag soup**. |
| 150 | |
| 151 | ``` |
| 152 | [Rendering style paragraph — 80-120 words from the chosen rendering file]. |
| 153 | [Deck color behavior — state the core anchors and any context-justified tonal treatment, e.g. "secondary background #F8F9FA provides the breathing field, primary #1E3A5F carries main forms, accent #D4AF37 marks one emphasis; subtle lighter/darker material transitions remain in the same visual family"]. |
| 154 | [Composition — from the chosen type file or §4.1 no-type prose]. |
| 155 | [Image-specific subject — translated from the row's Reference intent into concrete visual nouns]. |
| 156 | [Container note — "composed as a {W}x{H}px image for {page_role} use"; add composition cues only when the page actually needs them. SVG-overlay-reservation cues ("leave the lower band calm — SVG title overlays it", "keep the right third calmer for SVG text") are valid when `page_role: hero_page`, or when §VIII `Reference` / §IX `Layout` explicitly plans native labels, hotspots, lenses, or other SVG overlays inside a `local` image region. Otherwise a `local` image is a self-contained region block and reserves no interior overlay space]. |
| 157 | [Hard rules — see §5]. |
| 158 | ``` |
| 159 | |
| 160 | **Word budget**: 150-300 words. Embedded-text prompts skew longer; pure background prompts can be shorter. |
| 161 | |
| 162 | **Forbidden — tag-soup prompts**: |
| 163 | |
| 164 | ``` |
| 165 | ❌ "modern, flat design, gradient, vibrant, professional, clean, 4K, high quality" |
| 166 | ``` |
| 167 | |
| 168 | This produces generic, model-average output. The model is not weighting your tags — write **one coherent visual scene** instead. |
| 169 | |
| 170 | ### 4.1 No-type composition primitives |
| 171 | |
| 172 | Use these when no structural type applies. A/B can describe either a hero image or a local single-subject/portrait region; scale their framing to the actual container. C/D are hero-page compositions. E authors any custom hero or local composition, including a structural infographic that does not genuinely match one of the 11 type templates. |
| 173 | |
| 174 | **Primitive A — single dominant subject (product / object / concept hero)** |
| 175 | |
| 176 | > Start with one dominant subject as the clear focal point, positioned with intent (centered, rule-of-thirds offset, or slight left/right). Scale it to command the container while keeping supporting context subordinate. Leave a deliberate open side when the page composition needs breathing room or an overlay; no fixed padding is implied. No second-place subject competing. |
| 177 | |
| 178 | Use for: product reveal, concept introduction, chapter-opener visual, brand statement, or a local single-object region. |
| 179 | |
| 180 | **Primitive B — single human subject (portrait)** |
| 181 | |
| 182 | > One person, frontal or three-quarter turn, head + upper body. Start with the face as the clear focal point, centered or rule-of-thirds offset, with eyes near the upper-third horizontal line. Background neutral, minimal, or softly blurred. Keep comfortable headroom and no competing foreground objects; adjust framing to the container rather than enforcing fixed padding. |
| 183 | |
| 184 | Use for: founder profile, speaker bio, testimonial page, or executive intro, including a local bio region. Let the chosen rendering and Reference determine photographic, editorial, painterly, graphic, or other figure treatment; see §5.2. |
| 185 | |
| 186 | **Primitive C — typographic hero (the text *is* the image)** |
| 187 | |
| 188 | > The image's central content is one large text element — a short headline, big number, or single word — rendered as art and carrying dominant visual weight. Keep any supporting visual (small icon, geometric anchor, accent line) clearly subordinate. Give the letterforms enough breathing room for readability, adjusting scale and spacing to the actual text and container. |
| 189 | |
| 190 | Use with `text_policy: embedded`. Must obey the §5.3 rule — text that is part of the artwork and stable can be embedded; copy that must stay exact or editable goes to SVG overlay (switch to Primitive D). |
| 191 | |
| 192 | **Primitive D — atmospheric backdrop (no subject)** |
| 193 | |
| 194 | > Atmospheric field with no dominant subject — gradients, subtle patterns, or restrained color blocks. A small geometric anchor may sit in a corner or along an edge. Arrange visual activity around the SVG overlay region named by the page plan so that region stays calm enough for its title or text; its position and extent follow the composition rather than a fixed percentage. |
| 195 | |
| 196 | **Applies to `page_role: hero_page` only.** The "calm center for SVG overlay" contract defines this primitive. A `local` image uses §3 type templates or §4.1 A/B/E instead; when §VIII / §IX explicitly plans native overlays inside that region, its prompt may reserve only the named focal/quiet area without turning the whole asset into Primitive D. |
| 197 | |
| 198 | Use for: cover background, chapter divider background, breathing-page background, any page where the SVG layer carries the words and the image only sets tone. |
| 199 | |
| 200 | **Primitive E — custom (escape hatch)** |
| 201 | |
| 202 | When none of A/B/C/D describe the page's intended layout (triptych, asymmetric multi-focal, narrative diorama, etc.), write the composition description directly into the prompt's composition sentence — same paragraph slot A/B/C/D occupy, but in your own words. No new field; the freedom is in the prose. |
| 203 | |
| 204 | **Default — concise custom composition prose (may override for subject accuracy)**: |
| 205 | |
| 206 | | Rule | Value | |
| 207 | |---|---| |
| 208 | | Length | One paragraph, 2-5 sentences, replacing A/B/C/D's opening paragraph | |
| 209 | | Content | State enough subject count and layout structure to make the composition executable; include breathing room or an SVG-overlay region only when the page composition actually needs it | |
| 210 | | Clarity | Describe the actual geometry; a primitive name alone is not a substitute | |
| 211 | |
| 212 | Example opening for a triptych hero: |
| 213 | |
| 214 | > Triptych — three equal vertical bands of canvas, each holding one symbolic object centered in its band; objects share a low horizon line; bands separated by 2px hairline rules; collectively reads as a single composed page. [...rest of prompt continues with rendering paragraph + color behavior + container note...] |
| 215 | |
| 216 | **Fewshot examples per primitive** (one each, deck-context placeholders intact): |
| 217 | |
| 218 | > **A — 3d-isometric + deck-color product reveal, text_policy: none, 600×600** |
| 219 | > |
| 220 | > 3D isometric illustration in true 30°/30°/30° projection. One dominant product-form subject — a stylized device or sleek tech object — commands the center of the canvas. The subject is rendered in primary electric blue `#0EA5E9` on its lit faces, with 15% darker tonal shift on shadowed faces. A subtle 8%-opacity outer glow halo surrounds the subject. Small supporting context: three thin connecting lines in accent vivid cyan `#06B6D4` arcing from the subject toward the canvas edges (suggesting connectivity), and a soft 8% drop shadow grounding the subject. Background is deep secondary navy `#0A0E27`, including the shadowed plane. The subject is clearly the singular focal element, with deliberate breathing room around it. Composed as a 600×600 hero block. NO text, letters, numbers, or labels anywhere. Color values are rendering guidance only. |
| 221 | |
| 222 | > **B — corporate-photo + deck-color executive headshot, text_policy: none, 600×800** |
| 223 | > |
| 224 | > Editorial corporate portrait photograph of one professional executive. The person is centered slightly left of canvas center, photographed from chest-up at eye level, looking confidently toward the camera with a relaxed natural expression — not posed-stiff, not over-smiling. Professionally attired in a contemporary business setting (a tailored blazer, neutral palette clothing). Soft natural light from the upper left, gentle shadow on the right side of the face. Diverse, professionally attired subject, photorealistically rendered, contemporary styling. Background is a softly out-of-focus office context — secondary light gray `#F8F9FA` wall with a subtle hint of primary deep navy `#1E3A5F` in a blurred architectural element. Color grading is restrained and professional. Shallow depth of field — subject sharp, background gently blurred. Subject's eyes positioned near the upper-third horizontal line, with comfortable headroom. Composed as a 600×800 bio portrait. NO text, name tags, or captions in the image. Color values are rendering guidance only. |
| 225 | |
| 226 | > **C — ink-notes + deck-color big-number stat, text_policy: embedded, 800×500** |
| 227 | > |
| 228 | > Professional hand-drawn visual-note style on pure white background. The image's central content is the hand-lettered number "100x" — rendered in bold confident ink strokes as the dominant element, centered with deliberate slight wobble characteristic of hand-lettering. Beneath the number, a thin hand-drawn underline in ink. To the side of the number, one small hand-drawn doodle decoration — a star or upward arrow — adds visual rhythm. Accent coral `#E8655A` (from the deck's accent) appears only as a tiny emphasis dot, totaling under 4% of the canvas. Background is pure white `#FFFFFF`. Composed as an 800×500 typographic hero block with enough breathing room for the letterforms to read clearly. No other text or labels in the image — just the "100x" headline and the small doodle. |
| 229 | |
| 230 | > **D — vector-illustration + deck-color cover background, text_policy: none, 1280×720** |
| 231 | > |
| 232 | > Clean flat vector illustration backdrop. Atmospheric composition with no central subject — bold geometric shapes arranged along the canvas edges to leave the planned central title field calm. Primary deep navy `#1E3A5F` forms a confident diagonal block across the lower-left area; secondary light gray `#F8F9FA` provides the breathing field; accent gold `#D4AF37` appears only as one thin geometric line near the lower right corner, under 5% of the canvas. Crisp 2px outlines, no gradients, a single 8% soft drop shadow under the navy block. The intended SVG title region is deliberately calm and unbusy. Composed as a 1280×720 full-bleed PPT background. NO text, letters, numbers, signs, watermarks, or written symbols anywhere in the image. Color values are rendering guidance only — do not display HEX codes or color names as text. Simplified geometric shapes only. |
| 233 | |
| 234 | ### 4.2 Prompt depth — expand for subject-domain accuracy |
| 235 | |
| 236 | **Hard rule**: For images whose deck purpose calls for subject-domain accuracy (scientific figures, academic paper figures, engineering schematics, medical / legal / regulated content), expand the prompt without budget ceiling — 500-1000+ words is normal. The §4 word budget (150-300) is the routine-illustration default, not a cap. |
| 237 | |
| 238 | **Forbidden — pre-emptive shortening**: never trim a subject-domain prompt to fit §4's budget. Name the field's visual conventions explicitly in the prompt. |
| 239 | |
| 240 | **Detail to name in the prompt** (illustrative, not an enumeration to match): |
| 241 | |
| 242 | | Domain | Conventions to spell out | |
| 243 | |---|---| |
| 244 | | chemistry / materials | IUPAC atom colors, bond conventions, lattice type, Å / ps units, subplot labeling (A / B / C circles), view angle | |
| 245 | | biology | cell compartment colors, scale bars, organelle conventions, staining palette | |
| 246 | | physics | axis labels with proper symbols, signature curve shapes, unit annotations, peak labeling format | |
| 247 | | engineering | schematic notation, dimension callouts, section-cut conventions | |
| 248 | |
| 249 | **When uncertain about field conventions**: read `sources/` before drafting the prompt. |
| 250 | |
| 251 | ### 4.3 Illustration sheets — one generation, many spot elements |
| 252 | |
| 253 | An illustration sheet can produce several small **spot illustrations** in one generation and preserve closely matched rendering, deck-color treatment, and line quality before slicing. |
| 254 | |
| 255 | **Default — one sheet for a compatible spot family (may override when separate generation serves the assets better)**: Prefer a sheet when several elements share similar proportions, detail, quality, and semantic precision. Generate elements separately when those needs differ materially; quantity alone neither requires nor forbids a sheet. A single hero/local image stays with the normal one-row-per-image flow (§4.1). |
| 256 | |
| 257 | **Hard rule**: a spot sheet is a generation source, not a slide asset. In Default Generate, keep the sheet row out of `spec_lock.md images`; in Quick Generate, retain its generation-only status in active context and the operational manifest. The sheet is never referenced from SVG. Only sliced element rows are placed. |
| 258 | |
| 259 | **Sheet prompt convention** (one manifest item, `page_role: local`, `text_policy: none`, `image_size` chosen from final placement size): |
| 260 | |
| 261 | - Choose the sheet `aspect_ratio` and `--grid` from the target element shape. Do not default every sheet to `1:1` + a symmetric grid. |
| 262 | - Lay the elements out in an explicit **R×C grid, evenly spaced with clear gutters**, each element **centered in its own cell** and isolated (no element bleeds into a neighbor). |
| 263 | - State the intended cell shape in the prompt: compact square object, tall portrait element, or wide landscape vignette. Do not let the model shrink every subject into a centered square sticker. |
| 264 | - One **flat single-color background** across the whole sheet, set to the deck's background/secondary HEX — this is what lets the slicer key it out cleanly and lets the cut element sit on the slide without a visible box. |
| 265 | - Shared `deck_rendering` + `color_scheme` as always. NO text, labels, or numbers anywhere (§5.1, §5.3). |
| 266 | |
| 267 | **Cell geometry is designed, not assumed.** `slice_images.py --grid RxC` cuts rows first and columns second. The cell ratio is: |
| 268 | |
| 269 | ```text |
| 270 | cell_ratio = sheet_ratio * rows / cols |
| 271 | ``` |
| 272 | |
| 273 | Use that deliberately. On a wide sheet (`16:9`, `21:9`, `4:1`, `8:1`), `1xN` makes each cell tall/portrait because the width is divided by `N` while height is kept; `Nx1` makes each cell wide/landscape because height is divided by `N` while width is kept. A designed `MxN` grid is also valid when the resulting cell ratio matches the intended placements. |
| 274 | |
| 275 | | Target spot shape | Sheet plan | Slice grid | |
| 276 | |---|---|---| |
| 277 | | Compact objects / badges | `1:1` sheet | `2x2`, `2x3`, or `3x3` | |
| 278 | | Tall side accents / upright objects | wide or square sheet | `1xN`, or any `MxN` whose cells are portrait | |
| 279 | | Wide banners / horizontal vignettes | wide sheet | `Nx1`, or any `MxN` whose cells are landscape | |
| 280 | |
| 281 | If one deck needs mixed shapes, create separate sheets per shape family unless one carefully designed grid gives every element enough room. Keep the visual family consistent through the same `deck_rendering` and `color_scheme`, not by forcing all cells into one square sheet. |
| 282 | |
| 283 | **Resource contract — the sheet and its elements are different row kinds.** A sliced element can only be placed if it exists in the active placeable-resource authority: `spec_lock.md images` in Default Generate or the current agent's prepared resource decision in Quick Generate. Default Generate keeps both row kinds in §VIII under [`strategist-image.md`](./strategist-image.md); Quick Generate resolves the same distinction in active context and its operational manifest without creating planning artifacts: |
| 284 | |
| 285 | - **Sheet row** — `Acquire Via: ai`, `Type: Illustration Sheet`, the intent prompt, named as the slice source with its intended cell shape and placement purpose (`Reference: landscape footer-vignette spot set`). It is generated in Step 5 but **never placed on a slide** — keep it **out of** `spec_lock.md images`. Image_Generator resolves the exact `aspect_ratio`, grid, and slice command from this intent. |
| 286 | - **Element rows** — one per used element, `Acquire Via: slice`, filename matching a `--names` output, `Reference` naming the parent sheet + cell/element. These **are** placed — list every one in the active placeable-resource authority, normally with `crop=no-crop` (a tight-trimmed transparent spot should be fit, not cover-cropped). Their dimensions are filled in after slicing (the preparation pass re-runs `analyze_images.py`). Each row carries an owner-resolved layout recommendation; SVG authoring may realize it as a direct cutout or inside an appropriate container while preserving the resource and crop/content constraints. |
| 287 | |
| 288 | For traceability, add optional `slice_grid` and `slice_names` fields to the sheet item in `image_prompts.json` after choosing the geometry. `image_gen.py` validates, preserves, and displays these metadata fields; it does not run the separate slicing command. |
| 289 | |
| 290 | **Slice** with [`slice_images.py`](../scripts/slice_images.py) — cells are cut row-major into individual files in `images/`. With `--alpha` they become transparent elements suitable for direct cutout placement or for composition inside a card, evidence frame, label, or other container. Recommended flags: `--names` (semantic per-cell filenames matching the element rows; the count **must** equal `rows*cols`), `--trim` (tight-crop each cell so imprecise placement inside a cell doesn't leave lopsided margins), `--alpha` (knock the flat background out to transparency so an element can sit on any slide color or container): |
| 291 | |
| 292 | ```bash |
| 293 | python3 scripts/slice_images.py <project>/images/illus_sheet.png --grid 2x3 \ |
| 294 | --names team,product,customer,growth,risk,vision --trim --alpha |
| 295 | ``` |
| 296 | |
| 297 | **Three constraints that decide whether it looks good**: |
| 298 | |
| 299 | 1. **Flat background, matched to the slide.** `image_gen.py` has no transparent-background mode, so the cut element carries whatever was behind it. A flat sheet background (= deck background HEX) is what `--alpha` keys out and what makes non-keyed pieces blend. |
| 300 | 2. **Clean grid, or it cuts ugly.** State the exact row/column structure and cell shape so the model does not invent a square matrix; `--trim` absorbs smaller placement variance. Do not generate several sheets or read them back merely to choose a favorite; re-roll only when user/live-preview feedback exposes an unusable slice. |
| 301 | 3. **Generate only as large as needed.** Each cell is a fraction of the sheet. Pick the smallest sheet size that keeps each sliced cell at least **1.5-2x** the intended display size. `1K` is usually enough for small 80-160px decorative spots; use `2K` for medium 180-320px placements; reserve `4K` for large, cropped, or potentially enlarged elements. |
| 302 | |
| 303 | **Reference — sliced-asset placement is not a constraint**: A transparent slice may remain an unboxed cutout or enter a card, evidence frame, label, panel, or other suitable container. The owner-resolved layout text is an expression recommendation; SVG authoring owns the actual geometry and treatment while preserving the resource role and crop/content constraints. |
| 304 | |
| 305 | **Through-line — one family, many roles.** A spot sheet pays off more when the same motif family also drives the cover and section dividers. A large cover / divider anchor is not a giant sheet cell—generate it as its own `hero_page` image sharing the sheet's `deck_rendering`, `color_scheme`, and subject world. Plan this only when the deck leans into illustration, never as a quota. |
| 306 | |
| 307 | --- |
| 308 | |
| 309 | ## 5. Global Hard Rules |
| 310 | |
| 311 | These rules apply to **every** prompt regardless of dimension choices. Append them as a closing sentence to every assembled prompt. |
| 312 | |
| 313 | ### 5.1 HEX is rendering guidance, not text |
| 314 | |
| 315 | Image generation models occasionally paint color names and HEX values as **visible labels in the image** (a `#1E3A5F` swatch literally drawn as the string "#1E3A5F"). This destroys the image. |
| 316 | |
| 317 | **Append to every prompt**: |
| 318 | |
| 319 | > Color values (HEX codes like #1E3A5F) and color names are rendering guidance only — do NOT display HEX codes, color names, or palette labels as visible text anywhere in the image. |
| 320 | |
| 321 | ### 5.2 Human depiction follows the selected rendering |
| 322 | |
| 323 | When the image contains people: |
| 324 | |
| 325 | > Match facial detail, anatomy, texture, and realism to the selected rendering and the row's Reference. A silhouette, detailed illustration, painterly figure, editorial photograph, or another treatment is valid when it belongs to that rendering. |
| 326 | |
| 327 | **Hard rule — likeness authorization**: Do not request an identifiable real-person or celebrity likeness unless the Reference explicitly names a user-authorized subject/source. Generic or fictional people remain free to follow the selected rendering. |
| 328 | |
| 329 | ### 5.3 Text policy — two-layer ownership |
| 330 | |
| 331 | Every AI-image page carries text in two layers: |
| 332 | |
| 333 | | Layer | Owned by | Examples | |
| 334 | |---|---|---| |
| 335 | | Layer 1 (image-owned) | the prompt — baked into the raster | figure-internal annotations (axis labels, A / B / C markers, units, scale bars, panel labels); architecture / schematic module names, node labels, signal-path identifiers; stable artistic lettering that *is* the visual | |
| 336 | | Layer 2 (SVG-owned) | `<text>` overlay — fully editable | authoritative deck/page/chapter titles; navigation, footer, body bullets, conclusion callout; readable copy, captions | |
| 337 | |
| 338 | `text_policy` controls only Layer 1. AI judges per image; no global default bias. |
| 339 | |
| 340 | **When `embedded` is the right call — positive triggers** (any one match supports `embedded`; the editability rule at the tail of §5.3 still has final say): |
| 341 | |
| 342 | | Trigger | Typical Layer 1 text | |
| 343 | |---|---| |
| 344 | | Paper-figure panel comparison (A/B/C, before/after) | Panel labels — `A` / `B` / `C`, or short panel descriptors | |
| 345 | | Textbook math / signal figure | Curve names (`sin` / `cos`), axis labels, unit symbols | |
| 346 | | Architecture / schematic following discipline conventions | Module names (`Self-Attention`, `FFN`, `Add & Norm`), node ids, signal-path tags | |
| 347 | | Data figure with stable axes | Axis labels, units, scale bars | |
| 348 | | Typographic hero (§4.1 Primitive C) | The designed word / number that *is* the image | |
| 349 | |
| 350 | Defaulting an entire `ai` resource list to `none` because "SVG can always overlay" is the failure mode this table exists to break. When any row matches a trigger, start at `embedded` and verify the editability filter below still holds. |
| 351 | |
| 352 | | `text_policy` | Prompt cue | |
| 353 | |---|---| |
| 354 | | `none` | "NO text of any kind anywhere in the image — no letters, numbers, signs, watermarks, labels, or written symbols." | |
| 355 | | `embedded` | Describe the stable Layer 1 lettering directly inside the visual scene: the exact character(s), how they are rendered, and the artistic treatment. | |
| 356 | |
| 357 | **Hard rule — cross-cutting**: Authoritative titles and Layer 2 chrome stay SVG regardless of `text_policy`. Bake title-like wording only when the approved plan explicitly treats those exact characters as stable artistic lettering that is part of the artwork rather than editable deck/page/chapter copy. Navigation, footer, body bullets, captions, and conclusion callouts always stay SVG. |
| 358 | |
| 359 | **Forbidden — text that may be reworded**: any word that may later change belongs in Layer 2, not Layer 1. Layer 1 is for stable visual identifiers and designed lettering that is part of the image itself. |
| 360 | |
| 361 | **Font choice for in-image text — free description, with the deck typography as one optional reference** |
| 362 | |
| 363 | The font for in-image text is a free natural-language description, not an enum. Pick whatever serves the image: blackletter for a heritage cover, hand-brushed for a manifesto poster, retro chrome 3D for Y2K, art-deco display for a luxury hero, ribbon script for a bookstore zine — any artistic treatment the image earns. |
| 364 | |
| 365 | The table below is **a reference for the one case where stable in-image lettering should read as the same typographic family as the SVG body** (e.g. an artistic cover wordmark should feel like the body Helvetica, not a surprise blackletter). Use it as a starting point, not a constraint. |
| 366 | |
| 367 | | Active typography source contains | Optional descriptor if you want to echo the SVG body | |
| 368 | |---|---| |
| 369 | | `KaiTi` / `FangSong` / `Georgia` / serif families | "elegant serif lettering, refined letterforms" | |
| 370 | | `Microsoft YaHei` / `PingFang SC` / `Arial` / sans-serif families | "clean geometric sans-serif, modern letterforms" | |
| 371 | | `SimHei` / `Impact` / `Arial Black` / display families | "bold display lettering, heavy expressive strokes" | |
| 372 | | `Consolas` / `Courier New` / monospace families | "monospace technical lettering, fixed-width" | |
| 373 | | sketch-notes / ink-notes rendering, or no family specified | "hand-lettered organic strokes, natural variation" | |
| 374 | |
| 375 | **When to ignore the table**: |
| 376 | |
| 377 | - Decorative / background lettering, posters, large mood words → describe the artistic treatment freely |
| 378 | - Stable artistic cover lettering that wants its own visual identity (blackletter, retro chrome, art-deco display, brushed script) → describe freely |
| 379 | - Sketch-notes / ink-notes / hand-drawn renderings where the lettering is part of the rendering itself → describe freely |
| 380 | - Any case where rendering already implies a font character (e.g. `vintage-poster` implies period display lettering) → trust the rendering, no need to echo SVG body |
| 381 | |
| 382 | **When to use the table**: stable artistic lettering on a deck whose visual identity is grounded in the SVG body typography, and where a surprise font choice would feel out of place. |
| 383 | |
| 384 | **In-image text vs SVG text — decide by editability, not by model capability** |
| 385 | |
| 386 | Layer 1 text is rasterized into the artwork — once generated it cannot be edited, corrected, searched, restyled, or reflowed. That is the durable reason to choose where text lives, independent of any backend's rendering ability or the script / length involved: |
| 387 | |
| 388 | | Text | Layer | |
| 389 | |---|---| |
| 390 | | Part of the artwork and stable — decorative lettering, artistic wordmark, hand-lettered keyword, figure-internal identifiers (axis labels, panel letters, units) | Layer 1 (image) OK | |
| 391 | | Authoritative titles, page chrome, body copy, captions, data values — anything that must stay exact, searchable, editable, or may be reworded | Layer 2 (SVG) | |
| 392 | |
| 393 | Generation is non-deterministic on every backend, but **do not pre-judge by script or length** — never push text to SVG, shorten a headline, or downgrade `embedded` to `none` on the assumption that a particular script or a long string "won't render". Decide where text lives by the editability rule above, not by guessed rendering ability. Name the exact characters to bake literally in the prompt; do not re-read the generated image to verify them. |
| 394 | |
| 395 | **Prefer in-image**: text that is genuinely part of the artwork and will not be edited — a designed word, a stat lettering, a figure-internal label. |
| 396 | |
| 397 | **Push to SVG overlay instead**: page chrome, captions, data values, or any copy that must stay exact or editable. When the headline must remain editable, switch to **Primitive D (atmospheric backdrop)** and overlay it as SVG text. |
| 398 | |
| 399 | ### 5.4 No brand names or trademarks in the subject |
| 400 | |
| 401 | > The image must not depict identifiable brand logos, trademarks, or product likenesses unless the row's Reference explicitly names a real brand asset the user owns. |
| 402 | |
| 403 | --- |
| 404 | |
| 405 | ## 6. Manifest Schema |
| 406 | |
| 407 | Write `project/images/image_prompts.json` with this shape: |
| 408 | |
| 409 | ```json |
| 410 | { |
| 411 | "project": "{project_name}", |
| 412 | "generated_at": "{ISO-8601 date}", |
| 413 | "deck_rendering": "vector-illustration", |
| 414 | "color_scheme": { |
| 415 | "background": "#FFFFFF", |
| 416 | "secondary_bg": "#F8F9FA", |
| 417 | "primary": "#1E3A5F", |
| 418 | "accent": "#D4AF37", |
| 419 | "secondary_accent": "#4A7BB5", |
| 420 | "body_text": "#1D2430" |
| 421 | }, |
| 422 | "items": [ |
| 423 | { |
| 424 | "filename": "cover_bg.png", |
| 425 | "purpose": "Cover background (Slide 01)", |
| 426 | "page_role": "hero_page", |
| 427 | "text_policy": "none", |
| 428 | "aspect_ratio": "16:9", |
| 429 | "image_size": "2K", |
| 430 | "prompt": "{fully assembled paragraph per §4 — use §4.1 Primitive D for atmospheric cover}", |
| 431 | "alt_text": "Modern tech abstract background with deep blue gradient and digital waves", |
| 432 | "status": "Pending" |
| 433 | }, |
| 434 | { |
| 435 | "filename": "framework_p05.png", |
| 436 | "purpose": "Methodology framework (Slide 05)", |
| 437 | "type": "framework", |
| 438 | "page_role": "local", |
| 439 | "text_policy": "none", |
| 440 | "aspect_ratio": "4:3", |
| 441 | "image_size": "1K", |
| 442 | "prompt": "{fully assembled paragraph per §4}", |
| 443 | "status": "Pending" |
| 444 | } |
| 445 | ] |
| 446 | } |
| 447 | ``` |
| 448 | |
| 449 | ### Field reference |
| 450 | |
| 451 | | Field | Required | Source | Description | |
| 452 | |---|---|---|---| |
| 453 | | `deck_rendering` | yes | Step 2 active authority | Single rendering name shared by all items in this deck | |
| 454 | | `color_scheme` | yes | Step 2 active authority | Core deck color anchors shared by every item; prompts may add contextual tonal behavior, but no separate image palette | |
| 455 | | `items[].filename` | yes | Active resource authority | Output filename with extension | |
| 456 | | `items[].type` | no | Step 3 per-image | Optional one-of-11 internal-composition type for a local structural infographic when a template genuinely fits. Omit it for custom §4.1 E prose, `hero_page`, an Illustration Sheet, and local single-subject/portrait prose. | |
| 457 | | `items[].page_role` | yes | Step 3 per-image | `local` (default — region block on SVG page) or `hero_page` (image is page's main voice; SVG overlay minimal or empty) | |
| 458 | | `items[].text_policy` | yes | Step 3 per-image | `none` (image carries no text — explicit visual rule) or `embedded` (image contains stable artistic lettering, hand-lettered keywords, or visual identifiers like axis labels / subplot letters / unit symbols). AI judges per image; no global default bias — see §5.3. | |
| 459 | | `items[].aspect_ratio` | yes | Container sizing | Passed to `image_gen.py --aspect_ratio` | |
| 460 | | `items[].prompt` | yes | §4 assembly | The full assembled paragraph | |
| 461 | | `items[].image_size` | no | Container sizing | `512px` / `1K` / `2K` / `4K` | |
| 462 | | `items[].model` | no | Per-item execution override | Backend model for this item; otherwise the CLI/backend default wins | |
| 463 | | `items[].alt_text` | no | Accessibility | Short caption | |
| 464 | | `items[].slice_grid` | paired optional | §4.3 sheet geometry | Illustration sheet only; exact `RxC` grid to pass to `slice_images.py --grid`; requires `slice_names` | |
| 465 | | `items[].slice_names` | paired optional | §4.3 sheet geometry | Illustration sheet only; comma-separated safe PNG basenames to pass to `slice_images.py --names`; requires exactly `rows*cols` unique outputs | |
| 466 | | `items[].status` | yes | CLI manages | `Pending` initially; CLI updates to `Generated` / `Failed` / `Needs-Manual` | |
| 467 | |
| 468 | > **Back-compat for legacy `type` values**: existing manifests using `background` / `hero` / `portrait` / `typography` (the four removed pseudo-types) remain readable. Read them as: `background` → `page_role: hero_page` + no type; `hero` → `page_role: hero_page` + no type (use §4.1 Primitive A in prompt); `portrait` → `page_role: local` + no type (use §4.1 Primitive B); `typography` → `page_role: hero_page` + `text_policy: embedded` + no type (use §4.1 Primitive C). New manifests also omit `type` for custom §4.1 E prose, hero pages, and local single-subject/portrait prose. |
| 469 | > |
| 470 | > **Existing manifest compatibility**: |
| 471 | > |
| 472 | > - **Fixed compatibility defaults**: a missing `page_role` resolves to `local`; a missing `text_policy` resolves to `none`. Emit one aggregate legacy-compatibility warning per manifest. |
| 473 | > - **Declared replay procedure**: an existing manifest may lack `deck_rendering`, or an existing local item may lack `type`, because `items[].prompt` is already assembled. Leave that metadata absent, execute the existing prompt verbatim, and do not reconstruct either value. New manifests follow the field table; custom §4.1 E prose, hero pages, and local single-subject/portrait prose omit `type` intentionally. |
| 474 | > - A legacy non-empty `deck_style_anchor` string or object remains readable for replay and sidecar display but never overrides a current `deck_rendering`. |
| 475 | > - A legacy `deck_palette` field may remain but cannot override `color_scheme`. Read legacy `page_role: full_page` as `hero_page`. |
| 476 | |
| 477 | --- |
| 478 | |
| 479 | ## 7. Generation Execution |
| 480 | |
| 481 | > Prerequisite: §3 Steps 1-3 complete; `images/image_prompts.json` exists and validates. The manifest is the shared audit/source contract for all modes. It does **not** imply that `image_gen.py --manifest` should run; that command is Path A only. |
| 482 | |
| 483 | ### Path Selection (Deterministic) |
| 484 | |
| 485 | C (AI-generated) supports three implementation modes sharing one `image_prompts.json` source: |
| 486 | |
| 487 | | Trigger | Mode | Mechanism | |
| 488 | |---|---|---| |
| 489 | | **Default** — `IMAGE_BACKEND` configured | **Path A**: `image_gen.py --manifest` | One command runs the whole manifest with concurrency; status writes back per item | |
| 490 | | `IMAGE_BACKEND` not configured (or Path A fails) AND host has a native image tool | **Path B**: Host-native tool | Agent invokes the host's image capability; outputs land at `project/images/<filename>` | |
| 491 | | **Both Path A and Path B fail/unavailable** | **Offline Manual Mode** | Manifest stays on disk; user generates externally from `items[].prompt` and places files at `project/images/<filename>` | |
| 492 | |
| 493 | **Quick Generate selection**: an explicit user instruction for `api`, `host-native`, or `manual` retained in active context wins. When the user did not specify a path, select `auto` and run the A → B → C chain without asking or creating a planning artifact. |
| 494 | |
| 495 | **Default Generate selection — declared-procedure fallback when no path is confirmed**: the confirmed user choice wins. When neither channel confirmed a specific path, Generate Step 4 records the effective choice as `auto`; that explicit durable value uses the automatic A → B → C chain. A missing/blank/unknown project value is not an implicit API authorization: |
| 496 | |
| 497 | 0. **Confirmed override (wins)** — honor `AI Image Acquisition Path` from `design_spec.md §I`. Generate Step 4 already consumed the final confirmation into that durable artifact; do not reopen `result.json` here. If the recorded choice is set and not `auto`, honor it directly, **even when it contradicts `IMAGE_BACKEND`**: |
| 498 | - `api` → **Path A** (`image_gen.py --manifest`). |
| 499 | - `host-native` → **Path B** (host's native image tool) — skip A and do **not** run `image_gen.py --manifest`, *even if `IMAGE_BACKEND` is configured*. |
| 500 | - `manual` → **Offline Manual** (write prompts, render the Markdown sidecar, hand off; do **not** run `image_gen.py --manifest`). |
| 501 | If an explicitly chosen path is unavailable or still fails after its retry, mark the affected row `Needs-Manual`; do not switch to another automated provider. Only when the Design Spec records `auto` does the automatic chain decide. A legacy project missing this Design Spec row returns to Step 4 recovery to consume persisted confirmation once and record it; Image_Generator does not inspect the confirmation channel itself. |
| 502 | 1. **Try Path A** — if `IMAGE_BACKEND` is configured (env or `.env`), run `image_gen.py --manifest`. If it fails twice in a row, fall to Path B. |
| 503 | 2. **Try Path B** — if `IMAGE_BACKEND` was not configured (A skipped), or A failed, and the host has a native image tool (Codex / Antigravity / Claude Code / similar), the agent invokes the host's image capability directly. |
| 504 | 3. **Fall to C (Offline Manual)** — if B is also unavailable (no host-native tool) or fails, write prompts to `images/image_prompts.json` and hand off to the user. |
| 505 | |
| 506 | **Hard rule**: this step is execution, not re-decision. Default Generate uses the path locked in Strategist Step 4 h. Quick Generate uses the explicit active-context instruction or `auto`. Never present an interactive choice here. |
| 507 | |
| 508 | > All three modes share one output contract: file at `project/images/<filename>`. Step 6 SVG references are mode-agnostic. |
| 509 | |
| 510 | ### Path A — `image_gen.py --manifest` (Default) |
| 511 | |
| 512 | ```bash |
| 513 | python3 scripts/image_gen.py \ |
| 514 | --manifest project/images/image_prompts.json \ |
| 515 | --output project/images |
| 516 | ``` |
| 517 | |
| 518 | The CLI validates the file behind every `Generated` row before skipping it, iterates retryable rows with bounded adaptive concurrency, and atomically writes each status. A missing/corrupt generated file returns to `Failed`; persistent rate limits finish this run as retryable `Failed` instead of looping forever. |
| 519 | |
| 520 | **Parameters**: |
| 521 | |
| 522 | | Parameter | Short | Description | Default | |
| 523 | |---|---|---|---| |
| 524 | | `--manifest` | - | Path to `image_prompts.json` | — | |
| 525 | | `--concurrency` | - | Max concurrent requests; halves on rate-limit, min 1 | `IMAGE_CONCURRENCY` env or `3` | |
| 526 | | `--image_size` | - | Default size (`512px`/`1K`/`2K`/`4K`); per-item `image_size` wins | `1K` | |
| 527 | | `--output` | `-o` | Output directory | Manifest's parent dir | |
| 528 | | `--backend` | `-b` | Override `IMAGE_BACKEND` for this run | env | |
| 529 | | `--model` | `-m` | Default model; per-item `model` wins | Backend default | |
| 530 | | `--list-backends` | - | Print support tiers and exit | — | |
| 531 | |
| 532 | > The single-image form `image_gen.py "prompt" --filename ...` is preserved for ad-hoc one-offs (re-rolling a single image) but is no longer the primary path. |
| 533 | |
| 534 | **Configuration sources**: |
| 535 | - Current process environment variables |
| 536 | - First `.env` found in this order: current working directory, skill directory (e.g. `~/.agents/skills/ppt-master/.env`), clone repo root, `~/.ppt-master/.env` |
| 537 | |
| 538 | Precedence: |
| 539 | - Current process environment wins |
| 540 | - `.env` fills missing values only |
| 541 | |
| 542 | | Variable | Required | Description | |
| 543 | |----------|----------|-------------| |
| 544 | | `IMAGE_BACKEND` | Required | Backend identifier; run `image_gen.py --list-backends` for the current set | |
| 545 | | `IMAGE_CONCURRENCY` | Optional | Manifest-mode default concurrency (CLI `--concurrency` wins) | |
| 546 | | `{PROVIDER}_API_KEY` | Required | Provider-specific API key, e.g. `GEMINI_API_KEY`, `ZHIPU_API_KEY` | |
| 547 | | `{PROVIDER}_BASE_URL` | Optional | Provider-specific custom endpoint | |
| 548 | | `{PROVIDER}_MODEL` | Optional | Provider-specific model override | |
| 549 | | `OPENAI_SIZE_PRESET` | Optional | OpenAI-compatible size mapping: `auto`, `legacy`, `gpt-image`, `gpt-image-2`, `dall-e-2` | |
| 550 | | `OPENAI_RESPONSE_FORMAT` | Optional | OpenAI-compatible response field: `auto`, `b64_json`, `url`, `omit` | |
| 551 | | `OPENAI_QUALITY` | Optional | OpenAI-compatible quality field: `auto`, `omit`, `low`, `medium`, `high`, `standard`, `hd` | |
| 552 | |
| 553 | > Use provider-specific names only (e.g. `GEMINI_API_KEY`, `OPENAI_API_KEY`). See `.env.example` in clone mode or `${SKILL_DIR}/.env.example` in skill-install mode for the full set per backend. |
| 554 | |
| 555 | > Note: OpenAI-compatible platforms that reject OpenAI-specific fields stay under `IMAGE_BACKEND=openai`; configure the `OPENAI_*` compatibility knobs instead of adding a provider-specific backend. |
| 556 | |
| 557 | > `IMAGE_API_KEY`, `IMAGE_MODEL`, and `IMAGE_BASE_URL` are intentionally unsupported. |
| 558 | |
| 559 | > If `.env` or the current environment contains multiple provider configs, `IMAGE_BACKEND` explicitly selects the active one. |
| 560 | |
| 561 | **Support tiers (recommended usage)**: Core / Extended / Experimental. Run `image_gen.py --list-backends` for the current assignments. |
| 562 | |
| 563 | **Concurrency (manifest mode)**: |
| 564 | - Default 3 concurrent requests, halves on the first rate-limit response, minimum 1 (= serial fallback) |
| 565 | - Rate-limited items requeue automatically; per-item failures are recorded with `last_error` and skipped |
| 566 | - Interrupting mid-run is safe — completed items keep `status: Generated` and are skipped on re-run |
| 567 | - On normal completion the Markdown sidecar is re-rendered automatically; if the run is interrupted, run `--render-md` manually to refresh the sidecar |
| 568 | |
| 569 | ### Path B — Host-Native Image Tool |
| 570 | |
| 571 | Triggered automatically when `IMAGE_BACKEND` is not configured (or Path A fails) **and** the host provides a native image generation tool (Codex, Antigravity, Claude Code's image tool, and similar). No user prompting required — the agent detects the host capability and proceeds. The user may also explicitly name this path ("use Codex's image tool") to force it even when `IMAGE_BACKEND` is configured. |
| 572 | |
| 573 | - Agent invokes the host's native image tool directly; prompts come from `items[].prompt` |
| 574 | - Do **not** run `image_gen.py --manifest` in Path B. That command is Path A and may use configured API/proxy backends even when the user confirmed host-native. |
| 575 | - Still run `python3 scripts/image_gen.py --render-md project/images/image_prompts.json` so the human-readable sidecar exists without touching any backend. |
| 576 | - **Batch for speed, mind the rate**: when the host can run independent tool calls in parallel (e.g. Claude Code issues independent calls concurrently), fire several generations together in modest groups — a few rows at a time (~3–4), not the whole manifest at once — so their latency overlaps without flooding the host's image quota. When the host only runs tools serially, generate one row at a time. This mirrors Path A's default concurrency of 3. |
| 577 | - Outputs **must** land at `project/images/<filename-from-resource-list>`. Match the Image Resource List dimensions when the host supports arbitrary sizes. Hosts with **fixed native resolutions** (common — e.g. ~1672x941 landscape / ~1086x1448 portrait) generate at the closest native size and backfill the actual pixels into the resource list `Dimensions` column — same convention as formula rows ("actual dimensions from formula manifest") and slice rows ("dimensions filled after slicing"). Do **not** upscale the file to fake the requested size (interpolation adds no detail); minor display-side upscaling (up to ~1.3x in practice) may surface as a non-blocking quality-checker warning and requires no acknowledgement. |
| 578 | - Mark each item's `status` `Generated` in the manifest the moment its file lands — as each completes, not in one pass at the end (so an interrupted batch leaves accurate state) |
| 579 | - Executor downstream is path-agnostic — no spec change required between Path A and Path B |
| 580 | |
| 581 | ### Offline Manual Mode (C's third implementation mode) |
| 582 | |
| 583 | **Trigger**: the automatic chain reaches this point after both Path A and Path B fail or are unavailable, the user explicitly confirmed `manual`, or an explicitly confirmed automated path still fails after its own retry. |
| 584 | |
| 585 | **Workflow** (no user prompting; system enters this mode automatically): |
| 586 | |
| 587 | 1. Verify `images/image_prompts.json` was written |
| 588 | 2. Set `status: "Needs-Manual"` on every affected item per [`image-base.md`](./image-base.md) §6 |
| 589 | 3. Apply the mode boundary: |
| 590 | - Default Generate: continue to Step 6; Executor draws a dashed placeholder and Step 7 verifies the supplied file |
| 591 | - Quick Generate: retain the prompt and `Needs-Manual` status, and block direct export until every required supplied file is validated and its row is reconciled to `Generated` |
| 592 | 4. Print one consolidated handoff to the user: |
| 593 | - Filenames awaiting manual generation |
| 594 | - Pointer to `images/image_prompts.md` (paste-ready `### Image N:` block per item) or `image_prompts.json` (`items[].prompt`) |
| 595 | - Target placement: `project/images/<filename>` matching the resource list exactly |
| 596 | - Continuation: Default Generate re-runs Step 7; Quick may validate the supplied file, rerun its resource gate and final checker, then use `--quick-generate` only while the original active context remains available — otherwise start a clean Quick run |
| 597 | |
| 598 | **User-initiated**: When Strategist Step 4 captured `manual` in Default Generate, or the user explicitly requested `manual` in the Quick Generate active context, Path A is skipped from the start. |
| 599 | |
| 600 | > Default Generate tolerates `Needs-Manual` rows through authoring and resumes |
| 601 | > at Step 7. Quick Generate preserves the same operational manifest and handoff |
| 602 | > but does not run `--quick-generate` while a required row still says |
| 603 | > `Needs-Manual`. If the original active context remains available, validate a |
| 604 | > later supplied file and update it to `Generated`; otherwise start a clean |
| 605 | > Quick run rather than treating the manifest as a resumable design record. |
| 606 | |
| 607 | #### AI-specific Failure Handling (extends image-base.md §6) |
| 608 | |
| 609 | When the path is `auto` and Path A's backend fails twice in a row: |
| 610 | |
| 611 | 1. Do not halt. Automatically attempt to fall back to **Path B (Host-Native Tool)**. |
| 612 | 2. If Path B also fails or is unavailable, mark the row `Needs-Manual`. |
| 613 | 3. Report to user: filename, prompt used, error message. |
| 614 | 4. Fall through to **Offline Manual Mode** above. |
| 615 | |
| 616 | When `api` or `host-native` was explicitly confirmed, failure or unavailability does not authorize an automated provider switch. Retry the confirmed path once; if it still fails, mark the row `Needs-Manual`, report the filename/prompt/error, and use the manual handoff above. |
| 617 | |
| 618 | > If the alternate platform watermarks outputs (e.g. Gemini web), the repository includes `scripts/gemini_watermark_remover.py`. |
| 619 | |
| 620 | #### Guardrails (All Modes) |
| 621 | |
| 622 | **Hard rule**: |
| 623 | |
| 624 | - Do not claim an image is generated without an actual file at the expected path |
| 625 | - `Needs-Manual` is set only when `manual` was confirmed or the selected automated recovery path was attempted and failed — not as a way to skip work that automation could have done |
| 626 | - Status transitions are evidence-driven: a file at the expected path permits `Generated`; an exhausted recovery path permits `Needs-Manual` |
| 627 | |
| 628 | --- |
| 629 | |
| 630 | ## 8. Common Issues & Variant Workflow |
| 631 | |
| 632 | ### Reference field is omitted or blank — declared-inference fallback for existing AI rows |
| 633 | |
| 634 | When an existing AI Resource List row omits `Reference` or contains a blank `Reference`, infer a reasonable image from its non-empty `Purpose`. If `Purpose` is also omitted or blank, stop and repair the row. Examples (not prescriptions): |
| 635 | |
| 636 | | Purpose | A reasonable starting point | |
| 637 | |---------|-----------------------------| |
| 638 | | Cover | `page_role: hero_page` + §4.1 Primitive A (single-subject) or D (atmospheric); choose `text_policy` by what the cover should communicate | |
| 639 | | Chapter divider | `page_role: hero_page` + Primitive D (atmospheric) or A (single-subject); keep the authoritative chapter title in SVG, with `embedded` reserved for separate stable artistic lettering | |
| 640 | | Methodology / framework illustration | `type: framework`, `page_role: local` | |
| 641 | | Process / workflow illustration | `type: flowchart`, `page_role: local` | |
| 642 | | Before/After or two-option page | `type: comparison`, `page_role: local` | |
| 643 | | Team / lifestyle photo (group) | `type: scene`, `page_role: local`; rendering = `corporate-photo` or `warm-scene` | |
| 644 | | Single-person headshot / bio | `page_role: local` + §4.1 Primitive B (portrait); rendering = `corporate-photo` for photo realism | |
| 645 | | Big-number / hero quote block | `page_role: hero_page` + §4.1 Primitive C (typographic); `text_policy: embedded` | |
| 646 | | Mood transition / atmosphere | `page_role: hero_page` + Primitive D (atmospheric), or `type: scene` if narrative | |
| 647 | |
| 648 | ### When Images Are Unsatisfactory |
| 649 | |
| 650 | Diagnose the failure category, adjust the **one specific dimension** responsible, do not rewrite the whole prompt. |
| 651 | |
| 652 | | Symptom | Most likely cause | Adjustment | |
| 653 | |---|---|---| |
| 654 | | Image looks generic, model-average | Tag-soup prompt | Rewrite as one coherent paragraph per §4 | |
| 655 | | Wrong style family (looks photorealistic when flat was intended) | Rendering mismatch or rendering paragraph diluted | Reaffirm chosen rendering's style paragraph at the top of the prompt | |
| 656 | | Colors don't match deck | Core role anchors or their semantic/proportion instructions were diluted | Restate which deck roles own the field, main forms, and sparse accents; remove unrelated hues while preserving context-justified tonal transitions | |
| 657 | | Hex code or color name visible as text in image | Missing §5.1 closing sentence | Append the §5.1 hard rule verbatim | |
| 658 | | Garbled letters in supposedly text-free image | `text_policy: none` rule too weak | Strengthen with explicit list: "no letters, no numbers, no words, no signs, no labels, no captions, no watermarks" | |
| 659 | | SVG text overlay clashes with busy image area | Page design needs negative space the prompt didn't request | Add a composition cue like "leave the {center / left third / lower band} relatively calm for text overlay" — only when the page actually overlays text on top of the image | |
| 660 | | Subject vague | Reference field too abstract | Rewrite reference with concrete nouns (verbs + objects) | |
| 661 | | Human depiction conflicts with the selected style or intent | §5.2 rendering/Reference cues were diluted | Restate the selected rendering's facial detail, anatomy, texture, and realism cues without changing the locked rendering | |
| 662 | |
| 663 | **Variant workflow**: |
| 664 | |
| 665 | 1. Set the unsatisfactory item's `status` back to `Pending` and update its `prompt` in place |
| 666 | 2. Re-run the same resolved path used for the original item: Path A may re-run `image_gen.py --manifest` (only that item is re-processed); Path B uses the host-native tool again for that item; Offline Manual re-renders the sidecar and hands off |
| 667 | 3. To try multiple stylistic approaches, append additional items with distinct filenames (e.g. `cover_bg_v2.png`) rather than overwriting |
| 668 | |
| 669 | --- |
| 670 | |
| 671 | ## 9. Forbidden |
| 672 | |
| 673 | - Generating prompts for `web` rows — those go through [`image-searcher.md`](./image-searcher.md) |
| 674 | - Brand names or HEX codes inside the subject description (degrades output) |
| 675 | - Mixing renderings or introducing an unrelated image-only palette across images in the same deck |
| 676 | - Tag-soup prompts (keyword lists separated by commas without a coherent visual scene) |
| 677 | - Globbing `image-renderings/*.md` or any subdirectory — read only the chosen preset or exact custom-reference files |
| 678 | - Placing an image without updating its `image_prompts.json` `status` and the active resource authority's status |
| 679 | - Switching rendering or core deck-color semantics for a single image—`hero_page` is not an exception to deck-wide coherence |
| 680 | - Embedding body copy, data points, bullet lists, or long quotes inside an image — those route to SVG |
| 681 |