Sync third-party and MCP marketplace plugins

Constraint: Public skills are published only by explicit administrator action unless they are tracked third-party market sources.
Confidence: high
Scope-risk: narrow
Directive: Keep private/internal skills out of the public marketplace and preserve normal incremental market Git history.
Tested: Marketplace validation passed.
This commit is contained in:
KeyInfo Bot
2026-08-17 00:02:14 +08:00
parent 5729c062ed
commit a73a6270a7
45 changed files with 635 additions and 393 deletions
+6 -6
View File
@@ -96,8 +96,8 @@
"repo": "https://github.com/hugohe3/ppt-master.git", "repo": "https://github.com/hugohe3/ppt-master.git",
"ref": "main", "ref": "main",
"adapter": "claude-skill", "adapter": "claude-skill",
"commit": "090a133040d9bf41dca887dab78386c553af5dc6", "commit": "53c9c2a5e9f1a49096324fba4f95833649c6a0f4",
"syncedAt": "2026-08-15T16:00:01Z" "syncedAt": "2026-08-16T16:00:01Z"
}, },
{ {
"id": "grill-me", "id": "grill-me",
@@ -105,8 +105,8 @@
"repo": "https://github.com/mattpocock/skills.git", "repo": "https://github.com/mattpocock/skills.git",
"ref": "main", "ref": "main",
"adapter": "skill-collection", "adapter": "skill-collection",
"commit": "8b78b531ab965735c5dc74f6f7a219e1e37326df", "commit": "068b6e0c62393147daf03530149cdce209c93da8",
"syncedAt": "2026-08-13T15:59:59Z" "syncedAt": "2026-08-16T16:00:01Z"
}, },
{ {
"id": "next-skills", "id": "next-skills",
@@ -114,8 +114,8 @@
"repo": "https://github.com/vercel/next.js.git", "repo": "https://github.com/vercel/next.js.git",
"ref": "canary", "ref": "canary",
"adapter": "skill-collection", "adapter": "skill-collection",
"commit": "6d0379d4b8558d1157dee78906f0850432453a21", "commit": "c18acf5cef6085bdf7561352be4a06acfc43b6fc",
"syncedAt": "2026-08-15T16:00:01Z" "syncedAt": "2026-08-16T16:00:01Z"
} }
] ]
} }
@@ -2,8 +2,8 @@
"sourceId": "grill-me", "sourceId": "grill-me",
"repo": "https://github.com/mattpocock/skills.git", "repo": "https://github.com/mattpocock/skills.git",
"ref": "main", "ref": "main",
"commit": "8b78b531ab965735c5dc74f6f7a219e1e37326df", "commit": "068b6e0c62393147daf03530149cdce209c93da8",
"adapter": "skill-collection", "adapter": "skill-collection",
"sourcePath": "skills/productivity", "sourcePath": "skills/productivity",
"syncedAt": "2026-08-13T15:59:59Z" "syncedAt": "2026-08-16T16:00:01Z"
} }
@@ -1,7 +1,6 @@
--- ---
name: grill-me name: grill-me
description: "在用户明确要求严格追问、压力测试或彻底澄清计划、设计、决策或想法时使用。" description: "在用户明确要求严格追问、压力测试或彻底澄清计划、设计、决策或想法时使用。"
disable-model-invocation: true
--- ---
Run a `/grilling` session. Call the Skill tool with "grilling".
@@ -5,7 +5,7 @@ description: "在用户希望通过分轮问题压力测试计划、决策或想
Interview the user relentlessly until you reach a shared understanding. Map this as a **design tree**: every decision branches into the decisions that hang off it. Interview the user relentlessly until you reach a shared understanding. Map this as a **design tree**: every decision branches into the decisions that hang off it.
Work the tree in **rounds**. The **frontier** is every decision whose prerequisites are already settled the questions you can ask _now_ without guessing at answers you haven't heard yet. Ask the whole frontier in one round: number each question and give your recommended answer. Then wait for the user's answers before the next round. Work the tree in **rounds**. The **frontier** is every decision whose prerequisites are already settled: the questions you can ask _now_ without guessing at answers you haven't heard yet. Ask the whole frontier in one round: number each question and give your recommended answer. Then wait for the user's answers before the next round.
Each question should be formatted like so: Each question should be formatted like so:
@@ -15,8 +15,8 @@ Each question should be formatted like so:
➡️ <your recommended answer> ➡️ <your recommended answer>
``` ```
Each round the user answers reshapes the tree settled decisions push the frontier outward and unblock questions that depended on them. Recompute the frontier and ask the next round. A question whose answer depends on another question still open in this round belongs to a _later_ round, not this one. Each round the user answers reshapes the tree: settled decisions push the frontier outward and unblock questions that depended on them. Recompute the frontier and ask the next round. A question whose answer depends on another question still open in this round belongs to a _later_ round, not this one.
Finding _facts_ is your job, never the user's. When a frontier question needs a fact from the environment (filesystem, tools, etc.), dispatch a sub-agent to find it don't ask the user for anything you could look up yourself. Don't block on it: a running exploration is an unsettled prerequisite, so only the questions downstream of it wait for the sub-agent to report ask the rest of the frontier now. The _decisions_ are the user's put each to them and wait. Finding _facts_ is your job, never the user's. When a frontier question needs a fact from the environment (filesystem, tools, etc.), dispatch a sub-agent to find it; don't ask the user for anything you could look up yourself. Don't block on it: a running exploration is an unsettled prerequisite, so only the questions downstream of it wait for the sub-agent to report; ask the rest of the frontier now. The _decisions_ are the user's: put each to them and wait.
The session is done when the frontier is empty: every branch of the design tree visited, nothing left silently assumed. Do not act on it until the user confirms you have reached a shared understanding. The session is done when the frontier is empty: every branch of the design tree visited, nothing left silently assumed. Do not act on it until the user confirms you have reached a shared understanding.
@@ -3,5 +3,5 @@
"name": "playwright浏览器自动化操作", "name": "playwright浏览器自动化操作",
"version": "20260605", "version": "20260605",
"keySource": "none", "keySource": "none",
"syncedAt": "2026-08-15T16:02:13Z" "syncedAt": "2026-08-16T16:02:13Z"
} }
@@ -2,8 +2,8 @@
"sourceId": "next-skills", "sourceId": "next-skills",
"repo": "https://github.com/vercel/next.js.git", "repo": "https://github.com/vercel/next.js.git",
"ref": "canary", "ref": "canary",
"commit": "6d0379d4b8558d1157dee78906f0850432453a21", "commit": "c18acf5cef6085bdf7561352be4a06acfc43b6fc",
"adapter": "skill-collection", "adapter": "skill-collection",
"sourcePath": "skills", "sourcePath": "skills",
"syncedAt": "2026-08-15T16:00:01Z" "syncedAt": "2026-08-16T16:00:01Z"
} }
@@ -2,8 +2,8 @@
"sourceId": "ppt-master", "sourceId": "ppt-master",
"repo": "https://github.com/hugohe3/ppt-master.git", "repo": "https://github.com/hugohe3/ppt-master.git",
"ref": "main", "ref": "main",
"commit": "090a133040d9bf41dca887dab78386c553af5dc6", "commit": "53c9c2a5e9f1a49096324fba4f95833649c6a0f4",
"adapter": "claude-skill", "adapter": "claude-skill",
"sourcePath": "skills/ppt-master", "sourcePath": "skills/ppt-master",
"syncedAt": "2026-08-15T16:00:01Z" "syncedAt": "2026-08-16T16:00:01Z"
} }
@@ -2,7 +2,7 @@
name: ppt-master name: ppt-master
description: "多格式源文档到高质量 SVG 页面再导出 PPTX 的多阶段演示文稿生成工作流。" description: "多格式源文档到高质量 SVG 页面再导出 PPTX 的多阶段演示文稿生成工作流。"
metadata: metadata:
version: "4.7.0" version: "4.8.0"
copyright: "Copyright (c) 2025-2026 Hugo He" copyright: "Copyright (c) 2025-2026 Hugo He"
license: "MIT" license: "MIT"
official_repository: "https://github.com/hugohe3/ppt-master" official_repository: "https://github.com/hugohe3/ppt-master"
@@ -54,7 +54,7 @@ never compete with it.
5. **No speculative execution** — Do not prepare later-phase artifacts before their owning step. 5. **No speculative execution** — Do not prepare later-phase artifacts before their owning step.
6. **Deterministic routing** — Do not add a route-choice question when [`routing.md`](workflows/routing.md) resolves the request. If a route prerequisite is missing, state it and stop that route. 6. **Deterministic routing** — Do not add a route-choice question when [`routing.md`](workflows/routing.md) resolves the request. If a route prerequisite is missing, state it and stop that route.
7. **Owning-source recovery** — On failure, repair or regenerate the owning source artifact and resume from the route's declared pointer. Do not silently downgrade a required artifact. 7. **Owning-source recovery** — On failure, repair or regenerate the owning source artifact and resume from the route's declared pointer. Do not silently downgrade a required artifact.
8. **Stable paths** — Use absolute skill/project paths; never derive them from CWD. 8. **Stable paths** — Use absolute skill/project paths; never derive them from CWD. The Skill root is the directory containing `SKILL.md`; the host supplies its path. If it cannot be determined, ask the user — never guess it via file search.
## Global Communication Rules ## Global Communication Rules
@@ -115,7 +115,7 @@ Apply the content-vs-expression contract above within the selected reading mode.
- **Core message ≥ `body`**: map the page's primary claim to declared `lead` / `subtitle`, never below the current body treatment. Footnotes, page numbers, and credits use declared `footnote` / `annotation`; do not invent a smaller role. - **Core message ≥ `body`**: map the page's primary claim to declared `lead` / `subtitle`, never below the current body treatment. Footnotes, page numbers, and credits use declared `footnote` / `annotation`; do not invent a smaller role.
- **Write unitless px, with at most two decimals.** Structural and mapped-role text uses only its anchor or a value within its `±2`px band; the sparse display-size exception is defined separately below. Do not substitute familiar pt-style numbers or emit long precision tails. - **Write unitless px, with at most two decimals.** Structural and mapped-role text uses only its anchor or a value within its `±2`px band; the sparse display-size exception is defined separately below. Do not substitute familiar pt-style numbers or emit long precision tails.
- **Sparse display-size exception**: a short non-structural Hero/Display element may use one undeclared size outside all anchor bands at most twice across the deck without a lock row. The third occurrence makes that size recurring: stop and return to Strategist to name the role in the Design Spec and `spec_lock.md`, then read back and validate the affected fragments before reuse. This exception never applies to titles, body copy, subtitles, annotations, footnotes, captions, data labels, or card copy, and nearby sizes must not be introduced to imitate one recurring treatment. - **Sparse display-size exception**: a short non-structural Hero/Display element may use one undeclared size outside all anchor bands at most twice across the deck without a lock row. The third occurrence makes that size recurring: stop and return to Strategist to name the role in the Design Spec and `spec_lock.md`, then read back and validate the affected fragments before reuse. This exception never applies to titles, body copy, subtitles, annotations, footnotes, captions, data labels, or card copy, and nearby sizes must not be introduced to imitate one recurring treatment.
- **Prepared decorative lettering**: When the approved plan selects stable artistic lettering as part of the visual, place its prepared AI/slice file as an image asset and keep the ordinary editable title/subtitle in separate native text frames. Do not recreate the asset with layered glyph copies or native WordArt; when the plan selects the text fallback instead, use ordinary `<text>` without inventing a missing image. - **Prepared decorative lettering**: When the approved plan selects stable artistic lettering as part of the visual, place its prepared AI/slice file as an image asset and keep the ordinary editable title/subtitle in separate native text frames. Do not recreate the asset with layered glyph copies or native WordArt; when the plan keeps that wording as native text and prepares no lettering asset, use ordinary `<text>` without inventing a missing image.
- **Outside-band recovery**: for structural text, reflow geometry and use the declared role band locally. For a sparse display occurrence, keep the unitless value and verify that its deck-wide count remains at most two. Never flatten a justified distinction or add a role merely to silence the checker. Mirror pages preserve exact source typography as inherited input. - **Outside-band recovery**: for structural text, reflow geometry and use the declared role band locally. For a sparse display occurrence, keep the unitless value and verify that its deck-wide count remains at most two. Never flatten a justified distinction or add a role merely to silence the checker. Mirror pages preserve exact source typography as inherited input.
- Images MUST reference files listed under `images`; no invented filenames - Images MUST reference files listed under `images`; no invented filenames
- For math, load [`native-formula.md`](./native-formula.md): simple notation stays text; one-line structural prose uses inline; matrices, multiline derivations, or standalone high structure use block. Keep exact LaTeX plus preview; never use an image. - For math, load [`native-formula.md`](./native-formula.md): simple notation stays text; one-line structural prose uses inline; matrices, multiline derivations, or standalone high structure use block. Keep exact LaTeX plus preview; never use an image.
@@ -162,8 +162,9 @@ Before drawing each page, look up its entry in `page_rhythm` (key format `P<NN>`
- **Main-agent ownership**: SVG generation must run in the main agent (not sub-agents) — pages share upstream context for cross-page visual continuity - **Main-agent ownership**: SVG generation must run in the main agent (not sub-agents) — pages share upstream context for cross-page visual continuity
- **Generation rhythm**: P01 → first-page gate → uninterrupted remaining pages → final gate, in one context without batches or mid-run checker calls. - **Generation rhythm**: P01 → first-page gate → uninterrupted remaining pages → final gate, in one context without batches or mid-run checker calls.
- **Fact provenance**: when a §IX page lists `Fact IDs`, resolve each ID from `sources/*.facts.json` and keep the claim/value unchanged. Render a compact source footnote using the source name and a short URL/domain when space permits; when speaker notes are enabled, state the attribution naturally there too. When §IX says `Data class: scenario`, place a visible localized `Scenario data` / `情景数据` label adjacent to the affected KPI/chart and, when notes are enabled, state naturally there that the number is illustrative. Never attach an external fact ID to scenario data or let an unlabeled invented KPI look factual. - **Fact provenance**: when a §IX page lists `Fact IDs`, resolve each ID from `sources/*.facts.json` and keep the claim/value unchanged. Render a compact source footnote using the source name and a short URL/domain when space permits; when speaker notes are enabled, state the attribution naturally there too. When §IX says `Data class: scenario`, place a visible localized `Scenario data` / `情景数据` label adjacent to the affected KPI/chart and, when notes are enabled, state naturally there that the number is illustrative. Never attach an external fact ID to scenario data or let an unlabeled invented KPI look factual.
- **Default — compose compatible carriers by page job (may use any suitable subset)**: background paint, native shapes, editable text, prepared photos/scenes, transparent illustration slices, decorative lettering, icons, and visualizations may share one page. Use only prepared resources; choose their combination, visual weight, and z-order from the page message, hierarchy, and resolved style rather than isolating each carrier in its own block.
- **Default — stage each page with the style's composition geometry (may override when the content genuinely calls for a plain grid)**: an SVG page is a canvas, not a DOM. Resolve the page-scale move from `spec_lock.md`: a preset uses that selected style's §1 `Composition geometry`; `custom` executes `visual_style_behavior` first, then uses §1 geometry only from exact `visual_style_references` that the behavior assigns a shape or composition job. Other bases contribute only their assigned job, and an unreferenced novel custom follows its behavior alone. Before defaulting to stacked rounded-rect cards or uniform equal columns, use that resolved geometry to stage the page's primary zone. Card grids are one option among many, not the house layout. - **Default — stage each page with the style's composition geometry (may override when the content genuinely calls for a plain grid)**: an SVG page is a canvas, not a DOM. Resolve the page-scale move from `spec_lock.md`: a preset uses that selected style's §1 `Composition geometry`; `custom` executes `visual_style_behavior` first, then uses §1 geometry only from exact `visual_style_references` that the behavior assigns a shape or composition job. Other bases contribute only their assigned job, and an unreferenced novel custom follows its behavior alone. Before defaulting to stacked rounded-rect cards or uniform equal columns, use that resolved geometry to stage the page's primary zone. Card grids are one option among many, not the house layout.
- **Default — vary a planned deck motif instead of cloning it (may omit where it has no page job)**: when §III `Theme` names a cross-page motif, use the current §IX `Layout` to preserve its recognizable contour, direction, material, or relationship while varying scale, crop, density, position, and content interaction by page role. Apply it only where it supports hierarchy or continuity; do not paste identical ornament or invent a second recurring identity. - **Default — realize the planned motif system's reuse mode (may omit where it has no page job)**: when §III `Theme` names a cross-page motif or element family, exact repetition is valid for deliberate title/corner chrome; adaptive elements may vary scale, crop, density, position, and content interaction by page role. Preserve the named invariant, apply it only where it supports hierarchy or continuity, and do not invent a competing recurring identity.
- **Inherited containers**: preserve meaningful template frames; restyle radius, fill, stroke, and depth from the active Design Spec and `spec_lock.md`. Selected Chart/Table reference adaptation is owned by [`executor-visualization.md`](./executor-visualization.md); preview effects never override project styling or structural roles. - **Inherited containers**: preserve meaningful template frames; restyle radius, fill, stroke, and depth from the active Design Spec and `spec_lock.md`. Selected Chart/Table reference adaptation is owned by [`executor-visualization.md`](./executor-visualization.md); preview effects never override project styling or structural roles.
- **Reference — prefer semantic geometry over preset stacks**: for relationships such as ascending, converging, breaking through, or stacking, first seek a basic primitive, one exact preset, or a clear Boolean result. Only when none can faithfully express the relationship should one page-specific polygon/path replace a stack of generic arrows. - **Reference — prefer semantic geometry over preset stacks**: for relationships such as ascending, converging, breaking through, or stacking, first seek a basic primitive, one exact preset, or a clear Boolean result. Only when none can faithfully express the relationship should one page-specific polygon/path replace a stack of generic arrows.
- **Reference — create depth with restraint**: use rhythm, spacing, typography, accent bars, and subtle tints before shadows. Reserve lift for a few genuinely floating elements; keep peer grids, dividers, and ordinary body containers flat. When material layering itself is part of the resolved visual style, follow that style's hierarchy instead of flattening its body planes. - **Reference — create depth with restraint**: use rhythm, spacing, typography, accent bars, and subtle tints before shadows. Reserve lift for a few genuinely floating elements; keep peer grids, dividers, and ordinary body containers flat. When material layering itself is part of the resolved visual style, follow that style's hierarchy instead of flattening its body planes.
@@ -49,6 +49,7 @@ Choosing `none` is valid. Never bake a native treatment into a derivative.
| `A1-01` / `A1-04` | Existing prepared composite or host/AI path; `image_treat.py` does not blend | | `A1-01` / `A1-04` | Existing prepared composite or host/AI path; `image_treat.py` does not blend |
| `A2-01` | Existing/host-prepared RGBA or flat-key AI/slice asset; when source-scene registration is required, use `A2-02` / `A2-03` + [`image-generator.md`](./image-generator.md) §4.4 | | `A2-01` | Existing/host-prepared RGBA or flat-key AI/slice asset; when source-scene registration is required, use `A2-02` / `A2-03` + [`image-generator.md`](./image-generator.md) §4.4 |
| `A2-02` / `A2-03` | [`image-generator.md`](./image-generator.md) §4.4 registered layers | | `A2-02` / `A2-03` | [`image-generator.md`](./image-generator.md) §4.4 registered layers |
| `A2-04` | Existing/host-prepared transparent frame or device asset via the `A2-01` paths, plus an existing content picture registered beneath it; no automatic derivative |
| `A3-01` | Original/subject plus registered `image_treat.py` blur/tone/desaturate derivative | | `A3-01` | Original/subject plus registered `image_treat.py` blur/tone/desaturate derivative |
| `A3-02` | Registered full-canvas `image_treat.py` blur derivative; crop panels natively | | `A3-02` | Registered full-canvas `image_treat.py` blur derivative; crop panels natively |
| `A3-03` | `image_treat.py` desaturated base plus existing/§4.4 color subject layer | | `A3-03` | `image_treat.py` desaturated base plus existing/§4.4 color subject layer |
@@ -90,7 +91,7 @@ After all rows reach terminal status:
- Each derivative has its distinct file and usable parent; web provenance is copied in `image_sources.json` - Each derivative has its distinct file and usable parent; web provenance is copied in `image_sources.json`
- Every `slice` row has a generated element file, or is marked `Needs-Manual` because its parent sheet is not available - Every `slice` row has a generated element file, or is marked `Needs-Manual` because its parent sheet is not available
- No `Pending`, `Failed`, or `Needs-Selection` rows remain - No `Pending`, `Failed`, or `Needs-Selection` rows remain
- `image_prompts.json` exists when ≥1 ai row processed; every entry has `status ∈ {Generated, Needs-Manual}` (no `Pending` or `Failed` remaining) - `image_prompts.json` exists when ≥1 active ai row remains; every entry has `status ∈ {Generated, Needs-Manual}` (no `Pending` or `Failed` remaining)
- `image_sources.json` exists when ≥1 web row processed; every entry has `license_tier ∈ {no-attribution, attribution-required, manual}` (`manual` = a user-supplied `--from-url` replacement) - `image_sources.json` exists when ≥1 web row processed; every entry has `license_tier ∈ {no-attribution, attribution-required, manual}` (`manual` = a user-supplied `--from-url` replacement)
> `Needs-Manual` is terminal for acquisition, not export readiness. A later > `Needs-Manual` is terminal for acquisition, not export readiness. A later
@@ -105,11 +106,11 @@ After all rows reach terminal status:
## 6. Failure Handling ## 6. Failure Handling
**Hard rule — automatic exhaustion before blocking**: acquisition failures MUST NOT open an interactive choice or stop while an untried permitted strategy remains. **Hard rule — automatic exhaustion before blocking**: acquisition failures MUST NOT open an interactive choice or stop while an untried permitted strategy remains. After exhaustion, follow the owning path's decision policy; Default AI generation uses `image-generator.md` §7's three-outcome recovery gate instead of assuming manual fulfillment.
1. Run the selected path's initial strategy 1. Run the selected path's initial strategy
2. On recoverable failure (network, no candidates, license rejection, rate limit), continue through materially different strategies that remain inside that path's confirmed permissions; never loop an already exhausted strategy 2. On recoverable failure (network, no candidates, license rejection, rate limit), continue through materially different strategies that remain inside that path's confirmed permissions; never loop an already exhausted strategy
3. When the path-specific query variants/ranked pages/provider/license-stage or backend/retry strategy is exhausted, set `Status: Needs-Manual`, log the reason in conversation, and continue 3. When the path-specific query variants/ranked pages/provider/license-stage or backend/retry strategy is exhausted, follow its owning terminal rule. Web may set `Status: Needs-Manual`; Default AI rows remain `Failed` while its recovery decision or retry is unresolved, and only confirmed manual fulfillment sets `Needs-Manual`; Quick removes exhausted automated AI/dependent-slice jobs through `image-generator.md` §7's declared no-AI replan, while an explicitly selected manual path may set `Needs-Manual`
4. After the phase completes, summarize all `Needs-Manual` rows for the user — list filenames, where prompts live (`images/image_prompts.md` paste-ready blocks for ai rows; refresh via `image_gen.py --render-md` if stale), and where to place generated files (`project/images/<filename>`). After supply/replacement, validate the file and reconcile the owning row plus manifest to its usable status. For `slice` rows, list the parent sheet filename and target element names; the user places the sheet, then the agent reruns `slice_images.py`. 4. After the phase completes, summarize all `Needs-Manual` rows for the user — list filenames, where prompts live (`images/image_prompts.md` paste-ready blocks for ai rows; refresh via `image_gen.py --render-md` if stale), and where to place generated files (`project/images/<filename>`). After supply/replacement, validate the file and reconcile the owning row plus manifest to its usable status. For `slice` rows, list the parent sheet filename and target element names; the user places the sheet, then the agent reruns `slice_images.py`.
**Quick Generate export gate**: exhaust allowed automation without asking; stop **Quick Generate export gate**: exhaust allowed automation without asking; stop
@@ -117,7 +118,7 @@ before `--quick-generate` when a required row is not both backed by its
validated file/provenance and in a usable status. Preview/file presence alone validated file/provenance and in a usable status. Preview/file presence alone
never bypasses `Needs-Selection` or `Needs-Manual`. never bypasses `Needs-Selection` or `Needs-Manual`.
`Needs-Manual` is also the entry status for **Offline Manual Mode** (no `IMAGE_BACKEND` configured, no host-native image tool in use). Affected ai rows are marked `Needs-Manual` from the start without a failed attempt — see [`image-generator.md`](./image-generator.md) §7 Offline Manual Mode. `Needs-Manual` is also the entry status for **Offline Manual Mode**. Default uses it only when final Stage 2 or the runtime recovery decision explicitly confirmed `manual`; Quick uses it only when the active-context instruction explicitly selected `manual`. Neither profile checks configuration or probes a provider during planning; automated capability is resolved only during [`image-generator.md`](./image-generator.md) §7 execution.
Path-specific retry policies (provider chain, backend chain) live in the path's own reference. Path-specific retry policies (provider chain, backend chain) live in the path's own reference.
@@ -16,7 +16,7 @@ AI images exist to serve the deck's communication goal. Pick whatever combinatio
| `page_role` | Use | | `page_role` | Use |
|---|---| |---|---|
| `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 | | `local` | Image or transparent element is composed by SVG within the page. It may be boxed, unboxed, repeated as chrome, or become the page's dominant non-full-canvas visual; SVG owns final geometry and carrier combination |
| `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 | | `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 |
**Two text policies** (orthogonal to page_role): **Two text policies** (orthogonal to page_role):
@@ -153,7 +153,7 @@ Every assembled prompt follows this paragraph structure. **Write prose, not tag
[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"]. [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"].
[Composition — from the chosen type file or §4.1 no-type prose]. [Composition — from the chosen type file or §4.1 no-type prose].
[Image-specific subject — translated from the row's Reference intent into concrete visual nouns]. [Image-specific subject — translated from the row's Reference intent into concrete visual nouns].
[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]. [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 an opaque `local` image reserves no interior overlay space; a transparent illustration slice is generated as an isolated element and composed with other carriers by SVG].
[Hard rules — see §5]. [Hard rules — see §5].
``` ```
@@ -248,15 +248,17 @@ Example opening for a triptych hero:
**When uncertain about field conventions**: read `sources/` before drafting the prompt. **When uncertain about field conventions**: read `sources/` before drafting the prompt.
### 4.3 Illustration sheets — one generation, many spot or lettering elements ### 4.3 Illustration sheets — one generation, many composable illustration or lettering elements
An Illustration Sheet can produce several small **spot illustrations** or An Illustration Sheet produces compatible transparent **illustration elements**
**decorative lettering elements** in one generation and preserve closely or **decorative lettering elements** with matched rendering, deck-color
matched rendering, deck-color treatment, and finish before slicing. A lettering treatment, and finish before slicing. Elements may differ in subject,
sheet is the batch form of stable Layer 1 artwork; it does not turn page copy silhouette, visual weight, and page job, then combine with backgrounds, native
into an image. shapes, text, photos, other slices, or lettering on any suitable page. The sheet
generates assets; SVG authors the page composition. A lettering sheet remains
stable Layer 1 artwork and does not turn page copy into an image.
**Default — one sheet for a compatible asset family (may override when separate generation serves the assets better)**: Prefer a sheet when several spots or lettering 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 or standalone artistic word stays with the normal one-row-per-image flow (§4.1). **Default — one sheet per compatible visual family (may override when separate generation serves the assets better)**: Group illustration elements or planned marks by a coherent visual identity, not an identical effect recipe. A family may vary subject, silhouette, material, lighting, intensity, and intended visual weight; recurring title/corner ornaments, dominant anchors, supporting figures, and small accents are examples rather than required roles. Split only when cell geometry, detail, quality, or semantic precision materially conflicts. Quantity alone neither requires nor forbids a sheet: a single transparent element may use a keyed `1x1` sheet, while a full-canvas or nontransparent image stays on the normal one-row path (§4.1).
**Hard rule**: a 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. **Hard rule**: a 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.
@@ -265,12 +267,13 @@ into an image.
`text_policy: none`, lettering sheets use `text_policy: embedded`): `text_policy: none`, lettering sheets use `text_policy: embedded`):
- Choose the sheet `aspect_ratio` and `--grid` from the target element shape. Do not default every sheet to `1:1` + a symmetric grid. - Choose the sheet `aspect_ratio` and `--grid` from the target element shape. Do not default every sheet to `1:1` + a symmetric grid.
- 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). - Lay the elements out in an explicit logical **R×C placement grid, evenly spaced with clear gutters**, each element **centered in its own cell** and isolated. The grid is never drawn: gutters are uninterrupted key background, with no visible cells, panels, divider lines, borders, frames, or alternate gutter color.
- State the intended cell shape in the prompt: compact square object, tall portrait element, wide landscape vignette, or wide lettering mark. Do not let the model shrink every subject into a centered square sticker. - State the intended cell shape in the prompt: compact square object, tall portrait element, wide landscape vignette, or wide lettering mark. Do not let the model shrink every subject into a centered square sticker.
- 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. Gutters are part of that same flat ground; keep paper grain, halftone, vignette, and every other texture inside the elements, never over the background. - One **flat single-color chroma-key background** across the whole sheet. Choose and state one pure single-channel key (`#00FF00`, `#0000FF`, or `#FF0000`) whose active color does not dominate any element or supporting effect; it is a technical key, not part of the deck palette. Use that same untouched color in every gutter, keep it out of reflections and color spill, and keep paper grain, halftone, vignette, and every other texture inside the elements, never over the background.
- Shared `deck_rendering` + `color_scheme` as always. - Shared `deck_rendering` + `color_scheme` as always.
- **Spot sheet**: apply the §5.3 `none` cue; include no text, labels, or numbers. - **Illustration sheet**: name each intended element and its page or recurring-reuse job; apply the §5.3 `none` cue and include no text, labels, or numbers.
- **Lettering sheet**: put exactly one named stable string in each cell as the only text, quote every complete character sequence literally in the prompt, and describe one shared artistic treatment. Keep each mark isolated on the flat key background with no scene, title copy, labels, watermark, mockup surface, or unrequested symbols. - **Lettering sheet**: put exactly one named stable string in each cell as the only text and quote every complete character sequence literally in the prompt. Give the shared visual family, communication role, placement/background relationship, relative visual weight, and intended energy, then follow §5.3's controlled artistic-authorship default. Keep artistry inside the glyph through its silhouette, stroke structure, material, texture, depth, and contour-bound light/shadow. Do not add literal topic motifs, scene fragments, icons, detached ribbons, particles, or other surrounding illustrations unless the approved treatment explicitly requests a lettering-plus-illustration lockup. Keep each complete mark and any approved glyph-bound effect inside its cell with clear key-only padding; include no scene, unrelated copy, labels, watermark, or mockup surface.
- **Delivery floor, not an aesthetic ceiling**: when the chosen lettering treatment or its supporting effects need more footprint, enlarge the cell, change the grid, or use a larger/separate sheet. Do not weaken an approved treatment merely to fit a convenient crop; this geometry rule never raises the expression level selected under §5.3.
**Cell geometry is designed, not assumed.** `slice_images.py --grid RxC` cuts rows first and columns second. The cell ratio is: **Cell geometry is designed, not assumed.** `slice_images.py --grid RxC` cuts rows first and columns second. The cell ratio is:
@@ -280,38 +283,41 @@ cell_ratio = sheet_ratio * rows / cols
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. 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.
| Target spot shape | Sheet plan | Slice grid | | Target element shape | Sheet plan | Slice grid |
|---|---|---| |---|---|---|
| Compact objects / badges | `1:1` sheet | `2x2`, `2x3`, or `3x3` | | Compact objects / badges | `1:1` sheet | `2x2`, `2x3`, or `3x3` |
| Tall side accents / upright objects | wide or square sheet | `1xN`, or any `MxN` whose cells are portrait | | Tall side accents / upright objects | wide or square sheet | `1xN`, or any `MxN` whose cells are portrait |
| Wide banners / horizontal vignettes | wide sheet | `Nx1`, or any `MxN` whose cells are landscape | | Wide banners / horizontal vignettes | wide sheet | `Nx1`, or any `MxN` whose cells are landscape |
| Large page anchors / dominant cutouts | dedicated sheet matching the silhouette | `1x1` |
| Decorative words, phrases, or multi-line lettering lockups | wide sheet | `Nx1`, or any `MxN` whose cells fit the planned string shapes | | Decorative words, phrases, or multi-line lettering lockups | wide sheet | `Nx1`, or any `MxN` whose cells fit the planned string shapes |
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. Within one visual family, create separate sheets per shape family when mixed shapes cannot share a grid with enough room. Keep the family coherent through the same `deck_rendering` and `color_scheme`, not by forcing all cells into one square sheet or prescribing the same effect stack.
**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: **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:
- **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`, or `decorative lettering set: exact strings = ...`). 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. - **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: reusable title/corner illustration family`, or `decorative lettering set: exact strings = ...`). 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.
- **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. - **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 element should be fit, not cover-cropped). One row may serve several page compositions. 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.
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. For every sheet that will yield placeable elements, add `slice_grid` and `slice_names` to its `image_prompts.json` item when choosing the geometry. The comma-separated safe PNG basenames are the creation-time marker for the complete required output set. `image_gen.py` validates, preserves, and displays these metadata fields; it does not run the separate slicing command.
**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): **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. Use `--names` (semantic per-cell filenames matching the element rows; the count **must** equal `rows*cols`), `--trim` (tight-crop), `--alpha` (key out the flat ground), `--bg` (the exact key HEX named in the prompt), and `--strict-alpha` (write nothing when deterministic keying checks find an incomplete cut):
```bash ```bash
SHEET_KEY_HEX="#00FF00" # example only; choose a key absent from every element/effect
python3 scripts/slice_images.py <project>/images/illus_sheet.png --grid 2x3 \ python3 scripts/slice_images.py <project>/images/illus_sheet.png --grid 2x3 \
--names team,product,customer,growth,risk,vision --trim --alpha --names team,product,customer,growth,risk,vision --trim --alpha \
--bg "${SHEET_KEY_HEX}" --strict-alpha
``` ```
**Three constraints that decide whether it looks good**: **Three constraints that decide whether it looks good**:
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. Texture over the ground defeats the key, so request grain or halftone only inside the elements. `slice_images.py` warns when an element keeps opaque corners after `--alpha` or when `--trim` removed nothing; both mean the key did not take. Regenerate the sheet with a clean ground, or rerun with an explicit `--bg` and a larger `--tolerance`, before placing the element on a non-background surface. 1. **High-contrast flat chroma key.** `image_gen.py` has no transparent-background mode, so choose pure green, blue, or red whose active channel does not dominate the intended element/effect colors, state its exact HEX in the prompt, and pass it unchanged to `--bg`. The pure-key path removes color spill while recovering partial alpha for antialiasing, shadow, and glow. Texture, reflections, or key-color spill over the field still defeat extraction; keep them inside the artwork and away from the chosen key. If the model returns one visually flat chroma field with bounded pixel drift, measure that drift and raise `--tolerance` only enough to absorb it; the complete-boundary `--strict-alpha` gate must still pass. Regenerate or enlarge the sheet rather than placing a non-strict slice when an effect actually reaches an edge; use `--inset` only when an isolated outer gutter needs it.
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. For lettering, a wrong/missing character, extra copy, fused cells, or scene background makes the parent sheet unusable. 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, then slice the replacement sheet again. 2. **Clean invisible grid, or it cuts ugly.** State the exact row/column structure and cell shape without asking the model to draw the grid; `--trim` absorbs smaller placement variance. Fused cells, a scene background, or any flourish/effect crossing its cell makes the parent sheet unusable. Do not generate several sheets or read them back merely to choose a favorite; re-roll only after strict keying failure or user/live-preview feedback exposes an unusable slice, then slice the replacement sheet again.
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. 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 accents; use `2K` for medium placements; reserve `4K` for large, cropped, or potentially enlarged elements.
**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. Decorative-lettering slices may combine with shapes or other slices, while ordinary titles/subtitles remain separate SVG text. 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. **Reference — sliced-asset placement is not a constraint**: A transparent slice may remain an unboxed cutout, enter a container, or combine with background fields, native shapes, text, photos, other slices, and decorative lettering. It may repeat unchanged as deliberate title/corner chrome or be recomposed by page. Ordinary editable copy remains separate SVG text. The owner-resolved layout text is an expression recommendation; SVG authoring owns geometry and treatment while preserving resource identity and crop/content constraints.
**Through-line — one family, many roles.** A sheet pays off more when its visual family recurs coherently: an illustration sheet may share a motif world with cover/divider anchors, while a lettering sheet can supply the deck's small set of matching artistic hooks. A large cover/divider scene is not a giant sheet cell—generate it as its own `hero_page` image sharing the sheet's `deck_rendering` and `color_scheme`. Plan either relationship only when it serves the deck, never as a quota. **Through-line — one family, many page compositions.** Reuse a family wherever it improves hierarchy, rhythm, continuity, or visual character. Exact repetition is valid for stable title/corner chrome; anchors, supporting elements, and accents may instead vary in scale, position, pairing, and content interaction. A large transparent anchor composed by SVG remains `local` / `slice`; use `hero_page` only when one prepared bitmap owns the page composition. Plan either behavior by fit, never as a quota.
--- ---
@@ -463,6 +469,8 @@ Defaulting an entire `ai` resource list to `none` because "SVG can always overla
**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. **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.
**Default — controlled, deck-aligned artistic authorship (may override when the user explicitly requests high expression or confirms a strongly expressive direction)**: For decorative lettering, give the model the exact intended string, communication role, placement/background relationship, deck identity, relative visual weight, and desired energy. The resolved rendering, semantic colors, mood, and page hierarchy define the envelope. Without the stated override, keep expression controlled and glyph-native: carry identity through the glyph silhouette, stroke construction, internal material/texture, contour-bound depth/light, and letterform composition; do not translate the topic into literal illustrations or detached decoration around the word. A lettering-plus-illustration lockup is a separate treatment and requires an explicit user request or confirmed design direction. Within the chosen treatment, let the model decide and combine—or omit—the calligraphic gesture, material, dimensionality, texture, lighting, internal hierarchy, and composition; such terms are possibility space, not an effect recipe. Do not flatten the art merely to simplify extraction: §4.3's key field, clear padding, and cell isolation protect delivery without raising the chosen intensity. When fit is uncertain, use the lower effect density; never infer high expression or external motifs from the topic, place, or wording alone. Keep a multi-line lockup as one element when its hierarchy is part of the art.
**Font choice for in-image text — free description, with the deck typography as one optional reference** **Font choice for in-image text — free description, with the deck typography as one optional reference**
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. 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.
@@ -566,8 +574,8 @@ Write `project/images/image_prompts.json` with this shape:
| `items[].image_size` | no | Container sizing | `512px` / `1K` / `2K` / `4K` | | `items[].image_size` | no | Container sizing | `512px` / `1K` / `2K` / `4K` |
| `items[].model` | no | Per-item execution override | Backend model for this item; otherwise the CLI/backend default wins | | `items[].model` | no | Per-item execution override | Backend model for this item; otherwise the CLI/backend default wins |
| `items[].alt_text` | no | Accessibility | Short caption | | `items[].alt_text` | no | Accessibility | Short caption |
| `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` | | `items[].slice_grid` | required for a placeable-element sheet | §4.3 sheet geometry | Exact `RxC` grid to pass to `slice_images.py --grid`; requires `slice_names` |
| `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 | | `items[].slice_names` | required for a placeable-element sheet | §4.3 sheet geometry | Comma-separated safe PNG basenames to pass to `slice_images.py --names`; requires exactly `rows*cols` unique outputs |
| `items[].status` | yes | CLI manages | `Pending` initially; CLI updates to `Generated` / `Failed` / `Needs-Manual` | | `items[].status` | yes | CLI manages | `Pending` initially; CLI updates to `Generated` / `Failed` / `Needs-Manual` |
> **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. > **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.
@@ -591,31 +599,26 @@ C (AI-generated) supports three implementation modes sharing one `image_prompts.
| Trigger | Mode | Mechanism | | Trigger | Mode | Mechanism |
|---|---|---| |---|---|---|
| **Default** `IMAGE_BACKEND` configured | **Path A**: `image_gen.py --manifest` | One command runs the whole manifest with concurrency; status writes back per item | | `api` / `auto` permits Path A and `IMAGE_BACKEND` is configured | **Path A**: `image_gen.py --manifest` | One command runs the whole manifest with concurrency; status writes back per item |
| `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>` | | `host-native` / `auto` permits Path B and the host has a native image tool | **Path B**: Host-native tool | Agent invokes the host's image capability; outputs land at `project/images/<filename>` |
| **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>` | | Default confirmed `manual`, or Quick explicitly selected `manual` | **Offline Manual Mode** | Manifest stays on disk; user generates externally from `items[].prompt` and places files at `project/images/<filename>` |
**Callable-generator test for proactive planning**: Path A counts when **Planning boundary**: Strategist and Quick decide AI visual jobs from communication need, not current backend configuration. Do not inspect configuration or probe a provider before planning. Resolve actual Path A/B capability only when this section executes the selected path.
`IMAGE_BACKEND` is configured; Path B counts when the current host exposes a
native image-generation tool. Offline Manual alone, web search, and a
vision-only tool do not count. This predicate allows Strategist/Quick to add
decorative-lettering resources proactively; it never overrides an explicit or
confirmed image-source/path choice.
**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. **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 try Path A → Path B without asking or creating a planning artifact. If an automated path exhausts, apply the Quick no-AI replan below; Offline Manual is entered only from an explicit `manual` instruction.
**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: **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`; in Default, `auto` authorizes Path A → Path B only and never pre-authorizes Offline Manual. A missing/blank/unknown project value is not an implicit API or manual-generation authorization:
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`**: 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`**:
- `api`**Path A** (`image_gen.py --manifest`). - `api`**Path A** (`image_gen.py --manifest`).
- `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*. - `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*.
- `manual`**Offline Manual** (write prompts, render the Markdown sidecar, hand off; do **not** run `image_gen.py --manifest`). - `manual`**Offline Manual** (write prompts, render the Markdown sidecar, hand off; do **not** run `image_gen.py --manifest`).
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. If an explicitly chosen automated path is unavailable or still fails after its retry, do not switch provider or presume manual fulfillment; enter the Default recovery decision below. Only when the Design Spec records `auto` may both automated paths be attempted. 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.
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. 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.
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. 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.
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. 3. **Resolve exhausted automation** — Default enters the recovery decision below; Quick applies the no-AI replan below.
**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. **Hard rule**: normal execution does not reopen path selection. The only Default exception is the one recovery decision after the confirmed automated path or `auto`'s A → B sequence is actually unavailable/exhausted. Quick uses its explicit active-context instruction or automated path, then applies its declared no-AI replan without asking when automation exhausts.
> All three modes share one output contract: file at `project/images/<filename>`. Step 6 SVG references are mode-agnostic. > All three modes share one output contract: file at `project/images/<filename>`. Step 6 SVG references are mode-agnostic.
@@ -692,14 +695,14 @@ Triggered automatically when `IMAGE_BACKEND` is not configured (or Path A fails)
### Offline Manual Mode (C's third implementation mode) ### Offline Manual Mode (C's third implementation mode)
**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. **Trigger**: Default reaches this mode only after the user confirmed `manual` in final Stage 2 or at the exhausted-automation recovery decision. Quick reaches it only through an explicit `manual` instruction.
**Workflow** (no user prompting; system enters this mode automatically): **Workflow** (manual fulfillment is already authorized; do not ask again inside acquisition):
1. Verify `images/image_prompts.json` was written 1. Verify `images/image_prompts.json` was written
2. Set `status: "Needs-Manual"` on every affected item per [`image-base.md`](./image-base.md) §6 2. Set `status: "Needs-Manual"` on every affected item per [`image-base.md`](./image-base.md) §6
3. Apply the mode boundary: 3. Apply the mode boundary:
- Default Generate: continue to Step 6; Executor draws a dashed placeholder and Step 7 verifies the supplied file - Default Generate: continue to Step 6; Executor draws a dashed placeholder, but Step 7 blocks every export command until the supplied file is validated and the placeholder is replaced
- 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` - 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`
4. Print one consolidated handoff to the user: 4. Print one consolidated handoff to the user:
- Filenames awaiting manual generation - Filenames awaiting manual generation
@@ -709,23 +712,36 @@ Triggered automatically when `IMAGE_BACKEND` is not configured (or Path A fails)
**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. **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.
#### Default Exhausted-Automation Decision
When required AI rows remain unresolved after Default's confirmed automated path or `auto`'s eligible A → B sequence, keep them `Failed` and pause once with one consolidated list of filenames, prompts, attempted paths, and concrete errors. Ask the user to choose exactly one outcome:
1. **Repair and retry** — wait for the user to repair the named key, balance, endpoint, or host capability, then rerun only the same confirmed path; for `auto`, rerun the repaired eligible path and retain the A → B permission. If it fails again, return to this same decision with the new error.
2. **Generate manually** — update `design_spec.md §I` to `AI Image Acquisition Path: manual` as the newer explicit override, mark the affected rows `Needs-Manual`, render the handoff above, and continue authoring only up to the Step 7 image-readiness gate.
3. **Cancel affected AI images** — return to Generate Step 4 as a post-confirmation override; remove the affected `ai` / dependent `slice` rows and revise their §IX page jobs plus lock rows to native editable text/SVG or already-confirmed non-AI sources. If no AI rows remain, set the path to `not applicable` and remove the AI Image Strategy subsection. Never introduce a new image source or silently drop required communication content.
Do not create `Needs-Manual` state in Default before manual fulfillment is explicitly confirmed.
> Default Generate tolerates `Needs-Manual` rows through authoring and resumes > Default Generate tolerates `Needs-Manual` rows through authoring and resumes
> at Step 7. Quick Generate preserves the same operational manifest and handoff > at Step 7. An explicitly manual Quick run preserves the same operational
> but does not run `--quick-generate` while a required row still says > manifest and handoff but does not run `--quick-generate` while a required row still says
> `Needs-Manual`. If the original active context remains available, validate a > `Needs-Manual`. If the original active context remains available, validate a
> later supplied file and update it to `Generated`; otherwise start a clean > later supplied file and update it to `Generated`; otherwise start a clean
> Quick run rather than treating the manifest as a resumable design record. > Quick run rather than treating the manifest as a resumable design record.
#### Quick Exhausted-Automation No-AI Replan
When an automated AI path or required dependent slicing remains unresolved after its allowed attempts in Quick, do not ask the user and do not enter Offline Manual. Retain the affected filename, attempted path, concrete error, and replacement carrier in active context for the final Quick completion report; remove the affected `ai` row plus dependent `slice` rows from the active resource plan and remove the corresponding manifest item. Re-render `images/image_prompts.md` when other AI items remain; when none remain, remove both `images/image_prompts.json` and `images/image_prompts.md` so no stale failed row survives the replan. Preserve the communication job with native editable text/SVG or already prepared non-AI assets and continue the same run. Do not introduce another image source merely to replace the failed AI job. If the user still wants AI imagery, they must repair the generation capability and start a new Quick run.
#### AI-specific Failure Handling (extends image-base.md §6) #### AI-specific Failure Handling (extends image-base.md §6)
When the path is `auto` and Path A's backend fails twice in a row: When the path is `auto` and Path A's backend fails twice in a row:
1. Do not halt. Automatically attempt to fall back to **Path B (Host-Native Tool)**. 1. Do not halt. Automatically attempt to fall back to **Path B (Host-Native Tool)**.
2. If Path B also fails or is unavailable, mark the row `Needs-Manual`. 2. If Path B also fails or is unavailable, Default enters the three-outcome decision above without changing the row to `Needs-Manual`; Quick applies the no-AI replan above.
3. Report to user: filename, prompt used, error message. 3. Report the filename, prompt used, and error message through the owning outcome.
4. Fall through to **Offline Manual Mode** above.
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. 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, Default enters the decision above, while Quick applies the no-AI replan above.
> If the alternate platform watermarks outputs (e.g. Gemini web), the repository includes `scripts/gemini_watermark_remover.py`. > If the alternate platform watermarks outputs (e.g. Gemini web), the repository includes `scripts/gemini_watermark_remover.py`.
@@ -734,8 +750,8 @@ When `api` or `host-native` was explicitly confirmed, failure or unavailability
**Hard rule**: **Hard rule**:
- Do not claim an image is generated without an actual file at the expected path - Do not claim an image is generated without an actual file at the expected path
- `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 - `Needs-Manual` is set only when manual fulfillment was confirmed in Default or explicitly selected in Quick — not as a way to skip work that automation could have done
- Status transitions are evidence-driven: a file at the expected path permits `Generated`; an exhausted recovery path permits `Needs-Manual` - Status transitions are evidence-driven: a file at the expected path permits `Generated`; exhausted Default automation remains `Failed` until a retry succeeds or the user chooses manual or cancellation; exhausted Quick AI rows are removed only through the declared no-AI replan
--- ---
@@ -766,6 +782,8 @@ Diagnose the failure category, adjust the **one specific dimension** responsible
| Image looks generic, model-average | Tag-soup prompt | Rewrite as one coherent paragraph per §4 | | Image looks generic, model-average | Tag-soup prompt | Rewrite as one coherent paragraph per §4 |
| 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 | | 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 |
| 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 | | 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 |
| Lettering feels unrelated, overdecorated, or too dominant for its role | Expression exceeded the deck identity or planned visual weight | Retain the exact string and visual family; lower effect density, ornament, contrast, or lighting energy for the affected item/family instead of shrinking it into submission |
| Lettering carries mountains, buildings, animals, icons, ribbons, or other topic decoration around the glyph | The model turned subject context into an unrequested illustration lockup | Remove every external motif and rerun the affected item/family; express the identity through glyph structure, material, texture, depth, and contour-bound light instead |
| Hex code or color name visible as text in image | Missing §5.1 closing sentence | Append the §5.1 hard rule verbatim | | Hex code or color name visible as text in image | Missing §5.1 closing sentence | Append the §5.1 hard rule verbatim |
| 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" | | 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" |
| 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 | | 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 |
@@ -17,9 +17,9 @@ Compact composition vocabulary for prepared images and illustrations. Use the pa
| Group | Responsibility | Families | Entries | | Group | Responsibility | Families | Entries |
|---|---|---|---:| |---|---|---|---:|
| `P` · Primary Structures | Define the page skeleton | `P1` Single Visual · `P2` Image as Canvas · `P3` Multi-Visual | 46 | | `P` · Primary Structures | Define the page skeleton | `P1` Single Visual · `P2` Image as Canvas · `P3` Multi-Visual | 47 |
| `M` · Modifier Layers | Add crop/reveal, tone/focus, or framing/placement/depth treatment to an existing skeleton | `M1` Reveal/Crop/Registration · `M2` Tone/Focus/Contrast · `M3` Framing/Placement/Depth | 27 | | `M` · Modifier Layers | Add crop/reveal, tone/focus, or framing/placement/depth treatment to an existing skeleton | `M1` Reveal/Crop/Registration · `M2` Tone/Focus/Contrast · `M3` Framing/Placement/Depth | 28 |
| `A` · Asset-Dependent Treatments | Require a prepared composite, cutout, or registered derivative | `A1` Composite/Appearance · `A2` Subject Layers · `A3` Registered Derivatives | 10 | | `A` · Asset-Dependent Treatments | Require a prepared composite, cutout, or registered derivative | `A1` Composite/Appearance · `A2` Subject Layers · `A3` Registered Derivatives | 11 |
| `C` · Cross-Page Continuity | Sustain a visual relationship across slides | `C1` Persistent State · `C2` Camera Continuity · `C3` Matched Framing | 4 | | `C` · Cross-Page Continuity | Sustain a visual relationship across slides | `C1` Persistent State · `C2` Camera Continuity · `C3` Matched Framing | 4 |
| Mechanism, not generic “mask” | Owner | | Mechanism, not generic “mask” | Owner |
@@ -42,11 +42,12 @@ Compact composition vocabulary for prepared images and illustrations. Use the pa
|---|---| |---|---|
| Quiet, direct evidence | `#P1-11` negative space, `#P1-12` framed figure, `#P3-04` small multiples, `#P3-03` comparison | | Quiet, direct evidence | `#P1-11` negative space, `#P1-12` framed figure, `#P3-04` small multiples, `#P3-03` comparison |
| One visual should become the page canvas | `#P2-01``#P2-10` native overlays | | One visual should become the page canvas | `#P2-01``#P2-10` native overlays |
| One source should span unusual geometry | `#M1-10` one picture, `#M1-11` addressable pictures, `#A3-01` sharp subject over receded copy | | One source should span unusual geometry | `#M1-10` one picture, `#M1-11` addressable pictures, `#M1-12` glyph-shaped picture, `#A3-01` sharp subject over receded copy |
| Several visuals should read as one system | `#P3-05` grid, `#P3-14` mosaic with text cell, `#P3-20` tessellation, `#P3-21` split tiling, `#P3-22` curve array, `#P3-23` depth row | | Several visuals should read as one system | `#P3-05` grid, `#P3-14` mosaic with text cell, `#P3-20` tessellation, `#P3-21` split tiling, `#P3-22` curve array, `#P3-23` depth row, `#P3-24` curved photo wall |
| A foreground needs an opening or reveal | `#M1-06` true hole, `#M1-07` cut scrim, `#M1-08` background-registered fill, `#M1-05` text subtraction | | A foreground needs an opening or reveal | `#M1-06` true hole, `#M1-07` cut scrim, `#M1-08` background-registered fill, `#M1-05` text subtraction |
| Text needs contrast without discarding the visual | `#M2-01` directional scrim, `#M2-05` spotlight, `#A3-02` prepared frosted panel, `#M2-09` grid scrim | | Text needs contrast without discarding the visual | `#M2-01` directional scrim, `#M2-05` spotlight, `#A3-02` prepared frosted panel, `#M2-09` grid scrim |
| A subject should cross or re-layer around native content | `#A2-02` frame breakout or `#A2-03` registered reconstruction group | | A subject should cross or re-layer around native content | `#A2-02` frame breakout or `#A2-03` registered reconstruction group |
| A screenshot or interface needs a credible physical carrier | `#A2-04` registered device or frame mockup |
| A cover, divider, or promotional page needs image-led structure | `#P1-01`, `#P1-04`, `#P1-13`, or `#P3-15``#P3-19` | | A cover, divider, or promotional page needs image-led structure | `#P1-01`, `#P1-04`, `#P1-13`, or `#P3-15``#P3-19` |
| Consecutive pages should share one visual world | `#C1-01` persistent state, `#C2-01` pan, `#C2-02` push/pull, or `#C3-01` matched framing | | Consecutive pages should share one visual world | `#C1-01` persistent state, `#C2-01` pan, `#C2-02` push/pull, or `#C3-01` matched framing |
@@ -68,7 +69,7 @@ Compact composition vocabulary for prepared images and illustrations. Use the pa
- **#P1-10 · Slim image strip with large type** — place a narrow image strip beside oversized horizontal type. - **#P1-10 · Slim image strip with large type** — place a narrow image strip beside oversized horizontal type.
- **#P1-11 · Negative-space dominant** — keep the visual and copy compact so whitespace carries hierarchy. - **#P1-11 · Negative-space dominant** — keep the visual and copy compact so whitespace carries hierarchy.
- **#P1-12 · Framed figure with caption** — float one image in whitespace with a restrained frame and native caption. - **#P1-12 · Framed figure with caption** — float one image in whitespace with a restrained frame and native caption.
- **#P1-13 · Illustration as layout field** — let a large illustration or cutout set the page rhythm; place copy in its calm regions. - **#P1-13 · Illustration as layout field** — let one or more transparent illustration elements, alone or combined with native shapes or images, set the page rhythm; place copy in the resulting calm regions.
### 3.2 P2 · Image as Canvas with Native Overlay ### 3.2 P2 · Image as Canvas with Native Overlay
@@ -112,6 +113,7 @@ Compact composition vocabulary for prepared images and illustrations. Use the pa
- **#P3-21 · Split tiling** — fragment one parent contour into interlocking cells, each holding a different image as an independent object. - **#P3-21 · Split tiling** — fragment one parent contour into interlocking cells, each holding a different image as an independent object.
- **#P3-22 · Containers arrayed along a curve** — distribute containers consistently along an arc, wave, or ring; keep image orientation intentional. - **#P3-22 · Containers arrayed along a curve** — distribute containers consistently along an arc, wave, or ring; keep image orientation intentional.
- **#P3-23 · Embracing arc row** — create depth with a center-weighted scale and vertical-offset rhythm while keeping the objects two-dimensional. - **#P3-23 · Embracing arc row** — create depth with a center-weighted scale and vertical-offset rhythm while keeping the objects two-dimensional.
- **#P3-24 · Curved panoramic photo wall** — bow a multi-row lattice of same-treatment photo cells along one shared cylindrical curve: row edges follow common arcs, cell height and side-edge slant grow from the center outward as precomputed 2D clip contours, gutters stay even along the lattice, and every cell remains an independent upright clipped picture — never a shear/skew transform; optionally recede or omit the central columns when a native title should own the calm center.
--- ---
@@ -130,6 +132,7 @@ Compact composition vocabulary for prepared images and illustrations. Use the pa
- **#M1-09 · Deliberately misregistered fragments** — separate same-source fragments and break their alignment intentionally for torn, misprint, or glitch language. - **#M1-09 · Deliberately misregistered fragments** — separate same-source fragments and break their alignment intentionally for torn, misprint, or glitch language.
- **#M1-10 · One image across detached shapes** — export one native picture with disjoint clip subpaths so one continuous scene spans every shape. - **#M1-10 · One image across detached shapes** — export one native picture with disjoint clip subpaths so one continuous scene spans every shape.
- **#M1-11 · Same-source addressable crops** — export several independent native pictures that share an exact source coordinate system; follow [`executor-image.md`](./executor-image.md) §1. - **#M1-11 · Same-source addressable crops** — export several independent native pictures that share an exact source coordinate system; follow [`executor-image.md`](./executor-image.md) §1.
- **#M1-12 · Glyph-shaped picture** — fill letterforms with an image. Default to the editable native text picture fill in [`svg-effects.md`](./svg-effects.md) §6.3, which keeps the string as text; only when the page needs a true picture object — one continuous photo reading through several glyphs, or a glyph contour treated like other clipped pictures — materialize a stable short string (year, hero number, motif word) through [`native-shape-authoring.md`](./native-shape-authoring.md) §6 and clip one picture to that geometry. Rewordable copy never takes the materialized form; `#M1-05` remains the subtraction variant behind glyph-shaped holes.
The following three patterns are topologically different and are not interchangeable: The following three patterns are topologically different and are not interchangeable:
@@ -176,9 +179,10 @@ The following three patterns are topologically different and are not interchange
### 5.2 A2 · Subject and Cutout Layers ### 5.2 A2 · Subject and Cutout Layers
- **#A2-01 · Transparent sticker or cutout** — use a prepared RGBA asset and preserve its open silhouette. - **#A2-01 · Transparent illustration or cutout** — use a prepared RGBA asset and preserve its open silhouette; compose it freely or repeat it as planned page chrome.
- **#A2-02 · Subject breaking out of a container** — register a prepared foreground subject across its frame boundary. - **#A2-02 · Subject breaking out of a container** — register a prepared foreground subject across its frame boundary.
- **#A2-03 · Registered reconstruction group** — align a clean base with one or more prepared transparent midground/subject/foreground layers in one coordinate system. Draw each member at its required z-order. Give every full-canvas member the same `x`, `y`, `width`, `height`, and aspect mapping; never trim or independently crop it. Several padded-bbox-disjoint objects may share one prepared plate while remaining separate nested-SVG picture crops. - **#A2-03 · Registered reconstruction group** — align a clean base with one or more prepared transparent midground/subject/foreground layers in one coordinate system. Draw each member at its required z-order. Give every full-canvas member the same `x`, `y`, `width`, `height`, and aspect mapping; never trim or independently crop it. Several padded-bbox-disjoint objects may share one prepared plate while remaining separate nested-SVG picture crops.
- **#A2-04 · Registered device or frame mockup** — seat a screenshot or flat artwork beneath a prepared transparent device/frame asset at its exact registered screen region; the frame keeps its RGBA silhouette on top and both remain independent picture objects, so the content stays replaceable.
### 5.3 A3 · Registered Derivatives ### 5.3 A3 · Registered Derivatives
@@ -234,6 +238,10 @@ If motion is enabled, [`animations.md`](./animations.md) owns its implementation
| One selected region needs explanation | Use `#P2-03` for an outline and caption on one picture; use `#P2-04` when a second same-source picture must magnify the region | | One selected region needs explanation | Use `#P2-03` for an outline and caption on one picture; use `#P2-04` when a second same-source picture must magnify the region |
| Several sources should read as one object | `#P3-21` + restrained `#M3-01`, or `#P3-20` + a native text/color cell | | Several sources should read as one object | `#P3-21` + restrained `#M3-01`, or `#P3-20` + a native text/color cell |
| Several mixed-ratio photos need one visual rhythm | Use `#P3-06` + `#M1-01` + `#M2-01`; repeat equal-size parallelogram carriers on one horizontal `vector` with fixed positive advance and alternating transverse offsets, fill-crop each upright bitmap, overlay each carrier with a same-contour directional gradient, and keep labels native | | Several mixed-ratio photos need one visual rhythm | Use `#P3-06` + `#M1-01` + `#M2-01`; repeat equal-size parallelogram carriers on one horizontal `vector` with fixed positive advance and alternating transverse offsets, fill-crop each upright bitmap, overlay each carrier with a same-contour directional gradient, and keep labels native |
| Many same-kind photos need one ceremonial fan or arc | `#P3-22` + `#M1-01`; repeat one equal-width slanted strip contour along the arc with fixed angular advance and constant gutters, clip one upright photo into each strip as its own picture, and keep every crop focal-safe |
| A many-photo montage should read as one immersive curved wall | `#P3-24` + `#M1-02` + optional `#M2-08`; derive every cell contour from one cylindrical mapping — curved top/bottom edges on shared row arcs, straight slanted sides, even gutters — keep each bitmap upright inside its precomputed clip with no shear/skew transform, and fade or omit the central columns when the native title should own the calm center |
| A photo surface should read as folded or louvered | `#M1-11` + `#M2-03`; cut one source into equal-width registered strips and alternate a subtly darker and lighter wash per strip so the seams read as folds, keeping each strip an addressable picture |
| Mixed evidence tiles should read as one bento field | `#P3-12` + `#P3-14` + `#P2-05`; pack rounded cells sharing one corner radius and gap rhythm, mix image cells with native KPI/text cells, and let one oversized cell anchor the hierarchy |
| One continuous scene should span detached shapes | `#M1-10` + optional `#M3-05`; combine `#P1-08` for a triangular image-and-copy split, and keep one-picture topology | | One continuous scene should span detached shapes | `#M1-10` + optional `#M3-05`; combine `#P1-08` for a triangular image-and-copy split, and keep one-picture topology |
| Same-source windows must remain independent | `#M1-11`; add `#C2-01` or `#C2-02` only when consecutive pages use the relationship | | Same-source windows must remain independent | `#M1-11`; add `#C2-01` or `#C2-02` only when consecutive pages use the relationship |
| A prepared subject should re-layer over its source | `#A2-03`; keep the base and cutout registered, and insert a native middle layer only when it has a distinct job | | A prepared subject should re-layer over its source | `#A2-03`; keep the base and cutout registered, and insert a native middle layer only when it has a distinct job |
@@ -241,7 +249,7 @@ If motion is enabled, [`animations.md`](./animations.md) owns its implementation
| A visual argument should build across pages | `#C1-01` + `#P2-01` or `#P2-05`; keep the underlying source and frame stable | | A visual argument should build across pages | `#C1-01` + `#P2-01` or `#P2-05`; keep the underlying source and frame stable |
| Technical figure needs explanation | `#P1-12` + `#P2-07` / `#P2-03`; use `#P2-04` only when a second cropped detail is useful, and keep explanatory labels native | | Technical figure needs explanation | `#P1-12` + `#P2-07` / `#P2-03`; use `#P2-04` only when a second cropped detail is useful, and keep explanatory labels native |
**Registration boundary**: registration-dependent effects succeed only when their declared coordinate relationship remains exact. Preserve registration for `#M1-10`, `#A2-02`, `#A3-01`, `#M1-08`, `#A2-03`, `#A3-02`, `#A3-03`, and `#M1-11`; `#M1-09` is the intentional exception. **Registration boundary**: registration-dependent effects succeed only when their declared coordinate relationship remains exact. Preserve registration for `#M1-10`, `#A2-02`, `#A3-01`, `#M1-08`, `#A2-03`, `#A2-04`, `#A3-02`, `#A3-03`, and `#M1-11`; `#M1-09` is the intentional exception.
**Source-correspondence boundary**: `#P2-04` reuses one exact source but intentionally changes the detail crop, scale, and placement; preserve the selected-region correspondence instead of forcing page-space registration. **Source-correspondence boundary**: `#P2-04` reuses one exact source but intentionally changes the detail crop, scale, and placement; preserve the selected-region correspondence instead of forcing page-space registration.
@@ -39,10 +39,10 @@ Each rendering keeps its own authoritative file with: style paragraph, line / te
| Rendering | One-liner | Best for | | Rendering | One-liner | Best for |
|---|---|---| |---|---|---|
| [`watercolor`](./watercolor.md) | Painterly soft edges, color bleeding | Lifestyle / travel / brand story | | [`watercolor`](./watercolor.md) | Painterly soft edges, color bleeding | Illustrative lifestyle / travel story / brand story |
| [`warm-scene`](./warm-scene.md) | Golden-hour cinematic warmth | Personal growth / origin story | | [`warm-scene`](./warm-scene.md) | Golden-hour cinematic warmth | Personal growth / origin story |
| [`screen-print`](./screen-print.md) | Halftone poster art, 2-5 flat colors | Cultural / media / cinematic covers | | [`screen-print`](./screen-print.md) | Halftone poster art, 2-5 flat colors | Cultural / media / cinematic covers |
| [`vintage-poster`](./vintage-poster.md) | Mid-century modern poster, halftone + paper grain | Cultural / brand heritage / hospitality / anniversaries | | [`vintage-poster`](./vintage-poster.md) | Mid-century modern poster, halftone + paper grain | Cultural retrospective / brand heritage / historic hospitality identity / anniversaries |
### 1.4 Specialty ### 1.4 Specialty
@@ -75,9 +75,9 @@ Write `image_rendering_references` only when the confirmed custom direction actu
--- ---
## 2. Auto-selection table — `design_spec` → rendering ## 2. Selection recall — confirmed style + image job → rendering
Match `design_spec.md d` (mode + `visual_style`) against this table. First match wins. **No row matches** → use `custom` per §1.5 rather than force-fitting `vector-illustration`. (When the locked `visual_style` names a paired rendering, prefer that for aesthetic alignment.) **Reference — not a constraint**: Resolve the direction's intended image jobs and visual style before using this table. A topic keyword alone never selects the deck-wide rendering or turns a named real-world subject into an AI row. Travel planning and other documentary work keeps verifiable places in `provided` / `web`; its AI rendering applies only to complementary invented or deliberately stylized roles. Choose the strongest task-and-style match. **No row matches** → use `custom` per §1.5 rather than force-fitting `vector-illustration`. When the visual style names a paired rendering, prefer that for aesthetic alignment only when the intended image jobs still fit.
| `d. Style` signal | Recommended rendering | Alternates | | `d. Style` signal | Recommended rendering | Alternates |
|---|---|---| |---|---|---|
@@ -95,7 +95,7 @@ Match `design_spec.md d` (mode + `visual_style`) against this table. First match
| Finance / data journalism / 财经 | `editorial` or `digital-dashboard` | `vector-illustration` | | Finance / data journalism / 财经 | `editorial` or `digital-dashboard` | `vector-illustration` |
| Personal story / 个人成长 / lifestyle | `watercolor`, `warm-scene` | `corporate-photo`, `paper-cut` | | Personal story / 个人成长 / lifestyle | `watercolor`, `warm-scene` | `corporate-photo`, `paper-cut` |
| Cultural / media / opinion / cinematic | `screen-print`, `vintage-poster` | `editorial`, `warm-scene` | | Cultural / media / opinion / cinematic | `screen-print`, `vintage-poster` | `editorial`, `warm-scene` |
| Brand heritage / hospitality / 老字号 / 周年 | `vintage-poster` | `screen-print`, `editorial` | | Brand heritage / historic hospitality identity / 老字号 / 周年 | `vintage-poster` | `screen-print`, `editorial` |
| Gaming / retro / 8-bit / 复古 | `pixel-art` | `vintage-poster` | | Gaming / retro / 8-bit / 复古 | `pixel-art` | `vintage-poster` |
| Environment / wellness / 环保 / 户外 | `nature` | `watercolor`, `paper-cut` | | Environment / wellness / 环保 / 户外 | `nature` | `watercolor`, `paper-cut` |
| Classroom / blackboard / 课堂 | `chalkboard` | `sketch-notes` | | Classroom / blackboard / 课堂 | `chalkboard` | `sketch-notes` |
@@ -10,13 +10,13 @@ Role definition for the **web image acquisition path**: translate the active res
## 1. License Tier Discipline ## 1. License Tier Discipline
Every **provider-sourced** image is classified into one of two tiers; anything else is rejected outright. A third tier, `manual`, exists **only** for a directly selected [`--from-url`](#5-running-image_searchpy) replacement — it is never the result of a provider search accepting an unknown license. Every **provider-sourced** image is classified into one of two tiers; anything else is rejected outright. A third tier, `manual`, exists **only** for a directly selected replacement from [`--from-url`](#5-running-image_searchpy) or an adopted-page source package — it is never the result of a provider search accepting an unknown license.
| Tier | Licenses | On-slide attribution | | Tier | Licenses | On-slide attribution |
|---|---|---| |---|---|---|
| `no-attribution` | CC0, Public Domain, Pexels License, Pixabay Content License | None | | `no-attribution` | CC0, Public Domain, Pexels License, Pixabay Content License | None |
| `attribution-required` | CC BY, CC BY-SA | Inline credit `<text>` on the slide | | `attribution-required` | CC BY, CC BY-SA | Inline credit `<text>` on the slide |
| `manual` | User-supplied via `--from-url` (license unverified) | None — verifying rights / any credit is the user's responsibility | | `manual` | Directly selected URL or adopted-page package image (license unverified) | None — verifying rights / any credit is the user's responsibility |
**Forbidden — auto-rejected licenses**: **Forbidden — auto-rejected licenses**:
@@ -213,7 +213,7 @@ Never treat a generic `required_terms` pass as acceptance. For example, matching
1. With vision, promote the one passing thumbnail selected under the review-stage contract; this is the first original-image request. 1. With vision, promote the one passing thumbnail selected under the review-stage contract; this is the first original-image request.
2. If none passes and `has_more_candidates` is true, fetch the next ranked page (8 by default). Candidate numbers continue globally, so page 2 starts at `candidate_09`; do not repeat page 1 or download an original. 2. If none passes and `has_more_candidates` is true, fetch the next ranked page (8 by default). Candidate numbers continue globally, so page 2 starts at `candidate_09`; do not repeat page 1 or download an original.
3. After the current pool is exhausted, add materially different query variants for identity wording, official translation, alias, viewpoint, or disambiguation and generate a fresh pool; do not repeat a semantically exhausted query. 3. After the current pool is exhausted, add materially different query variants for identity wording, official translation, alias, viewpoint, or disambiguation and generate a fresh pool; do not repeat a semantically exhausted query.
4. With vision only, if normal search is exhausted, open one relevant retained research page and test one plausible inline-image URL with the same `--from-url` command below. Inspect that single download before trying another; never bulk-download the page or use it as the initial pool. 4. With vision only, if normal search is exhausted, select one relevant adopted `source_url` and follow [`topic-research`](../workflows/stages/topic-research.md) § Hand-off to fetch its Markdown + companion-image source package. Review that package, copy only one passing image into `<project>/images/`, and reconcile the query row plus `image_sources.json` from the selected `image_manifest.json` entry with `license_tier: manual`. Fetch another page only when the current package has no passing image; never auto-expand facts URLs or promote the whole package.
5. **manual URL replace (universal, model-agnostic)** — use a directly selected URL and swap it in: 5. **manual URL replace (universal, model-agnostic)** — use a directly selected URL and swap it in:
```bash ```bash
python3 scripts/image_search.py --from-url <image-url> --filename <name>.jpg -o <project_path>/images python3 scripts/image_search.py --from-url <image-url> --filename <name>.jpg -o <project_path>/images
@@ -226,7 +226,7 @@ Never treat a generic `required_terms` pass as acceptance. For example, matching
that query row and the active roster to `Sourced` before export; a stale that query row and the active roster to `Sourced` before export; a stale
`Needs-Manual` status remains blocking `Needs-Manual` status remains blocking
([`executor-web-image.md`](./executor-web-image.md) §1); ([`executor-web-image.md`](./executor-web-image.md) §1);
6. When the query variants, ranked pages, configured provider chain, permitted license stages, and eligible retained-page fallback are exhausted, mark the row `Needs-Manual`. 6. When the query variants, ranked pages, configured provider chain, permitted license stages, and eligible adopted-page package fallback are exhausted, mark the row `Needs-Manual`.
**This review never opens an acquisition-time interaction** ([`image-base.md`](./image-base.md) §6). Default Generate may build a placeholder and continue to Step 6. Quick Generate finishes all permitted automated strategies, records `Needs-Manual`, and blocks direct export when the unresolved image is required. **This review never opens an acquisition-time interaction** ([`image-base.md`](./image-base.md) §6). Default Generate may build a placeholder and continue to Step 6. Quick Generate finishes all permitted automated strategies, records `Needs-Manual`, and blocks direct export when the unresolved image is required.
@@ -319,7 +319,7 @@ Every successful download appends or replaces one entry keyed on `filename`:
| `selection_method` | `visual-thumbnail` after promotion from a reviewed preview, or `metadata-ranked` for the strict no-vision / best-only path. It never claims a visual check that did not occur. | | `selection_method` | `visual-thumbnail` after promotion from a reviewed preview, or `metadata-ranked` for the strict no-vision / best-only path. It never claims a visual check that did not occur. |
| `width` / `height` | Measured from the file actually saved to disk. Use these for layout. | | `width` / `height` | Measured from the file actually saved to disk. Use these for layout. |
| `metadata_dimensions` | Present only when upstream-claimed size differs from the saved file (preview vs original). Informational only. | | `metadata_dimensions` | Present only when upstream-claimed size differs from the saved file (preview vs original). Informational only. |
| `license_tier` | Drives Executor's attribution decision: `no-attribution` / `attribution-required` for provider-sourced images, or `manual` for a directly selected `--from-url` replacement (embed only; rights/credit are the user's responsibility). | | `license_tier` | Drives Executor's attribution decision: `no-attribution` / `attribution-required` for provider-sourced images, or `manual` for a directly selected URL/source-package replacement (embed only; rights/credit are the user's responsibility). |
| `attribution_required` | Boolean alias of `license_tier == "attribution-required"`. | | `attribution_required` | Boolean alias of `license_tier == "attribution-required"`. |
| `attribution_text` | Canonical credit source. Preserve its author/provider/license facts; compress only through §7's visual grammar rather than inventing or dropping identity. | | `attribution_text` | Canonical credit source. Preserve its author/provider/license facts; compress only through §7's visual grammar rather than inventing or dropping identity. |
| `stage` | `all` by default, or `no-attribution-only` when strict mode is used. | | `stage` | `all` by default, or `no-attribution-only` when strict mode is used. |
@@ -390,7 +390,7 @@ Executor reads `image_sources.json` per slide that uses a Sourced image. For eac
|---|---| |---|---|
| `no-attribution` | Embed `<image>` only | | `no-attribution` | Embed `<image>` only |
| `attribution-required` | Embed `<image>` **and** an inline credit element per §7 | | `attribution-required` | Embed `<image>` **and** an inline credit element per §7 |
| `manual` | Embed `<image>` only — directly selected URL (`--from-url`); verifying usage rights / any required credit is the user's responsibility | | `manual` | Embed `<image>` only — directly selected URL or adopted-page package image; verifying usage rights / any required credit is the user's responsibility |
Executor does not interpret raw license strings — `license_tier` is sufficient. Executor does not interpret raw license strings — `license_tier` is sufficient.
@@ -404,7 +404,7 @@ In addition to the shared checkpoint in [`image-base.md`](./image-base.md) §10:
- [ ] Every required web row is `Sourced` with a downloaded original at `project/images/<filename>` OR is marked `Needs-Manual`; `Needs-Selection` remains incomplete - [ ] Every required web row is `Sourced` with a downloaded original at `project/images/<filename>` OR is marked `Needs-Manual`; `Needs-Selection` remains incomplete
- [ ] Each multimodal `Sourced` web image was selected from a bounded ranked thumbnail page and only its winner original was downloaded; a no-pass page advanced through remaining pages before query replacement. Without vision, only strict metadata candidates may become `Sourced`, with `selection_method: metadata-ranked`; unresolved or visually unprovable intent becomes `Needs-Manual` without pretending a visual check occurred - [ ] Each multimodal `Sourced` web image was selected from a bounded ranked thumbnail page and only its winner original was downloaded; a no-pass page advanced through remaining pages before query replacement. Without vision, only strict metadata candidates may become `Sourced`, with `selection_method: metadata-ranked`; unresolved or visually unprovable intent becomes `Needs-Manual` without pretending a visual check occurred
- [ ] Each `Sourced` row has a manifest entry with valid `license_tier` and non-empty `attribution_text` (except `manual` `--from-url` rows, which carry no `attribution_text`) - [ ] Each `Sourced` row has a manifest entry with valid `license_tier` and non-empty `attribution_text` (except `manual` directly selected rows, which carry no `attribution_text`)
- [ ] Any `attribution-required` image has visible author + license credit in every SVG that references it - [ ] Any `attribution-required` image has visible author + license credit in every SVG that references it
- [ ] `metadata_dimensions` warnings surfaced when downloaded preview is much smaller than upstream-claimed size - [ ] `metadata_dimensions` warnings surfaced when downloaded preview is much smaller than upstream-claimed size
- [ ] `Needs-Manual` rows include the failure reason - [ ] `Needs-Manual` rows include the failure reason
@@ -12,33 +12,38 @@ Always-on Stage-2 rendering-candidate extension plus confirmed image elaboration
Before Stage 2, construct rendering candidates independently of the proposed source set. After confirmation, map the confirmed source set through [`strategist.md`](./strategist.md) §h and honor explicit `image_notes` roles; this module never adds a source. The confirmed non-`none` set is an allowed acquisition boundary, not coverage: use a suitable subset and leave irrelevant sources unused. Explicit must-use sources, assets, or page roles remain required. Asset inventory and judgment determine unconfirmed count, subject, placement, and composition without substituting an unconfirmed source. Before Stage 2, construct rendering candidates independently of the proposed source set. After confirmation, map the confirmed source set through [`strategist.md`](./strategist.md) §h and honor explicit `image_notes` roles; this module never adds a source. The confirmed non-`none` set is an allowed acquisition boundary, not coverage: use a suitable subset and leave irrelevant sources unused. Explicit must-use sources, assets, or page roles remain required. Asset inventory and judgment determine unconfirmed count, subject, placement, and composition without substituting an unconfirmed source.
For illustration, apply this precedence: confirmed `none` explicit user intent the locked visual style's `Illus.` propensity (`core` / `supportive` / `sparse`) → none. Propensity controls the lean, not the source or a page quota. When illustration is active, prefer one coherent motif family across hero/section anchors and local spots, but only when the confirmed assets can form that family. For illustration, confirmed `none` stops and explicit user intent wins. Otherwise the locked visual style's `Illus.` propensity (`core` / `supportive` / `sparse`) tunes centrality and recurrence after the per-page composition scan; it never restricts eligible page types, element scale, or carrier combinations. When illustration is active, prefer one coherent family that can serve the actual page jobs, including recurring title/corner chrome, dominant anchors, supporting figures, and accents.
**Context-first understanding for provided assets**: Do not visually scan `images/`. First infer identity, role, and crop / focus needs from source position and surrounding prose, captions / alt / titles, filename, user notes / confirmed `image_notes`, existing resource records, and CSV geometry. Inspect only one specific image when a remaining ambiguity would change selection, factual identity, page role, crop safety, or focal placement. Never inspect for inspiration, bulk-open the folder, or infer external facts / provenance from pixels. Record the result in §VIII. Leave an optional unresolved asset unused; route an unresolved must-use asset through failure recovery. **Context-first understanding for provided assets**: Do not visually scan `images/`. First infer identity, role, and crop / focus needs from source position and surrounding prose, captions / alt / titles, filename, user notes / confirmed `image_notes`, existing resource records, and CSV geometry. Inspect only one specific image when a remaining ambiguity would change selection, factual identity, page role, crop safety, or focal placement. Never inspect for inspiration, bulk-open the folder, or infer external facts / provenance from pixels. Record the result in §VIII. Leave an optional unresolved asset unused; route an unresolved must-use asset through failure recovery.
**Default — one coherent sheet for compatible same-family spots or lettering elements (may override when aspect, detail, quality, or semantic needs differ)**: prefer one Illustration Sheet when several AI-generated spots or stable decorative-lettering elements can share a useful cell shape and production treatment; generate them independently when forcing one sheet would weaken a planned element. When a sheet is chosen, plan one unplaced `ai` Illustration Sheet row plus one placed `slice` row per used element; only slice rows enter `spec_lock.md images`. State the intended placement shape family in the sheet reference. For lettering, also record every exact string, set the sheet to `text_policy: embedded`, and keep authoritative title/chrome wording outside the sheet. Use separate sheets for incompatible shapes or treatments. [`image-generator.md`](./image-generator.md) §4.3 owns grid, ratio, slicing, and execution details. Final Stage 2 chooses the AI execution path under `image-generator.md` §7; do not pre-empt or re-pick it here. **Default — one coherent sheet per compatible visual family (may override when aspect, detail, quality, or semantic needs differ)**: group AI illustration elements or exact lettering strings by shared visual identity rather than an identical effect recipe; compatible elements share a sheet and split only when their geometry, detail, quality, or semantics conflict. For each sheet, plan one unplaced `ai` Illustration Sheet row plus one placed `slice` row per used element; only slice rows enter `spec_lock.md images`, and one element row may serve several §IX pages. State each element's communication job, placement/reuse relationship, relative visual weight, energy, family, and shape without prescribing an effect stack. Use glyph-native expression by default; record a lettering-plus-illustration lockup only when the user explicitly requests it or the confirmed direction requires it. Lettering sheets use `text_policy: embedded`; the asset may carry the complete display title, while any required searchable, selectable, or outline-visible title remains an ordinary separate native text frame. [`image-generator.md`](./image-generator.md) §§4.3 and 5.3 own the controlled-default/high-expression boundary, artistic authorship, grid, key field, slicing, and execution details. Final Stage 2 chooses the AI execution path under `image-generator.md` §7; do not pre-empt or re-pick it here.
**Mandatory — materialize proactive lettering**: When confirmed image usage **Mandatory — evaluate decorative lettering under the two-question gate**:
retains `ai`, the effective acquisition path has a callable Path A/B, and the When confirmed image usage retains `ai`, scan the complete page roster once
complete page roster contains a suitable display string anywhere in the deck, before writing §VIII. Confirmed `ai` is a Permission, not coverage: never create
collect the compatible set once before writing §VIII. Eligibility turns on two lettering merely to justify the AI source or because no other AI-image job was
questions onlyis the wording stable, and would an artistic treatment found. Eligibility turns on two questions onlyis the wording stable, and would
communicate better than native type. Page role, length, line count, and kind of an artistic treatment communicate better than native type. When either answer
noun never filter candidates; treat cover hooks, chapter words, place or product is no, create no lettering row and keep the wording as native editable text.
names, dish or exhibit names, years, hero numbers, pull quotes, and motif words Page role, character count, word count, line count, and kind of noun never
as examples rather than the allowed set. Use one ordinary `ai` row filter candidates; a complete long title, multi-word phrase, and multi-line
for a single mark, or the sheet/element rows under §4.3 for several compatible lockup are as eligible as a short mark. Preserve each full exact character
marks, and record every exact character sequence; do not leave the choice as an sequence as one intended mark when its hierarchy belongs to the art; never
`image_notes` or §IX suggestion only. A two-character mark, a multi-word phrase, trim, rewrite, or split it merely to ease generation. When both answers are
and a two-line lockup are equally eligible; never trim a phrase toward one or two yes, the second answer already establishes the communication benefit; do not
characters to look more like a wordmark. Eligibility is wide but use stays add another eligibility test. Eligibility is wide, but deck-wide use stays
selective: build one small coherent set rather than lettering every heading. A selective: choose a coherent set rather than lettering every heading; this
planned wordmark and an editable page limits coverage, not eligibility. Adopt the selected marks under the confirmed
title coexist: the asset carries the display layer while subtitle, chrome, and natural-language image intent, then materialize every adopted choice as one
body remain native text. A confirmed `none`, explicit no-AI ordinary `ai` row or as the §4.3 sheet/element rows rather than leaving it as a
instruction, editable-only hook, or Offline Manual path does not activate this planning suggestion. Use has no coverage quota. An asset may carry the
proactive rule; an explicit user-required lettering asset still follows the complete long or multi-line title as its display layer. Keep an ordinary native
ordinary resource contract. title/subtitle in a separate text frame wherever the page needs a searchable,
selectable, or outline-visible heading. Chrome and body remain native text. A
confirmed `none`, explicit no-AI instruction,
editable-only hook, or Offline Manual path does not activate this proactive
rule; an explicit user-required lettering asset still follows the ordinary
resource contract.
**Mandatory — image-treatment path scan, not a quota**: Per selected image choose `none` (unchanged), `native` (SVG crop/clip, transform, opacity, frame/depth, overlap), or `prepared derivative` (separate pixel blur/tone or cutout/registered layers); `none` is valid. **Mandatory — image-treatment path scan, not a quota**: Per selected image choose `none` (unchanged), `native` (SVG crop/clip, transform, opacity, frame/depth, overlap), or `prepared derivative` (separate pixel blur/tone or cutout/registered layers); `none` is valid.
@@ -58,7 +58,7 @@ Do not force communication intent into one catalog label; Stage 1 records compos
| Reference | Preserve the selected direction or role; adapt its realization to context. | | Reference | Preserve the selected direction or role; adapt its realization to context. |
| Permission / default | An allowed candidate/source boundary or preference; Strategist may leave it unused, with no quota. | | Permission / default | An allowed candidate/source boundary or preference; Strategist may leave it unused, with no quota. |
**Authority chain — materials → Strategist preparation → realization.** User inputs set materials/acquisition bounds. Strategist owns sufficiency, gap-filling, and selection: roster/content, resources, page-local visualization/Layout references, fonts, palette anchors, the icon library/stroke plus curated project pool, and crop bans. Topic research and text-only import of its retained webpages may precede confirmation; their image links are only a post-search fallback. Independent AI/web/slice acquisition follows final confirmation plus completed §VIII/lock; icons are synced/validated during authoring without page assignment. Before Executor, each resource has a path and terminal/`Needs-Manual` state. Executor owns geometry, composition, hierarchy, spacing, treatment, and per-page choice among prepared icons; it never searches, generates, syncs, invents, or substitutes resources. Missing material/reselection returns upstream. Specificity defines freedom; References flex realization, never selection. **Authority chain — materials → Strategist preparation → realization.** User inputs set materials/acquisition bounds. Strategist owns sufficiency, gap-filling, and selection: roster/content, resources, page-local visualization/Layout references, fonts, palette anchors, the icon library/stroke plus curated project pool, and crop bans. Topic research and import of its two-artifact research pair may precede confirmation; facts URLs are not auto-expanded. Only after normal image search fails may one adopted webpage become a reviewable Markdown + companion-image source package, with accepted files promoted individually. Independent AI/web/slice acquisition follows final confirmation plus completed §VIII/lock; icons are synced/validated during authoring without page assignment. Before Executor, each resource has a path and terminal/`Needs-Manual` state. Executor owns geometry, composition, hierarchy, spacing, treatment, and per-page choice among prepared icons; it never searches, generates, syncs, invents, or substitutes resources. Missing material/reselection returns upstream. Specificity defines freedom; References flex realization, never selection.
Explicit *must*, *only*, *exactly*, *verbatim*, *do not*, or `no-crop` wording may strengthen only the named property into the appropriate Literal or Semantic requirement. Accepting an AI recommendation keeps the field's default type; it does not promote a Reference or Permission into a Literal requirement. Explicit *must*, *only*, *exactly*, *verbatim*, *do not*, or `no-crop` wording may strengthen only the named property into the appropriate Literal or Semantic requirement. Accepting an AI recommendation keeps the field's default type; it does not promote a Reference or Permission into a Literal requirement.
@@ -279,25 +279,36 @@ owns SVG authoring under [`native-hyperlinks.md`](./native-hyperlinks.md).
|---|---|---| |---|---|---|
| `none` | No images | Data reports or process documentation whose visual burden is fully served by charts / native SVG | | `none` | No images | Data reports or process documentation whose visual burden is fully served by charts / native SVG |
| `provided` | User-provided assets | Existing images carry factual, brand, product, or narrative authority | | `provided` | User-provided assets | Existing images carry factual, brand, product, or narrative authority |
| `ai` | AI-generated | Invented or deliberately stylized illustrations, backgrounds, metaphors, or a coherent spot family are needed | | `ai` | AI-generated | Invented or deliberately stylized scenes, illustrations, backgrounds, metaphors, decorative lettering, or another generated visual treatment is needed |
| `web` | Web-sourced | A named or evidence-bearing real-world subject must appear as itself | | `web` | Web-sourced | A named or evidence-bearing real-world subject must appear as itself |
| `placeholder` | Deferred | The image is required but will be supplied later | | `placeholder` | Deferred | The image is required but will be supplied later |
**Current inventory**: If `images/` is non-empty, run `python3 scripts/analyze_images.py <project_path>/images` and read `analysis/image_analysis.csv` before recommending a source. Re-run after that folder changes. **Current inventory**: If `images/` is non-empty, run `python3 scripts/analyze_images.py <project_path>/images` and read `analysis/image_analysis.csv` before recommending a source. Re-run after that folder changes.
**Default — evidence before synthesis (may override when explicit source constraints or the communication intent require another permitted source)**: Prefer `provided` when supplied assets already carry authority. Propose `web` when the actual appearance of an externally verifiable subject is material; propose `ai` when custom expression matters more than documentary identity. Mixed sources may serve different page roles. This is a source-fit decision, not an image quota. The three Stage-2 style directions never settle it: a rendering candidate resolves how imagery looks, never whether an externally verifiable subject must appear as itself. **Hard rule**: Credentials do not decide image need. Missing `IMAGE_BACKEND`, host generation, or keyed stock-provider credentials never justifies `none` or deletion of a planned web-compatible role. Web search retains zero-config providers; an explicit generation-only requirement follows the normal Offline Manual boundary.
**Mandatory — proactive decorative-lettering scan**: Before each Stage-2 **Mandatory — no AI capability preflight**: When `recommend.image_usage` includes `ai`, preserve an explicit user path instruction; otherwise recommend `auto`. Do not inspect backend configuration, check host-tool availability, or probe a provider during planning. Generate Step 5 execution is the first capability check.
`recommend.image_usage`, treat a configured `IMAGE_BACKEND` or host-native image
generator as callable; Offline Manual, web, and vision-only access do not
qualify. If callable and the planned roster contains an exact stable string
suited to illustrative lettering anywhere in the deck — page role, length, and
kind of noun never filter candidates — include `ai` plus its role in
`image_notes.value` without waiting for a request. Never invent or rewrite copy
to trigger it. Explicit no-AI or editable-only requirements win. Execution
follows [`image-generator.md`](./image-generator.md) §7.
**Recommendation output**: Write `recommend.image_usage` as one source id or an array for mixed sources. Put page roles, authoritative assets, preferred/avoided imagery, and placeholder tolerance in `image_notes.value`. `none` is exclusive. Generic human-scale topics such as family life, education, wellness, or children lean `ai` when no supplied asset carries the story; regulated investor decks, B2B finance reports, and data-only dashboards remain eligible for `none` by judgment. **Default — visual grounding before `none` (may override when the user forbids images or charts / native SVG fully carry the visual burden)**: First decide whether the audience must recognize, experience, compare, or choose an externally verifiable subject, place, product, or setting. When yes, propose `provided` / `web`; propose `ai` when invented or deliberately stylized expression materially improves a planned visual job. Treat `none` as a positive whole-deck conclusion. Mixed sources may serve different page roles. The three Stage-2 style directions never settle source: a rendering candidate resolves how imagery looks, never whether a real subject must appear as itself.
**Mandatory — assess proactive decorative lettering without making eligibility
an automatic source trigger**: Before each Stage-2 `recommend.image_usage`,
scan the complete planned roster for exact stable display strings whose
artistic treatment could communicate better than native type. Page role,
character count, word count, line count, and kind of noun never filter
candidates: a complete long or
multi-line title is as eligible as a short mark. Never invent, rewrite,
shorten, or split copy to make generation easier. An eligible string is one
possible AI visual job, not a mechanical reason to add
`ai`. The second eligibility question already decides whether lettering
strengthens communication: when its answer is yes, lettering may be the sole
intended AI job and may support an AI recommendation stated in
`image_notes.value`; when either answer is no, native editable text remains
valid. The absence of another AI-image job never forces lettering. Explicit
no-AI or editable-only requirements win. Execution follows
[`image-generator.md`](./image-generator.md) §7.
**Recommendation output**: Write `recommend.image_usage` as one source id or an array for mixed sources. Put the intended communication jobs of each proposed source, authoritative assets, preferred/avoided imagery, and placeholder tolerance in `image_notes.value`. When `ai` is proposed, explain in editable natural language how generated visuals are expected to contribute and mention any materially anticipated illustration or lettering role. Keep the note an open strategy—not an enum, carrier allowlist, page-by-page assignment, count, or resource manifest; name exact pages/assets only when already authoritative or required. `none` is exclusive. Decks built around real-world recognition or choice, including travel itineraries, lean `provided` / `web`; generic human-scale topics such as family life, education, wellness, or children lean `ai` when no supplied asset carries the story and invented or stylized expression serves it. Regulated investor decks, B2B finance reports, and data-only dashboards remain eligible for `none` by judgment.
**Confirmed value wins**: Accept the confirmed legacy string or multi-select array. Map `ai→ai`, `web→web`, `provided→user`, and `placeholder→placeholder` into §VIII `Acquire Via`. Every direction already carries a rendering candidate whether or not AI is proposed; generated images inherit the deck colors and never introduce a second image-palette choice. **Confirmed value wins**: Accept the confirmed legacy string or multi-select array. Map `ai→ai`, `web→web`, `provided→user`, and `placeholder→placeholder` into §VIII `Acquire Via`. Every direction already carries a rendering candidate whether or not AI is proposed; generated images inherit the deck colors and never introduce a second image-palette choice.
@@ -316,12 +327,15 @@ implementation parameters. Executor may adapt/decline the
two non-literal suggestions while preserving content and intent; explicit two non-literal suggestions while preserving content and intent; explicit
user/template requirements bind. user/template requirements bind.
**Reference — carriers compose, not compete**: Any page may combine a suitable subset of background paint, native shapes, editable text, photos/scenes, transparent illustration elements, decorative lettering, icons, and visualizations. Select only what improves communication and composition; outside explicit requirements, no carrier is mandatory or mutually exclusive.
| Capability | Opportunity signal | Design Spec handoff | | Capability | Opportunity signal | Design Spec handoff |
|---|---|---| |---|---|---|
| Image composition | Image-as-canvas, editorial crop, collage, cutout, or meaningful focus / comparison / evidence units carry the page better than an adjacent rectangle | Propose a permitted source; when selected, apply the already-loaded [`strategist-image.md`](./strategist-image.md) resource contract plus the conditional image-layout references, record a concise §VIII `Layout pattern` suggestion, and describe page-level image/overlay relationships in §IX `Layout` / `Images` | | Image composition | Image-as-canvas, editorial crop, collage, cutout, or meaningful focus / comparison / evidence units carry the page better than an adjacent rectangle | Propose a permitted source; when selected, apply the already-loaded [`strategist-image.md`](./strategist-image.md) resource contract plus the conditional image-layout references, record a concise §VIII `Layout pattern` suggestion, and describe page-level image/overlay relationships in §IX `Layout` / `Images` |
| Composable illustration family | One or more pages benefit from coherent reusable title/corner ornaments, dominant anchors, supporting figures, or accents that can mix with text, shapes, photos, or lettering | Apply [`strategist-image.md`](./strategist-image.md): plan transparent illustration elements by compatible family, record fixed reuse or adaptive variation in their §VIII `Reference`, and describe each used page's carrier relationships in §IX `Layout` / `Images` |
| Native paint / overlay | Gradient, translucency, scrim, vignette, or wash supports focus, hierarchy, depth, legibility, or image integration | Record purpose/layering in §IX `Layout`, plus `Images` when imagery participates; no new field or type/stops/opacity/coordinates—Executor chooses realization | | Native paint / overlay | Gradient, translucency, scrim, vignette, or wash supports focus, hierarchy, depth, legibility, or image integration | Record purpose/layering in §IX `Layout`, plus `Images` when imagery participates; no new field or type/stops/opacity/coordinates—Executor chooses realization |
| Native shape / Merge Shapes | A literal Office symbol, a stock bent/curved relationship contour, or a compound silhouette, negative-space cutout, overlap-only region, or meaningful fragmentation strengthens the visual idea | Add an optional §IX `Native shape suggestion` with the semantic result plus a candidate preset/Connector family or Boolean operation/operands | | Native shape / Merge Shapes | A literal Office symbol, a stock bent/curved relationship contour, or a compound silhouette, negative-space cutout, overlap-only region, or meaningful fragmentation strengthens the visual idea | Add an optional §IX `Native shape suggestion` with the semantic result plus a candidate preset/Connector family or Boolean operation/operands |
| AI decorative lettering asset | Any stable display string in the deck — cover hook, chapter word, place or product name, dish or exhibit name, year, hero number, pull quote, motif word — reads better with a material, dimensional, hand-rendered, or otherwise illustrative treatment than as ordinary text | Apply [`strategist-image.md`](./strategist-image.md): when compatible, plan one unplaced AI Illustration Sheet plus one transparent `slice` row per used lettering element; record every exact string, and keep subtitle/chrome/body as native text. A display wordmark and an editable page title may coexist | | AI decorative lettering asset | Any stable display string in the deck — including a complete long or multi-line title, cover hook, chapter word, place or product name, dish or exhibit name, year, hero number, pull quote, or motif word — reads better with a material, dimensional, hand-rendered, or otherwise illustrative treatment than as ordinary text | Apply [`strategist-image.md`](./strategist-image.md): preserve every complete exact string, group compatible marks by visual family, state their role/context/relative visual weight/energy without fixing an effect recipe, plan one unplaced AI Illustration Sheet per family plus one transparent `slice` row per used mark, and keep chrome/body as native text. The lettering asset may carry the complete long or multi-line title as its display layer; keep an ordinary native title/subtitle in a separate text frame wherever the page needs a searchable, selectable, or outline-visible heading. Never shorten copy to make it look more like a wordmark |
| Page transition | A section/state change, spatial continuity, recorded/self-running flow, or the same semantic object changing position, scale, crop, or state across adjacent pages benefits from motion | Add an optional §IX `Motion suggestion` describing the communication job and any continuing object's initial state → action → end state; leave effect, ids, pairing names, and timing to Executor | | Page transition | A section/state change, spatial continuity, recorded/self-running flow, or the same semantic object changing position, scale, crop, or state across adjacent pages benefits from motion | Add an optional §IX `Motion suggestion` describing the communication job and any continuing object's initial state → action → end state; leave effect, ids, pairing names, and timing to Executor |
| Object animation | Progressive reveal, emphasis, movement, removal, or deliberate stillness clarifies sequence, causality, comparison, hierarchy, narration order, full-view → detail, atmosphere → evidence, or hotspot/annotation order | Add an optional §IX `Motion suggestion` naming each relevant semantic unit's lifecycle duty and initial state → communication action → end state, plus any meaningful order/relationship; leave group ids, effects, options, and timing to Executor | | Object animation | Progressive reveal, emphasis, movement, removal, or deliberate stillness clarifies sequence, causality, comparison, hierarchy, narration order, full-view → detail, atmosphere → evidence, or hotspot/annotation order | Add an optional §IX `Motion suggestion` naming each relevant semantic unit's lifecycle duty and initial state → communication action → end state, plus any meaningful order/relationship; leave group ids, effects, options, and timing to Executor |
@@ -458,15 +472,16 @@ Lock the stable role set the deck needs, including recurring neutrals such as `s
| Core + surrounding forces | center-radiating or hub-spoke | | Core + surrounding forces | center-radiating or hub-spoke |
| Wide visual + explanation | top-bottom split | | Wide visual + explanation | top-bottom split |
**Default — define one cross-page visual motif when it can carry identity or **Default — define a cross-page visual motif system when it can carry identity or
meaning (may omit when restraint serves the deck better)**: after the complete meaning (may omit when restraint serves the deck better)**: after the complete
§IX roster and planned visual resources are known, choose or inherit one reusable §IX roster and planned visual resources are known, choose or inherit a reusable
page-scale geometry or material gesture—such as a directional contour, opening, motif or coherent element family—such as title/corner ornaments, a directional
line lattice, or oversized numeral. Fold its recognizable invariant and allowed contour, opening, line lattice, or oversized numeral.
variation (scale, crop, density, position, content interaction) into the Fold its invariant and reuse mode into §III `Theme`: exact repetition is valid
existing §III `Theme`, and mention it only in §IX `Layout` blocks that use it. for deliberate recurring chrome, while page-specific motifs may vary scale,
Vary it by page role instead of copying one ornament; create no motif field or crop, density, position, and content interaction. Mention it only in §IX
lock row. This is a continuity Reference, not a decoration quota. `Layout` blocks that use it; create no motif field or lock row. This is a
continuity Reference, not a decoration quota.
On PPT 16:9, start from a 1200×640 safe area with 40px outer margins, then adapt to content. Template workspaces may supply different geometry; when active, [`strategist-template.md`](./strategist-template.md) owns precedence. On PPT 16:9, start from a 1200×640 safe area with 40px outer margins, then adapt to content. Template workspaces may supply different geometry; when active, [`strategist-template.md`](./strategist-template.md) owns precedence.
@@ -33,7 +33,7 @@ and filter/clip contracts.
| **Needs-Selection** | Web search produced one bounded thumbnail-only candidate page; no original or provenance exists yet | Step 5 reviews/promotes one candidate, advances to `next_candidate_page`, or after pool exhaustion materially changes the query and returns the row to `Pending`; Executor must never consume this intermediate state | | **Needs-Selection** | Web search produced one bounded thumbnail-only candidate page; no original or provenance exists yet | Step 5 reviews/promotes one candidate, advances to `next_candidate_page`, or after pool exhaustion materially changes the query and returns the row to `Pending`; Executor must never consume this intermediate state |
| **Generated** | AI/slice output exists | Reference from `../images/`; manifest records govern attribution. An `Illustration Sheet` stays in §VIII only as an unplaced slice source | | **Generated** | AI/slice output exists | Reference from `../images/`; manifest records govern attribution. An `Illustration Sheet` stays in §VIII only as an unplaced slice source |
| **Sourced** | Web-sourced file exists at expected path | Reference from `../images/`; check `image_sources.json` for `license_tier` — if `attribution-required`, render an inline credit element on the slide (see [`executor-web-image.md`](./executor-web-image.md) §1 and [`image-searcher.md`](./image-searcher.md) §7 for the attribution contract) | | **Sourced** | Web-sourced file exists at expected path | Reference from `../images/`; check `image_sources.json` for `license_tier` — if `attribution-required`, render an inline credit element on the slide (see [`executor-web-image.md`](./executor-web-image.md) §1 and [`image-searcher.md`](./image-searcher.md) §7 for the attribution contract) |
| **Needs-Manual** | Automatic acquisition is unavailable/exhausted or the selected path requires manual fulfillment; for `slice`, the parent sheet is unavailable | Default Generate may use a dashed placeholder until its readiness gate. Quick Generate blocks every required row still in this status, even if an unverified candidate file exists; validate a supplied replacement and reconcile it to `Existing`, `Generated`, or `Sourced` first. For `slice`, supply the parent sheet and rerun `slice_images.py`; do not hand-place individual element files. | | **Needs-Manual** | The owning source path requires manual fulfillment; for `slice`, the parent sheet is unavailable | Default Generate may use a dashed placeholder until its readiness gate. Quick Generate blocks every required row still in this status, even if an unverified candidate file exists; validate a supplied replacement and reconcile it to `Existing`, `Generated`, or `Sourced` first. Quick automated AI exhaustion never creates this status: [`image-generator.md`](./image-generator.md) §7 removes the affected AI/dependent-slice jobs through its declared no-AI replan. For a retained manual `slice`, supply the parent sheet and rerun `slice_images.py`; do not hand-place individual element files. |
| **Existing** | User already has image (`Acquire Via: user`) | Place in `images/`, reference with `<image>` | | **Existing** | User already has image (`Acquire Via: user`) | Place in `images/`, reference with `<image>` |
| **Placeholder** | Intentionally not prepared yet (`Acquire Via: placeholder`) | Dashed border placeholder; replace later | | **Placeholder** | Intentionally not prepared yet (`Acquire Via: placeholder`) | Dashed border placeholder; replace later |
@@ -48,7 +48,7 @@ and filter/clip contracts.
2. Prepare project-local resources before SVG authoring: 2. Prepare project-local resources before SVG authoring:
- user → materialize the explicit source under project/images/ → Existing - user → materialize the explicit source under project/images/ → Existing
- Pending prepared derivative → follow [`image-base.md`](./image-base.md) §3 before ordinary `Acquire Via` dispatch - Pending prepared derivative → follow [`image-base.md`](./image-base.md) §3 before ordinary `Acquire Via` dispatch
- Pending / Failed + ai → Image_Generator runs image_gen.py → Generated - Pending / Failed + ai → Image_Generator executes the selected path → Generated, Default recovery decision, or Quick no-AI replan
- Pending / Failed + web + vision → Image_Searcher saves at most 8 ranked previews → Needs-Selection → promote one original or fetch the next page → Sourced / Needs-Manual - Pending / Failed + web + vision → Image_Searcher saves at most 8 ranked previews → Needs-Selection → promote one original or fetch the next page → Sourced / Needs-Manual
- Pending / Failed + web without vision → Image_Searcher accepts only a strict metadata-ranked best-only candidate and records that method → Sourced or Needs-Manual - Pending / Failed + web without vision → Image_Searcher accepts only a strict metadata-ranked best-only candidate and records that method → Sourced or Needs-Manual
- Pending + slice → after parent AI sheet is Generated, slice_images.py cuts element files → Generated - Pending + slice → after parent AI sheet is Generated, slice_images.py cuts element files → Generated
@@ -14,7 +14,7 @@ Each style keeps its own authoritative file with: shape & decoration, typography
> The **`visual_style` value is only ever a first-column `id`** (`swiss-minimal`, `editorial`, …). The "Paired rendering" column lists **image-rendering** names (`flat`, `minimalist-swiss`, `digital-dashboard`, …) — never treat one of those as the `visual_style`. Default records rendering under confirmation h; Quick keeps the selected rendering only in active context and any required image manifest. > The **`visual_style` value is only ever a first-column `id`** (`swiss-minimal`, `editorial`, …). The "Paired rendering" column lists **image-rendering** names (`flat`, `minimalist-swiss`, `digital-dashboard`, …) — never treat one of those as the `visual_style`. Default records rendering under confirmation h; Quick keeps the selected rendering only in active context and any required image manifest.
> >
> The **`Illus.`** column is each style's spot-illustration propensity — `core` (illustration is intrinsic to the look), `supportive` (use where it lifts, restrained), or `sparse` (the style's lead visual competes; default off). It sets the **default lean** only, for when the user gives no steer: an explicit user request to use / skip illustrations overrides it either way, and `image_usage: none` always writes no illustration rows. Full per-style rule in each file's §6. > The **`Illus.`** column is each style's illustration propensity — `core` (illustration is intrinsic to the look), `supportive` (illustration can share the composition), or `sparse` (use selectively so the style's lead visual remains clear). It tunes centrality and recurrence, never eligible page types, element scale, or carrier combinations. An explicit user request to use / skip illustrations overrides it either way, and `image_usage: none` always writes no illustration rows. Full per-style rule in each file's §6.
### 1.1 Corporate / product ### 1.1 Corporate / product
@@ -31,7 +31,7 @@ Each style keeps its own authoritative file with: shape & decoration, typography
| Visual style | Character | Best for | Paired rendering | Illus. | | Visual style | Character | Best for | Paired rendering | Illus. |
|---|---|---|---|---| |---|---|---|---|---|
| [`editorial`](./editorial.md) | Magazine hierarchy, rules & columns, serif/sans interplay | Finance, journalism, analysis, explainers | `editorial` | supportive | | [`editorial`](./editorial.md) | Magazine hierarchy, rules & columns, serif/sans interplay | Finance, journalism, analysis, explainers | `editorial` | supportive |
| [`photo-editorial`](./photo-editorial.md) | Full-bleed photography dominates, text points & captions | Architecture, design, fashion, culture, photo-led | `corporate-photo` | sparse | | [`photo-editorial`](./photo-editorial.md) | Full-bleed photography dominates, text points & captions | Architecture, design, fashion, culture, travel / destination, photo-led | `corporate-photo` | sparse |
| [`data-journalism`](./data-journalism.md) | Multi-column micro-charts, sidebars, source lines, dense | Finance, market reviews, research, data reports | `editorial` | sparse | | [`data-journalism`](./data-journalism.md) | Multi-column micro-charts, sidebars, source lines, dense | Finance, market reviews, research, data reports | `editorial` | sparse |
| [`brutalist`](./brutalist.md) | Newsprint density, ruled boxes, raw structure, flat | Annual reviews, research digests, manifestos | `screen-print` / `editorial` | supportive | | [`brutalist`](./brutalist.md) | Newsprint density, ruled boxes, raw structure, flat | Annual reviews, research digests, manifestos | `screen-print` / `editorial` | supportive |
@@ -41,7 +41,7 @@ Each style keeps its own authoritative file with: shape & decoration, typography
|---|---|---|---|---| |---|---|---|---|---|
| [`memphis`](./memphis.md) | Clashing color blocks, geometric confetti, bold outlines | Festivals, consumer, youth, launch hype | `flat` | core | | [`memphis`](./memphis.md) | Clashing color blocks, geometric confetti, bold outlines | Festivals, consumer, youth, launch hype | `flat` | core |
| [`zine`](./zine.md) | Riso misregistration, halftone, limited palette, print grit | Culture, design talks, indie brands | `screen-print` | core | | [`zine`](./zine.md) | Riso misregistration, halftone, limited palette, print grit | Culture, design talks, indie brands | `screen-print` | core |
| [`vintage-poster`](./vintage-poster.md) | Mid-century flat blocks, halftone, retro-geometric warmth | Heritage, hospitality, cultural, anniversaries | `vintage-poster` | core | | [`vintage-poster`](./vintage-poster.md) | Mid-century flat blocks, halftone, retro-geometric warmth | Heritage brands, historic hospitality identities, cultural retrospectives, anniversaries | `vintage-poster` | core |
| [`paper-cut`](./paper-cut.md) | Layered cut-paper sheets, soft inter-layer shadow, tactile | Cultural / folk, children, festival, sustainability | `paper-cut` | core | | [`paper-cut`](./paper-cut.md) | Layered cut-paper sheets, soft inter-layer shadow, tactile | Cultural / folk, children, festival, sustainability | `paper-cut` | core |
### 1.4 Hand-drawn / brush ### 1.4 Hand-drawn / brush
@@ -61,13 +61,16 @@ Each style keeps its own authoritative file with: shape & decoration, typography
--- ---
## 2. Auto-selection — content vibe / industry → style ## 2. Selection recall — communication task + content signal → style
**Reference — not a constraint**: Resolve the audience task, outcome, delivery context, required carriers, and artifact afterlife before using this table. A topic or industry word alone never selects a style. Reject a candidate that weakens the audience's practical task or cannot integrate required real-world imagery. A travel itinerary or destination decision aid is not retro merely because its subject is cultural or hospitality-related; choose retro only when nostalgia / heritage storytelling or an explicit user direction makes it part of the communication job.
| Signal | Recommended style | Alternates | | Signal | Recommended style | Alternates |
|---|---|---| |---|---|---|
| High-end consulting / architecture / luxury / minimal | `swiss-minimal` | `editorial` | | High-end consulting / architecture / luxury / minimal | `swiss-minimal` | `editorial` |
| Finance / journalism / research / long-form analysis | `editorial` | `data-journalism` | | Finance / journalism / research / long-form analysis | `editorial` | `data-journalism` |
| Photography-led / architecture / design / fashion / 大图 | `photo-editorial` | `editorial` | | Photography-led / architecture / design / fashion / 大图 | `photo-editorial` | `editorial` |
| Itinerary / destination guide / trip planning / 旅游计划 / 行程 | `photo-editorial` | `editorial`, `soft-rounded` |
| Data report / market review / 财经 / Bloomberg / Economist | `data-journalism` | `editorial` | | Data report / market review / 财经 / Bloomberg / Economist | `data-journalism` | `editorial` |
| Product / SaaS / training / consumer / friendly | `soft-rounded` | `editorial` | | Product / SaaS / training / consumer / friendly | `soft-rounded` | `editorial` |
| Modern SaaS / fintech / health-tech / premium app | `glassmorphism` | `dark-tech` | | Modern SaaS / fintech / health-tech / premium app | `glassmorphism` | `dark-tech` |
@@ -77,7 +80,7 @@ Each style keeps its own authoritative file with: shape & decoration, typography
| Annual review / manifesto / max-density editorial | `brutalist` | `editorial` | | Annual review / manifesto / max-density editorial | `brutalist` | `editorial` |
| Festival / consumer brand / youth / loud launch | `memphis` | `soft-rounded` | | Festival / consumer brand / youth / loud launch | `memphis` | `soft-rounded` |
| Indie publishing / design / culture / printed feel | `zine` | `editorial` | | Indie publishing / design / culture / printed feel | `zine` | `editorial` |
| Heritage / hospitality / retro brand / 老字号 / 周年 | `vintage-poster` | `zine` | | Heritage brand / historic hospitality identity / retro brand / 老字号 / 周年 | `vintage-poster` | `zine` |
| Cultural / folk / festival / children / sustainability | `paper-cut` | `sketch-notes` | | Cultural / folk / festival / children / sustainability | `paper-cut` | `sketch-notes` |
| Education / training / onboarding / 教学 | `sketch-notes` | `paper-cut` | | Education / training / onboarding / 教学 | `sketch-notes` | `paper-cut` |
| Methodology / before-after / manifesto / 方法论 | `ink-notes` | `editorial` | | Methodology / before-after / manifesto / 方法论 | `ink-notes` | `editorial` |
@@ -35,4 +35,4 @@ Engineering schematic — thin line work on dark blueprint paper, isometric proj
## 6. Illustration propensity ## 6. Illustration propensity
**supportive** — but note the natural illustration here is hand-drawn **SVG schematic line-work / annotated marks**, not raster spot sheets; reach for line diagrams over decorative cutouts. If raster spots are used at all, keep them schematic, sparse, and secondary to the SVG line work. With no user steer this is the default lean; an explicit user request wins either way, and `image_usage: none` writes no illustration rows. **supportive** — the natural illustration language is schematic line-work, annotated marks, and blueprint-aligned transparent elements, whether authored in SVG or generated and sliced. Choose the carrier that composes cleanly with the page. With no user steer this is the default lean; role, scale, reuse, and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -36,4 +36,4 @@ Brutalist editorial newspaper. Wall-to-wall small type, irregular column widths,
## 6. Illustration propensity ## 6. Illustration propensity
**supportive** — raw halftone cuts fit the newsprint density, but structure leads; use them sparingly, only where a page needs a visual jolt. With no user steer this is the default lean; an explicit user request wins either way, and `image_usage: none` writes no illustration rows. **supportive** — raw halftone cuts and illustration elements can anchor or disrupt the newsprint composition while structure remains legible. With no user steer this is the default lean; role, scale, reuse, and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -34,4 +34,4 @@ Classroom chalkboard — a dark slate field, soft chalk-stroke line work, powder
## 6. Illustration propensity ## 6. Illustration propensity
**core** — chalk-drawn marks and doodles are the classroom aesthetic; with no user steer, default to recommending a coherent spot-illustration family. Default *lean* only — heaviness and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows. **core** — chalk-drawn marks and doodles are the classroom aesthetic; with no user steer, default to recommending a coherent composable illustration family. Default *lean* only — role, scale, reuse, and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -35,4 +35,4 @@ Dark canvas, luminous accents, geometric precision. For tech, AI, dev tools, dat
## 6. Illustration propensity ## 6. Illustration propensity
**sparse** — glow accents and geometric precision lead, and raster spots tend to read as clip-art against the dark UI. With no user steer, default to none. If the user explicitly asks, prefer restrained geometric marks over illustrated characters. `image_usage: none` writes no illustration rows. **sparse** — glow accents and geometric precision lead. Use illustration selectively as a focal or supporting layer that belongs to the dark-tech system rather than generic clip-art; form and scale follow the page job. An explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -34,4 +34,4 @@ Bloomberg / Economist news-infographic — publication-grade information density
## 6. Illustration propensity ## 6. Illustration propensity
**sparse** — charts, sidebars, and data density carry the page; decorative spots undercut the serious data-publication tone. With no user steer, default to none. If the user explicitly asks, keep them minimal and journalistic. `image_usage: none` writes no illustration rows. **sparse** — charts, sidebars, and evidence lead. Use illustration selectively for orientation, narrative framing, or emphasis without obscuring values or source authority; form and scale follow the page job. An explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -36,4 +36,4 @@ Magazine-grade hierarchy. Columns, hairline rules, a serif / sans interplay, str
## 6. Illustration propensity ## 6. Illustration propensity
**supportive** — editorial spot illustrations beside articles are classic here; use them where they lift a section or break text density, kept restrained. With no user steer this is the default lean; an explicit user request wins either way, and `image_usage: none` writes no illustration rows. **supportive** — editorial illustration elements can anchor, frame, pace, or clarify a page while typography and evidence retain authority. With no user steer this is the default lean; role, scale, reuse, and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -34,4 +34,4 @@ Frosted-glass SaaS — translucent layered panels, flowing gradient light, float
## 6. Illustration propensity ## 6. Illustration propensity
**sparse** — translucent panels, gradient light, and floating depth carry the look; decorative spots compete with the glass. With no user steer, default to none. If the user explicitly asks, keep them minimal and let them sit behind / under the glass rather than on top. `image_usage: none` writes no illustration rows. **sparse** — translucent panels, gradient light, and floating depth lead. Use illustration selectively within, behind, beside, or across the glass layers when it improves composition without obscuring their hierarchy. An explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -35,4 +35,4 @@ Whiteboard-ink minimalism — a pale field, confident black hand-ink line work,
## 6. Illustration propensity ## 6. Illustration propensity
**supportive** — the style is already sparse and considered; a few hand-ink spots fit, but restraint is its temperament — use only where one genuinely earns its place. With no user steer this is the default lean; an explicit user request wins either way, and `image_usage: none` writes no illustration rows. **supportive** — hand-ink illustration elements can anchor, annotate, or connect a composition while the style's considered restraint remains intact. With no user steer this is the default lean; role, scale, reuse, and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -34,4 +34,4 @@ New-Chinese ink-wash — a rice-paper field, vast literati whitespace, restraine
## 6. Illustration propensity ## 6. Illustration propensity
**supportive** — a single brush mark or seal can anchor a page, but stillness and whitespace lead; use very few, very quiet. With no user steer this is the default lean; an explicit user request wins either way, and `image_usage: none` writes no illustration rows. **supportive** — brush, seal, or painted illustration elements can anchor or frame a page while stillness and whitespace lead. With no user steer this is the default lean; role, scale, reuse, and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -35,4 +35,4 @@ Memphis / Pop — clashing color blocks, geometric confetti, bold outlines, 80s-
## 6. Illustration propensity ## 6. Illustration propensity
**core** — bold-outline spots, confetti, and geometric shapes *are* the Memphis language; with no user steer, default to recommending a coherent spot-illustration family. Default *lean* only — heaviness and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows. **core** — bold-outline illustration elements, confetti, and geometric shapes *are* the Memphis language; with no user steer, default to recommending a coherent composable illustration family. Default *lean* only — role, scale, reuse, and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -34,4 +34,4 @@ Layered paper-craft — scissor-cut shapes stacked in tactile layers, soft shado
## 6. Illustration propensity ## 6. Illustration propensity
**core** — layered cut-paper elements *are* the style, and the slicer's transparent cutouts match it natively; with no user steer, default to recommending a coherent spot-illustration family. Default *lean* only — heaviness and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows. **core** — layered cut-paper elements *are* the style, and transparent slices match it natively; with no user steer, default to recommending a coherent composable illustration family. Default *lean* only — role, scale, reuse, and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -36,4 +36,4 @@ Photo-led editorial — large full-bleed photography dominates the page, text po
## 6. Illustration propensity ## 6. Illustration propensity
**sparse** — photography *is* the visual; illustration competes with the hero image. With no user steer, default to none. If the user explicitly asks, keep them to tiny captional marks that never rival the photo. `image_usage: none` writes no illustration rows. **sparse** — photography leads. Use illustration selectively when it frames, connects, or extends the photographic composition without displacing factual or visual authority; form and scale follow the page job. An explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -34,4 +34,4 @@
## 6. Illustration propensity ## 6. Illustration propensity
**core** — pixel sprites and blocky icons *are* the style; with no user steer, default to recommending a coherent spot-illustration family. Default *lean* only — heaviness and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows. **core** — pixel sprites and blocky forms *are* the style; with no user steer, default to recommending a coherent composable illustration family. Default *lean* only — role, scale, reuse, and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -34,4 +34,4 @@ Warm hand-drawn sketchnote — soft paper field, black ink doodle line work, gen
## 6. Illustration propensity ## 6. Illustration propensity
**core** — doodle spots and hand-drawn marks are intrinsic here, not optional garnish; with no user steer, default to recommending a coherent spot-illustration family (the decoration this style is built on). This sets the default *lean* only: heaviness and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows. **core** — doodles and hand-drawn illustration elements are intrinsic here, not optional garnish; with no user steer, default to recommending a coherent composable illustration family. This sets the default *lean* only: role, scale, reuse, and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -35,4 +35,4 @@ Approachable and modern. Rounded cards, gentle elevation, friendly rhythm. For p
## 6. Illustration propensity ## 6. Illustration propensity
**supportive** — friendly rounded spots suit this approachable style; use them where they lift rhythm or section warmth, kept restrained. With no user steer this is the default lean; an explicit user request wins either way, and `image_usage: none` writes no illustration rows. **supportive** — friendly rounded illustration elements can carry anchors, support, rhythm, or warmth across the page system. With no user steer this is the default lean; role, scale, reuse, and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -36,4 +36,4 @@ Strict Swiss-grid discipline. Modular grid, sharp geometry, aggressive whitespac
## 6. Illustration propensity ## 6. Illustration propensity
**sparse** — the whitespace and grid *are* the design; decorative spots compete with that restraint, so with no user steer, default to none. If the user explicitly asks, use them — but stay austere: very few, very light, never crowding the negative space. `image_usage: none` writes no illustration rows. **sparse** — whitespace and grid lead. Use illustration selectively when it sharpens hierarchy or creates a deliberate counterpoint without eroding negative space; scale and placement follow the page composition. An explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -34,4 +34,4 @@ Mid-century print poster (1950s1970s) — bold rounded-geometric shapes, limi
## 6. Illustration propensity ## 6. Illustration propensity
**core** — mid-century spot motifs and halftone cuts are intrinsic to the poster look; with no user steer, default to recommending a coherent spot-illustration family. Default *lean* only — heaviness and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows. **core** — mid-century illustration motifs and halftone cuts are intrinsic to the poster look; with no user steer, default to recommending a coherent composable illustration family. Default *lean* only — role, scale, reuse, and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -35,4 +35,4 @@ Risograph zine / DIY poster — misregistered color layers, halftone dots, a tig
## 6. Illustration propensity ## 6. Illustration propensity
**core** — riso / halftone spot imagery is part of the zine's print grit; with no user steer, default to recommending a coherent spot-illustration family. Default *lean* only — heaviness and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows. **core** — riso / halftone illustration and cutout imagery is part of the zine's print grit; with no user steer, default to recommending a coherent composable illustration family. Default *lean* only — role, scale, reuse, and placement stay Strategist judgment, an explicit user request wins either way, and `image_usage: none` writes no illustration rows.
@@ -1440,11 +1440,15 @@
"image_ai_path": [ "image_ai_path": [
{ {
"id": "auto", "id": "auto",
"label": "Automatic Path A → Path B → Offline Manual", "label": "Automatic Path A → Path B",
"label_zh": "自动选择", "label_zh": "自动生成(接口 → 宿主工具)",
"label_zh_tw": "自動選擇", "label_zh_tw": "自動生成(API → 宿主工具)",
"label_en": "Automatic Path A → Path B → Offline Manual", "label_en": "Automatic Path A → Path B",
"label_ja": "自動選択Path A → Path B → オフライン手動" "label_ja": "自動生成Path A → Path B",
"desc_zh": "两条自动路径都不可用或失败时暂停,由你选择修复后重试、手动生成或取消受影响的 AI 图;不会自动转为手动。",
"desc_zh_tw": "兩條自動路徑都不可用或失敗時暫停,由你選擇修復後重試、手動生成或取消受影響的 AI 圖;不會自動轉為手動。",
"desc_en": "If both automated paths are unavailable or fail, pause for your choice: repair and retry, generate manually, or cancel the affected AI images. Manual is never assumed.",
"desc_ja": "両方の自動経路が利用不能または失敗した場合は停止し、修復後の再試行、手動生成、影響を受けたAI画像の取り消しから選択します。手動へ自動移行しません。"
}, },
{ {
"id": "api", "id": "api",
@@ -1477,10 +1481,10 @@
"label_zh_tw": "離線手動", "label_zh_tw": "離線手動",
"label_en": "Offline Manual", "label_en": "Offline Manual",
"label_ja": "オフライン手動", "label_ja": "オフライン手動",
"desc_zh": "机制:写入图片提示词文件;用户在外部生成放入项目图片目录。", "desc_zh": "写入图片提示词;你在外部生成放入项目图片目录。必需图片齐全并验证前不允许导出。",
"desc_zh_tw": "機制:寫入圖片提示詞檔案;使用者在外部生成放入專案圖片目錄。", "desc_zh_tw": "寫入圖片提示詞;你在外部生成放入專案圖片目錄。必要圖片齊全並驗證前不允許匯出。",
"desc_en": "Prompts written to images/image_prompts.json; user generates externally and places files in project/images/.", "desc_en": "Prompts are written for external generation and placement in project/images/. Export stays blocked until every required file is present and validated.",
"desc_ja": "images/image_prompts.jsonにプロンプトを書き出し、ユーザーが外部で生成した画像をproject/images/に配置する方式。" "desc_ja": "外部生成用のプロンプトを書き出し、画像をproject/images/に配置します。必要な全ファイルの配置と検証が済むまでエクスポートできません。"
} }
], ],
"generation_mode": [ "generation_mode": [
@@ -34,10 +34,11 @@ Generate checks `design_spec.md §I / AI Image Acquisition Path` before manifest
mode: only `api` / `auto` permits Path A; a missing or unknown value fails mode: only `api` / `auto` permits Path A; a missing or unknown value fails
closed and returns to Step 4 recovery. Quick Generate has no Design Spec: use closed and returns to Step 4 recovery. Quick Generate has no Design Spec: use
the explicit active-context path when supplied, otherwise `auto` selects the the explicit active-context path when supplied, otherwise `auto` selects the
A → B → C chain defined in A → B chain defined in
[`image-generator.md`](../../references/image-generator.md) §7 without asking. [`image-generator.md`](../../references/image-generator.md) §7 without asking;
In either profile, `host-native` uses the host image tool directly and `manual` exhausted automation triggers Quick's no-AI replan rather than Offline Manual.
uses the read-only Markdown sidecar. In either profile, `host-native` uses the host image tool directly and an
explicit `manual` choice uses the read-only Markdown sidecar.
```bash ```bash
python3 scripts/image_gen.py "A modern futuristic workspace" python3 scripts/image_gen.py "A modern futuristic workspace"
@@ -151,8 +152,9 @@ Create a non-destructive PNG derivative from one bitmap already prepared under
`<project_path>/images/`. Use this only when a slide needs a baked bitmap effect; `<project_path>/images/`. Use this only when a slide needs a baked bitmap effect;
crop, mask, rotation, mirror, opacity, shadow, scrim, outline, and overlap remain crop, mask, rotation, mirror, opacity, shadow, scrim, outline, and overlap remain
native SVG/PPT treatments. This tool does not perform semantic background native SVG/PPT treatments. This tool does not perform semantic background
removal: use `slice_images.py --alpha` for flat-color keys, an already prepared removal: use `slice_images.py --alpha --bg <key> --strict-alpha` for flat-color
RGBA asset or the active host image editor for a standalone cutout, and keys (a pure red/green/blue key also recovers soft alpha and removes spill), an
already prepared RGBA asset or the active host image editor for a standalone cutout, and
[`image-generator.md`](../../references/image-generator.md) §4.4 only for [`image-generator.md`](../../references/image-generator.md) §4.4 only for
registered subject/base layers. registered subject/base layers.
@@ -218,33 +218,6 @@ def _has_usable_import(summary: dict[str, list[str]]) -> bool:
) )
def _research_source_urls(path: Path) -> list[str]:
"""Return unique source URLs from a v1 topic-research provenance file."""
if not path.is_file() or not path.name.endswith(".facts.json"):
return []
try:
payload = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
raise RuntimeError(f"Fact provenance is unreadable: {path} ({exc})") from exc
if not isinstance(payload, dict):
raise RuntimeError(f"Fact provenance must be a JSON object: {path}")
if payload.get("schema") != "ppt-master.fact-provenance.v1":
return []
facts = payload.get("facts")
if not isinstance(facts, list):
raise RuntimeError(f"Fact provenance facts must be an array: {path}")
urls: list[str] = []
for index, fact in enumerate(facts):
if not isinstance(fact, dict) or not is_url(str(fact.get("source_url", ""))):
raise RuntimeError(
f"Fact provenance facts[{index}] has no valid source_url: {path}"
)
url = fact["source_url"]
if url not in urls:
urls.append(url)
return urls
class ProjectManager: class ProjectManager:
"""Create, inspect, validate, and populate project folders.""" """Create, inspect, validate, and populate project folders."""
@@ -479,14 +452,11 @@ class ProjectManager:
self, self,
url: str, url: str,
markdown_path: Path, markdown_path: Path,
*,
download_images: bool = True,
) -> None: ) -> None:
route = build_conversion_command( route = build_conversion_command(
url, url,
markdown_path, markdown_path,
forced_type="web", forced_type="web",
extra_args=[] if download_images else ["--no-images"],
) )
self._run_tool(route.command) self._run_tool(route.command)
@@ -794,9 +764,6 @@ class ProjectManager:
"assets": [], "assets": [],
"images": [], "images": [],
"analysis": [], "analysis": [],
"research_sources_expected": [],
"research_sources_imported": [],
"research_sources_failed": [],
"notes": [], "notes": [],
"skipped": [], "skipped": [],
} }
@@ -823,20 +790,6 @@ class ProjectManager:
continue continue
expanded_items.append(item) expanded_items.append(item)
research_urls: list[str] = []
for item in expanded_items:
if is_url(item):
continue
for url in _research_source_urls(Path(item).expanduser()):
if url not in research_urls:
research_urls.append(url)
summary["research_sources_expected"] = research_urls.copy()
explicit_urls = {item for item in expanded_items if is_url(item)}
expanded_items.extend(url for url in research_urls if url not in explicit_urls)
research_url_set = set(research_urls)
text_only_research_urls = research_url_set - explicit_urls
imported_research_urls: set[str] = set()
explicit_markdown_stems = { explicit_markdown_stems = {
Path(item).stem Path(item).stem
for item in expanded_items for item in expanded_items
@@ -852,11 +805,7 @@ class ProjectManager:
sources_dir / f"{derive_url_basename(item)}.md" sources_dir / f"{derive_url_basename(item)}.md"
) )
try: try:
self._import_url( self._import_url(item, markdown_path)
item,
markdown_path,
download_images=item not in text_only_research_urls,
)
except Exception as exc: # pragma: no cover - summary path except Exception as exc: # pragma: no cover - summary path
archived = self._archive_url_record(sources_dir, item) archived = self._archive_url_record(sources_dir, item)
summary["url_records"].append(str(archived)) summary["url_records"].append(str(archived))
@@ -871,9 +820,6 @@ class ProjectManager:
continue continue
summary["markdown"].append(str(markdown_path)) summary["markdown"].append(str(markdown_path))
if item in research_url_set:
imported_research_urls.add(item)
if item not in text_only_research_urls:
self._propagate_companion_image_assets(markdown_path, project_dir) self._propagate_companion_image_assets(markdown_path, project_dir)
continue continue
@@ -1054,13 +1000,6 @@ class ProjectManager:
else: else:
summary["notes"].append(f"{item}: archived only, no automatic conversion") summary["notes"].append(f"{item}: archived only, no automatic conversion")
summary["research_sources_imported"] = [
url for url in research_urls if url in imported_research_urls
]
summary["research_sources_failed"] = [
url for url in research_urls if url not in imported_research_urls
]
# Cleanup: only a projects-local source directory may be removed after # Cleanup: only a projects-local source directory may be removed after
# its files move into the target project. Every other location is copied # its files move into the target project. Every other location is copied
# and remains untouched, even when the caller passes --move. # and remains untouched, even when the caller passes --move.
@@ -1259,16 +1198,9 @@ def main(argv: list[str] | None = None) -> int:
move=args.move, move=args.move,
copy=args.copy, copy=args.copy,
) )
has_usable_import = _has_usable_import(summary) import_complete = _has_usable_import(summary)
import_complete = has_usable_import and not summary["research_sources_failed"]
if import_complete: if import_complete:
print(f"[OK] Imported sources into: {args.project_path}") print(f"[OK] Imported sources into: {args.project_path}")
elif has_usable_import:
print(
f"[ERROR] Topic-research source import is incomplete: "
f"{args.project_path}",
file=sys.stderr,
)
else: else:
print( print(
f"[ERROR] No usable sources imported into: {args.project_path}", f"[ERROR] No usable sources imported into: {args.project_path}",
@@ -1298,11 +1230,6 @@ def main(argv: list[str] | None = None) -> int:
print("\nAnalysis artifacts:") print("\nAnalysis artifacts:")
for item in summary["analysis"]: for item in summary["analysis"]:
print(f" - {item}") print(f" - {item}")
if summary["research_sources_expected"]:
print("\nTopic-research source reconciliation:")
print(f" expected: {len(summary['research_sources_expected'])}")
print(f" imported: {len(summary['research_sources_imported'])}")
print(f" failed: {len(summary['research_sources_failed'])}")
if summary["notes"]: if summary["notes"]:
print("\nNotes:") print("\nNotes:")
for item in summary["notes"]: for item in summary["notes"]:
@@ -33,7 +33,7 @@
"skills/ppt-master/references/native-data-interface.md": 6750, "skills/ppt-master/references/native-data-interface.md": 6750,
"skills/ppt-master/references/pptx-structure-interface.md": 4750, "skills/ppt-master/references/pptx-structure-interface.md": 4750,
"skills/ppt-master/references/strategist.md": 18000, "skills/ppt-master/references/strategist.md": 18000,
"skills/ppt-master/references/strategist-image.md": 2750, "skills/ppt-master/references/strategist-image.md": 3250,
"skills/ppt-master/references/strategist-template.md": 3000, "skills/ppt-master/references/strategist-template.md": 3000,
"skills/ppt-master/templates/design_spec_reference.md": 3750, "skills/ppt-master/templates/design_spec_reference.md": 3750,
"skills/ppt-master/templates/spec_lock_reference.md": 2750, "skills/ppt-master/templates/spec_lock_reference.md": 2750,
@@ -357,7 +357,7 @@
"max_tokens": 135000 "max_tokens": 135000
}, },
"route.generate.quick-generate.web-image": { "route.generate.quick-generate.web-image": {
"description": "Quick Generate with web resource preparation, provenance, attribution, and image execution.", "description": "Quick Generate with web resource preparation, provenance, attribution, and image execution. Ceiling raised after BUDGET_LOAD_SET reported 115146 tokens.",
"scope": "cumulative", "scope": "cumulative",
"include": [ "include": [
"route.generate.quick-generate", "route.generate.quick-generate",
@@ -366,7 +366,7 @@
"stage.generate.executor.web-image" "stage.generate.executor.web-image"
], ],
"files": [], "files": [],
"max_tokens": 115000 "max_tokens": 130000
}, },
"route.generate.quick-generate.visualization": { "route.generate.quick-generate.visualization": {
"description": "Quick Generate with bounded Chart/Table recall, their data-object branches, optional native-data metadata, and chart-coordinate verification before the final checker; the fixed Quick context already carries Shape Composition Grammar.", "description": "Quick Generate with bounded Chart/Table recall, their data-object branches, optional native-data metadata, and chart-coordinate verification before the final checker; the fixed Quick context already carries Shape Composition Grammar.",
@@ -617,7 +617,7 @@
"stage.shared.generate-audio" "stage.shared.generate-audio"
], ],
"files": [], "files": [],
"max_tokens": 19000 "max_tokens": 21000
}, },
"route.generate.beautify-flat-no-image": { "route.generate.beautify-flat-no-image": {
"description": "Generate-PPTX 1:1 beautify profile on the flat no-image path.", "description": "Generate-PPTX 1:1 beautify profile on the flat no-image path.",
@@ -761,7 +761,7 @@
"max_tokens": 74000 "max_tokens": 74000
}, },
"stage.generate.image.ai-two-types": { "stage.generate.image.ai-two-types": {
"description": "Incremental AI-image role with the selected preset or exact custom rendering bases and two selected local types.", "description": "Incremental AI-image role with the selected preset or exact custom rendering bases and two selected local types. Ceiling raised after BUDGET_LOAD_SET reported 26216 tokens.",
"scope": "incremental", "scope": "incremental",
"files": [ "files": [
"skills/ppt-master/references/image-base.md", "skills/ppt-master/references/image-base.md",
@@ -792,7 +792,7 @@
"registry": "image-type-templates" "registry": "image-type-templates"
} }
], ],
"max_tokens": 26000 "max_tokens": 29000
}, },
"stage.generate.image.web": { "stage.generate.image.web": {
"description": "Incremental web-image acquisition role. Ceiling raised after BUDGET_LOAD_SET reported 9341 tokens.", "description": "Incremental web-image acquisition role. Ceiling raised after BUDGET_LOAD_SET reported 9341 tokens.",
@@ -1073,7 +1073,7 @@
"max_tokens": 1250 "max_tokens": 1250
}, },
"stage.generate.executor.image": { "stage.generate.executor.image": {
"description": "Conditional image embedding and execution rules with the always-read layout math and compact composition vocabulary.", "description": "Conditional image embedding and execution rules with the always-read layout math and compact composition vocabulary. Ceiling raised after BUDGET_LOAD_SET reported 13167 tokens.",
"scope": "incremental", "scope": "incremental",
"files": [ "files": [
"skills/ppt-master/references/executor-image.md", "skills/ppt-master/references/executor-image.md",
@@ -1081,7 +1081,7 @@
"skills/ppt-master/references/image-layout-patterns.md", "skills/ppt-master/references/image-layout-patterns.md",
"skills/ppt-master/references/svg-image-embedding.md" "skills/ppt-master/references/svg-image-embedding.md"
], ],
"max_tokens": 13000 "max_tokens": 15000
}, },
"stage.generate.executor.web-image": { "stage.generate.executor.web-image": {
"description": "Conditional sourced-image attribution layered on image execution.", "description": "Conditional sourced-image attribution layered on image execution.",
@@ -1527,12 +1527,12 @@
"prefix_counts": { "prefix_counts": {
"P1": 13, "P1": 13,
"P2": 10, "P2": 10,
"P3": 23, "P3": 24,
"M1": 11, "M1": 12,
"M2": 9, "M2": 9,
"M3": 7, "M3": 7,
"A1": 4, "A1": 4,
"A2": 3, "A2": 4,
"A3": 3, "A3": 3,
"C1": 1, "C1": 1,
"C2": 2, "C2": 2,
@@ -14,11 +14,12 @@ Two optional cleanups address the realities of cropping a raster sheet:
placement inside a cell does not leave lopsided margins. placement inside a cell does not leave lopsided margins.
--alpha knock the (flat) sheet background out to transparency, so an element --alpha knock the (flat) sheet background out to transparency, so an element
can sit on a differently-colored slide without a visible box. can sit on a differently-colored slide without a visible box.
Both need a background color; it is auto-sampled from each cell's border unless Both need a background color; it is auto-sampled from the dominant flat field
you pass --bg. Keying only works on a genuinely flat ground, so each element is unless you pass --bg. Keying only works on a genuinely flat ground, so each
checked afterwards and a warning is printed when the background clearly did not element is checked before writing. --strict-alpha turns an incomplete key into
key out. See references/image-generator.md section 4.3 for the sheet contract an error with no output files. An explicit pure red, green, or blue key also
that keeps the ground flat. recovers clean RGB and partial alpha for antialiasing, shadows, and glows. See
references/image-generator.md section 4.3 for the sheet contract.
Usage: Usage:
python3 scripts/slice_images.py <sheet_image> --grid RxC [options] python3 scripts/slice_images.py <sheet_image> --grid RxC [options]
@@ -26,7 +27,8 @@ Usage:
Examples: Examples:
python3 scripts/slice_images.py projects/demo/images/illus_sheet.png --grid 2x3 python3 scripts/slice_images.py projects/demo/images/illus_sheet.png --grid 2x3
python3 scripts/slice_images.py projects/demo/images/illus_sheet.png --grid 2x3 \ python3 scripts/slice_images.py projects/demo/images/illus_sheet.png --grid 2x3 \
--names team,product,customer,growth,risk,vision --trim --alpha --names team,product,customer,growth,risk,vision --trim --alpha \
--bg "#00FF00" --strict-alpha
python3 scripts/slice_images.py projects/demo/images/illus_sheet.png --grid 1x4 \ python3 scripts/slice_images.py projects/demo/images/illus_sheet.png --grid 1x4 \
--prefix spot_ --bg "#F8F9FA" --alpha --prefix spot_ --bg "#F8F9FA" --alpha
@@ -37,6 +39,7 @@ Dependencies:
import argparse import argparse
import re import re
import sys import sys
from collections import Counter
from pathlib import Path from pathlib import Path
from statistics import median from statistics import median
from typing import Optional from typing import Optional
@@ -45,12 +48,19 @@ from console_encoding import configure_utf8_stdio
configure_utf8_stdio() configure_utf8_stdio()
from PIL import Image, ImageChops, ImageFilter from PIL import (
Image,
ImageChops,
ImageFilter,
ImageMath,
)
_GRID_RE = re.compile(r"^\s*(\d+)\s*[xX×]\s*(\d+)\s*$") _GRID_RE = re.compile(r"^\s*(\d+)\s*[xX×]\s*(\d+)\s*$")
_BG_BUCKET_SIZE = 16
_BG_SAMPLE_BORDER = 2 _BG_SAMPLE_BORDER = 2
_BG_SAMPLE_MAX_SIDE = 256
_DEFAULT_FEATHER = 4 _DEFAULT_FEATHER = 4
_CORNER_OPAQUE_ALPHA = 32 _BOUNDARY_OPAQUE_ALPHA = 32
def _log(msg: str) -> None: def _log(msg: str) -> None:
@@ -86,9 +96,8 @@ def _safe_basename(name: str) -> str:
return base return base
def _sample_bg(cell: Image.Image) -> tuple[int, int, int]: def _sample_border_bg(rgb: Image.Image) -> tuple[int, int, int]:
"""Estimate the flat background color from a cell's border ring.""" """Estimate a background candidate from a cell's border ring."""
rgb = cell.convert("RGB")
w, h = rgb.size w, h = rgb.size
border = max(1, min(_BG_SAMPLE_BORDER, w, h)) border = max(1, min(_BG_SAMPLE_BORDER, w, h))
px = rgb.load() px = rgb.load()
@@ -113,6 +122,60 @@ def _sample_bg(cell: Image.Image) -> tuple[int, int, int]:
return tuple(round(median(channel)) for channel in zip(*pixels)) # type: ignore[return-value] return tuple(round(median(channel)) for channel in zip(*pixels)) # type: ignore[return-value]
def _sample_pixels(rgb: Image.Image) -> list[tuple[int, int, int]]:
"""Return a bounded RGB sample for background-candidate scoring."""
sample = rgb.copy()
sample.thumbnail(
(_BG_SAMPLE_MAX_SIDE, _BG_SAMPLE_MAX_SIDE),
Image.Resampling.NEAREST,
)
raw = sample.tobytes()
return list(zip(raw[0::3], raw[1::3], raw[2::3]))
def _dominant_bg_candidate(
pixels: list[tuple[int, int, int]],
) -> tuple[int, int, int]:
"""Estimate the dominant flat field from quantized sampled pixels."""
buckets = Counter(
tuple(channel // _BG_BUCKET_SIZE for channel in pixel)
for pixel in pixels
)
dominant_bucket = buckets.most_common(1)[0][0]
members = [
pixel
for pixel in pixels
if tuple(channel // _BG_BUCKET_SIZE for channel in pixel) == dominant_bucket
]
return tuple(round(median(channel)) for channel in zip(*members)) # type: ignore[return-value]
def _background_coverage(
pixels: list[tuple[int, int, int]],
candidate: tuple[int, int, int],
tolerance: int,
) -> int:
"""Count sampled pixels close enough to a background candidate."""
return sum(
max(abs(pixel[index] - candidate[index]) for index in range(3)) <= tolerance
for pixel in pixels
)
def _sample_bg(cell: Image.Image, tolerance: int) -> tuple[int, int, int]:
"""Choose the background candidate that covers most of the cell."""
rgb = cell.convert("RGB")
pixels = _sample_pixels(rgb)
candidates = (
_sample_border_bg(rgb),
_dominant_bg_candidate(pixels),
)
return max(
candidates,
key=lambda candidate: _background_coverage(pixels, candidate, tolerance),
)
def _max_channel_difference(cell: Image.Image, bg: tuple[int, int, int]) -> Image.Image: def _max_channel_difference(cell: Image.Image, bg: tuple[int, int, int]) -> Image.Image:
"""Return the maximum absolute RGB channel difference from the background.""" """Return the maximum absolute RGB channel difference from the background."""
diff = ImageChops.difference(cell.convert("RGB"), Image.new("RGB", cell.size, bg)) diff = ImageChops.difference(cell.convert("RGB"), Image.new("RGB", cell.size, bg))
@@ -120,6 +183,106 @@ def _max_channel_difference(cell: Image.Image, bg: tuple[int, int, int]) -> Imag
return ImageChops.lighter(ImageChops.lighter(red, green), blue) return ImageChops.lighter(ImageChops.lighter(red, green), blue)
def _pure_chroma_channel(bg: tuple[int, int, int]) -> Optional[int]:
"""Return the active channel for an exact pure RGB key, if any."""
if bg.count(255) != 1 or bg.count(0) != 2:
return None
return bg.index(255)
def _channel_alpha(channel: Image.Image, bg_value: int) -> Image.Image:
"""Return the minimum alpha that can explain one channel over a key."""
lut = []
for value in range(256):
if value > bg_value:
denominator = 255 - bg_value
alpha = 255 if denominator == 0 else round(
(value - bg_value) * 255 / denominator
)
elif value < bg_value:
alpha = 255 if bg_value == 0 else round(
(bg_value - value) * 255 / bg_value
)
else:
alpha = 0
lut.append(alpha)
return channel.point(lut)
def _chroma_alpha(rgb: Image.Image, bg: tuple[int, int, int]) -> Image.Image:
"""Recover foreground opacity for a pure single-channel chroma key.
A non-key-dominant pixel is treated as opaque foreground. A key-dominant
pixel uses color-to-alpha recovery, which preserves soft shadows, glows,
and antialiased edges without making an ordinary solid foreground color
unnecessarily translucent.
"""
channels = rgb.split()
channel_alphas = [
_channel_alpha(channel, bg_value)
for channel, bg_value in zip(channels, bg)
]
raw_alpha = ImageChops.lighter(
ImageChops.lighter(channel_alphas[0], channel_alphas[1]),
channel_alphas[2],
)
key_index = _pure_chroma_channel(bg)
if key_index is None:
return raw_alpha
other_channels = [
channel for index, channel in enumerate(channels) if index != key_index
]
key_excess = ImageChops.subtract(
channels[key_index],
ImageChops.lighter(other_channels[0], other_channels[1]),
)
key_dominance = key_excess.point(lambda value: 255 if value > 0 else 0)
opaque = Image.new("L", rgb.size, 255)
return Image.composite(raw_alpha, opaque, key_dominance)
def _decontaminate_channel(
channel: Image.Image,
alpha: Image.Image,
bg_value: int,
) -> Image.Image:
"""Remove a composited key channel while supporting Pillow 9 through 12."""
if hasattr(ImageMath, "lambda_eval"):
return ImageMath.lambda_eval(
lambda op: op["convert"](
bg_value
+ (op["channel"] - bg_value)
* 255
/ op["max"](op["alpha"], 1),
"L",
),
channel=channel,
alpha=alpha,
)
return ImageMath.eval( # type: ignore[attr-defined]
"convert(bg + (channel - bg) * 255 / max(alpha, 1), 'L')",
channel=channel,
alpha=alpha,
bg=bg_value,
)
def _decontaminate_rgb(
rgb: Image.Image,
alpha: Image.Image,
bg: tuple[int, int, int],
) -> Image.Image:
"""Recover foreground RGB values from a composited pure chroma key."""
return Image.merge(
"RGB",
tuple(
_decontaminate_channel(channel, alpha, bg_value)
for channel, bg_value in zip(rgb.split(), bg)
),
)
def _soft_mask_from_diff(diff: Image.Image, tolerance: int) -> Image.Image: def _soft_mask_from_diff(diff: Image.Image, tolerance: int) -> Image.Image:
"""Build a feathered alpha mask around the tolerance threshold.""" """Build a feathered alpha mask around the tolerance threshold."""
low = max(0, tolerance - _DEFAULT_FEATHER) low = max(0, tolerance - _DEFAULT_FEATHER)
@@ -143,13 +306,20 @@ def _content_masks(
cell: Image.Image, cell: Image.Image,
bg: tuple[int, int, int], bg: tuple[int, int, int],
tolerance: int, tolerance: int,
) -> tuple[Image.Image, Image.Image]: ) -> tuple[Image.Image, Image.Image, Optional[Image.Image]]:
"""Build binary trim and soft alpha masks from the same color distance.""" """Build trim/alpha masks and optional chroma-decontaminated RGB."""
diff = _max_channel_difference(cell, bg) rgb = cell.convert("RGB")
diff = _max_channel_difference(rgb, bg)
trim_mask = diff.point(lambda p: 255 if p > tolerance else 0) trim_mask = diff.point(lambda p: 255 if p > tolerance else 0)
alpha_mask = _soft_mask_from_diff(diff, tolerance) tolerance_gate = _soft_mask_from_diff(diff, tolerance)
alpha_mask = alpha_mask.filter(ImageFilter.MinFilter(3)) if _pure_chroma_channel(bg) is not None:
return trim_mask, alpha_mask chroma_alpha = _chroma_alpha(rgb, bg)
alpha_mask = ImageChops.multiply(chroma_alpha, tolerance_gate)
keyed_rgb = _decontaminate_rgb(rgb, chroma_alpha, bg)
return trim_mask, alpha_mask, keyed_rgb
alpha_mask = tolerance_gate.filter(ImageFilter.MinFilter(3))
return trim_mask, alpha_mask, None
def _keying_findings( def _keying_findings(
@@ -164,10 +334,10 @@ def _keying_findings(
) -> list[str]: ) -> list[str]:
"""Report objective signs that the flat-background key did not take. """Report objective signs that the flat-background key did not take.
Two deterministic symptoms: a cut element whose corners are still opaque, Two deterministic symptoms: a cut element whose cell boundary is still
and a ``--trim`` that removed nothing because the mask found content along opaque, and content that reaches any cell edge. Both violate the clear-key
every edge. Both mean the sampled background did not match the real ground gutter required by the sheet contract, usually because the ground is not
(a textured or multi-color ground is the usual cause). flat or an element/effect crossed its cell boundary.
""" """
findings: list[str] = [] findings: list[str] = []
hex_bg = "#{:02X}{:02X}{:02X}".format(*cell_bg) hex_bg = "#{:02X}{:02X}{:02X}".format(*cell_bg)
@@ -175,28 +345,61 @@ def _keying_findings(
if alpha and alpha_mask is not None: if alpha and alpha_mask is not None:
px = alpha_mask.load() px = alpha_mask.load()
width, height = alpha_mask.size width, height = alpha_mask.size
corners = ( border = max(1, min(_BG_SAMPLE_BORDER, width, height))
px[0, 0], px[width - 1, 0], boundary = []
px[0, height - 1], px[width - 1, height - 1], for y in range(border):
boundary.extend(px[x, y] for x in range(width))
for y in range(max(border, height - border), height):
boundary.extend(px[x, y] for x in range(width))
for y in range(border, max(border, height - border)):
boundary.extend(px[x, y] for x in range(border))
boundary.extend(
px[x, y] for x in range(max(border, width - border), width)
)
opaque = sum(
1 for value in boundary if value > _BOUNDARY_OPAQUE_ALPHA
) )
opaque = sum(1 for value in corners if value > _CORNER_OPAQUE_ALPHA)
if opaque: if opaque:
findings.append( findings.append(
f"{label}: {opaque}/4 corners stayed opaque after --alpha " f"{label}: {opaque}/{len(boundary)} boundary pixels stayed opaque "
f"after --alpha "
f"(sampled background {hex_bg})" f"(sampled background {hex_bg})"
) )
if trim: if trim:
cell_width, cell_height = cell_size cell_width, cell_height = cell_size
if bbox[2] - bbox[0] >= cell_width and bbox[3] - bbox[1] >= cell_height: touched_edges = []
if bbox[0] <= 0:
touched_edges.append("left")
if bbox[1] <= 0:
touched_edges.append("top")
if bbox[2] >= cell_width:
touched_edges.append("right")
if bbox[3] >= cell_height:
touched_edges.append("bottom")
if touched_edges:
findings.append( findings.append(
f"{label}: --trim removed nothing, so content reaches every cell edge " f"{label}: content reaches the {'/'.join(touched_edges)} cell edge(s) "
f"(sampled background {hex_bg})" f"(sampled background {hex_bg})"
) )
return findings return findings
def _log_keying_findings(findings: list[str]) -> None:
"""Report incomplete flat-background keying."""
_log("\n[WARN] Alpha extraction is incomplete — the key field or cell")
_log(" isolation failed:")
for finding in findings:
_log(f" - {finding}")
_log(" Fix: regenerate with one genuinely flat ground and keep every "
"element/effect")
_log(" inside its cell with a clear key-only gutter, or rerun with an "
"explicit")
_log(" --bg <hex> and a larger --tolerance; use --inset when a drawn "
"outer gutter is isolated from every element.")
def slice_sheet( def slice_sheet(
sheet_path: Path, sheet_path: Path,
rows: int, rows: int,
@@ -208,6 +411,7 @@ def slice_sheet(
inset: float = 0.0, inset: float = 0.0,
trim: bool = False, trim: bool = False,
alpha: bool = False, alpha: bool = False,
strict_alpha: bool = False,
bg: Optional[tuple[int, int, int]] = None, bg: Optional[tuple[int, int, int]] = None,
tolerance: int = 18, tolerance: int = 18,
) -> list[Path]: ) -> list[Path]:
@@ -218,6 +422,8 @@ def slice_sheet(
automated run never silently drops cells. Each name must be a bare filename. automated run never silently drops cells. Each name must be a bare filename.
""" """
total_cells = rows * cols total_cells = rows * cols
if strict_alpha and not alpha:
raise ValueError("strict_alpha requires alpha=True")
if names is not None and len(names) != total_cells: if names is not None and len(names) != total_cells:
raise ValueError( raise ValueError(
f"--names has {len(names)} entries but the {rows}x{cols} grid has " f"--names has {len(names)} entries but the {rows}x{cols} grid has "
@@ -247,6 +453,7 @@ def slice_sheet(
stem = sheet_path.stem stem = sheet_path.stem
name_prefix = _safe_basename(prefix) if prefix else f"{stem}_" name_prefix = _safe_basename(prefix) if prefix else f"{stem}_"
prepared: list[tuple[int, int, Image.Image, Path]] = []
written: list[Path] = [] written: list[Path] = []
findings: list[str] = [] findings: list[str] = []
@@ -264,10 +471,13 @@ def slice_sheet(
trim_mask: Optional[Image.Image] = None trim_mask: Optional[Image.Image] = None
alpha_mask: Optional[Image.Image] = None alpha_mask: Optional[Image.Image] = None
keyed_rgb: Optional[Image.Image] = None
bbox = None bbox = None
if trim or alpha: if trim or alpha:
cell_bg = bg if bg is not None else _sample_bg(cell) cell_bg = bg if bg is not None else _sample_bg(cell, tolerance)
trim_mask, alpha_mask = _content_masks(cell, cell_bg, tolerance) trim_mask, alpha_mask, keyed_rgb = _content_masks(
cell, cell_bg, tolerance
)
bbox = trim_mask.getbbox() bbox = trim_mask.getbbox()
if bbox is None: if bbox is None:
raise ValueError(f"cell ({r},{c}) is all background; no element was sliced") raise ValueError(f"cell ({r},{c}) is all background; no element was sliced")
@@ -279,8 +489,12 @@ def slice_sheet(
if trim and trim_mask is not None and alpha_mask is not None and bbox is not None: if trim and trim_mask is not None and alpha_mask is not None and bbox is not None:
cell = cell.crop(bbox) cell = cell.crop(bbox)
alpha_mask = alpha_mask.crop(bbox) alpha_mask = alpha_mask.crop(bbox)
if keyed_rgb is not None:
keyed_rgb = keyed_rgb.crop(bbox)
if alpha and alpha_mask is not None: if alpha and alpha_mask is not None:
if keyed_rgb is not None:
cell = keyed_rgb.convert("RGBA")
cell.putalpha(alpha_mask) cell.putalpha(alpha_mask)
if safe_names: if safe_names:
@@ -290,25 +504,25 @@ def slice_sheet(
else: else:
out_name = f"{name_prefix}{idx + 1:02d}.png" out_name = f"{name_prefix}{idx + 1:02d}.png"
out_path = output_dir / out_name out_path = output_dir / out_name
prepared.append((r, c, cell, out_path))
idx += 1
if findings:
_log_keying_findings(findings)
if strict_alpha:
raise ValueError(
"strict alpha validation found incomplete background keying; "
"no output files were written"
)
for r, c, cell, out_path in prepared:
cell.save(out_path) cell.save(out_path)
written.append(out_path) written.append(out_path)
_log(f"[OK] cell ({r},{c}) -> {out_path.name} ({cell.width}x{cell.height})") _log(f"[OK] cell ({r},{c}) -> {out_path.name} ({cell.width}x{cell.height})")
idx += 1
if len(written) != total_cells: if len(written) != total_cells:
raise ValueError(f"sliced {len(written)} elements but expected {total_cells}") raise ValueError(f"sliced {len(written)} elements but expected {total_cells}")
if findings:
_log("\n[WARN] Background keying looks incomplete — the cut element(s) may")
_log(" still carry a visible box on a differently-colored slide:")
for finding in findings:
_log(f" - {finding}")
_log(" Fix: regenerate the sheet with one genuinely flat ground "
"(no grain, halftone, or")
_log(" vignette over the background, gutters included), or rerun with "
"an explicit")
_log(" --bg <hex> and a larger --tolerance.")
return written return written
@@ -320,7 +534,8 @@ def build_parser() -> argparse.ArgumentParser:
epilog="""Examples: epilog="""Examples:
python3 scripts/slice_images.py projects/demo/images/illus_sheet.png --grid 2x3 python3 scripts/slice_images.py projects/demo/images/illus_sheet.png --grid 2x3
python3 scripts/slice_images.py projects/demo/images/illus_sheet.png --grid 2x3 \\ python3 scripts/slice_images.py projects/demo/images/illus_sheet.png --grid 2x3 \\
--names team,product,customer,growth,risk,vision --trim --alpha --names team,product,customer,growth,risk,vision --trim --alpha \\
--bg "#00FF00" --strict-alpha
""", """,
) )
parser.add_argument("sheet", help="Path to the generated illustration sheet image") parser.add_argument("sheet", help="Path to the generated illustration sheet image")
@@ -350,9 +565,14 @@ def build_parser() -> argparse.ArgumentParser:
"--alpha", action="store_true", "--alpha", action="store_true",
help="Make the (flat) background transparent in each element", help="Make the (flat) background transparent in each element",
) )
parser.add_argument(
"--strict-alpha", action="store_true",
help="Fail without writing outputs when --alpha validation finds incomplete keying",
)
parser.add_argument( parser.add_argument(
"--bg", default=None, "--bg", default=None,
help="Background hex color for --trim/--alpha (default: auto-sample cell border)", help="Background hex color for --trim/--alpha; an exact pure red/green/blue "
"key enables despill and soft-alpha recovery (default: auto-sample)",
) )
parser.add_argument( parser.add_argument(
"--tolerance", type=int, default=18, "--tolerance", type=int, default=18,
@@ -385,6 +605,9 @@ def main(argv: Optional[list[str]] = None) -> int:
if not 0 <= args.tolerance <= 255: if not 0 <= args.tolerance <= 255:
print("[ERROR] --tolerance must be in [0, 255]", file=sys.stderr) print("[ERROR] --tolerance must be in [0, 255]", file=sys.stderr)
return 1 return 1
if args.strict_alpha and not args.alpha:
print("[ERROR] --strict-alpha requires --alpha", file=sys.stderr)
return 1
names = [n.strip() for n in args.names.split(",") if n.strip()] if args.names else None names = [n.strip() for n in args.names.split(",") if n.strip()] if args.names else None
output_dir = Path(args.output) if args.output else sheet_path.parent output_dir = Path(args.output) if args.output else sheet_path.parent
@@ -393,7 +616,8 @@ def main(argv: Optional[list[str]] = None) -> int:
written = slice_sheet( written = slice_sheet(
sheet_path, rows, cols, output_dir, sheet_path, rows, cols, output_dir,
names=names, prefix=args.prefix, inset=args.inset, names=names, prefix=args.prefix, inset=args.inset,
trim=args.trim, alpha=args.alpha, bg=bg, tolerance=args.tolerance, trim=args.trim, alpha=args.alpha, strict_alpha=args.strict_alpha,
bg=bg, tolerance=args.tolerance,
) )
except (OSError, ValueError) as exc: except (OSError, ValueError) as exc:
print(f"[ERROR] Slicing failed: {exc}", file=sys.stderr) print(f"[ERROR] Slicing failed: {exc}", file=sys.stderr)
@@ -51,8 +51,8 @@ request does not explicitly select Quick.
🚧 **GATE**: The user has provided a topic / desired outcome and any available initial material. 🚧 **GATE**: The user has provided a topic / desired outcome and any available initial material.
> **Topic-only**: run [`topic-research`](stages/topic-research.md) immediately, > **Topic-only**: run [`topic-research`](stages/topic-research.md) immediately,
> then use its factual supplement as source content; Step 2 reads retained > then use its research pair as source content; Step 2 imports that pair without
> webpage URLs from the facts JSON and imports them as text evidence. > expanding the facts JSON's webpage URLs.
When the user provides non-Markdown content, convert immediately through the When the user provides non-Markdown content, convert immediately through the
unified dispatcher. It preserves the backend converters' existing behavior, unified dispatcher. It preserves the backend converters' existing behavior,
@@ -87,8 +87,8 @@ After reading direct and converted content, assess factual sufficiency:
**Sufficiency test**: research only to avoid inventing, omitting, or leaving **Sufficiency test**: research only to avoid inventing, omitting, or leaving
unsupported a factual claim the requested outcome requires; file presence or unsupported a factual claim the requested outcome requires; file presence or
length is irrelevant. It records the needed facts and adopted webpages. Step 2 length is irrelevant. It records the needed facts and adopted webpage URLs in
imports those webpages as text-only evidence; Step 5 the research pair. Step 2 fetches no adopted page; Step 5
acquires only Strategist-selected independent AI / web / slice assets after acquires only Strategist-selected independent AI / web / slice assets after
final confirmation. final confirmation.
@@ -106,7 +106,7 @@ final confirmation.
> Browser-based live preview cannot render EMF (will show blank) — this is expected; > Browser-based live preview cannot render EMF (will show blank) — this is expected;
> the PPTX output is the source of truth. > the PPTX output is the source of truth.
**✅ Checkpoint — Confirm source content, retained webpage inputs, and any factual supplement are ready, proceed to Step 2.** **✅ Checkpoint — Confirm source content and any factual supplement/provenance pair are ready, proceed to Step 2.**
--- ---
@@ -158,9 +158,8 @@ Import source content (choose based on the situation):
| User provided text directly in conversation | No import needed — content is already in conversation context; subsequent steps can reference it directly | | User provided text directly in conversation | No import needed — content is already in conversation context; subsequent steps can reference it directly |
When Topic Research ran, include only its research pair. `project_manager.py` When Topic Research ran, include only its research pair. `project_manager.py`
reads the facts JSON's unique `source_url` values, archives each page in imports the facts JSON as an ordinary file and never expands its `source_url`
text-only mode, and fails incomplete source reconciliation. It does not add page values, so project initialization fetches no adopted page.
images to `<project>/images/`.
For PPTX sources, `import-sources` automatically runs the standard intake enrichment: For PPTX sources, `import-sources` automatically runs the standard intake enrichment:
@@ -521,7 +520,7 @@ Then **lazy-load the path-specific reference** for each row that actually needs
| Prepared derivative | `references/image-base.md`; add `references/image-generator.md` §4.4 only for registered layers | after its named canonical source reaches a usable terminal state, run `python3 ${SKILL_DIR}/scripts/image_treat.py ...` for the declared per-pixel treatment or the existing §4.4 preparation path | | Prepared derivative | `references/image-base.md`; add `references/image-generator.md` §4.4 only for registered layers | after its named canonical source reaches a usable terminal state, run `python3 ${SKILL_DIR}/scripts/image_treat.py ...` for the declared per-pixel treatment or the existing §4.4 preparation path |
| `ai` | `references/image-generator.md` | write `<project_path>/images/image_prompts.json`, then follow `image-generator.md §7 Path Selection` (`image_gen.py --manifest` is **Path A only**) | | `ai` | `references/image-generator.md` | write `<project_path>/images/image_prompts.json`, then follow `image-generator.md §7 Path Selection` (`image_gen.py --manifest` is **Path A only**) |
| `web` | `references/image-searcher.md` | `python3 ${SKILL_DIR}/scripts/image_search.py ...` (≥2 web rows → `--batch images/image_queries.json`) | | `web` | `references/image-searcher.md` | `python3 ${SKILL_DIR}/scripts/image_search.py ...` (≥2 web rows → `--batch images/image_queries.json`) |
| `slice` | `references/image-generator.md` §4.3 | derived — **after** the parent `ai` sheet row is `Generated`, run `python3 ${SKILL_DIR}/scripts/slice_images.py <project_path>/images/<sheet>.png --grid RxC --names ... --trim --alpha` (see workflow step 2.5) | | `slice` | `references/image-generator.md` §4.3 | derived — **after** the parent `ai` sheet row is `Generated`, run `python3 ${SKILL_DIR}/scripts/slice_images.py <project_path>/images/<sheet>.png --grid RxC --names ... --trim --alpha --bg KEY_HEX_FROM_PROMPT --strict-alpha` (see workflow step 2.5) |
| `user` / `placeholder` | (skip) | (skip) | | `user` / `placeholder` | (skip) | (skip) |
A deck with only `ai` rows never loads `image-searcher.md`; a deck with only `web` rows never loads `image-generator.md`. A mixed deck loads both, processes each row through its own path, and writes both `image_prompts.json` and `image_sources.json`. A deck with only `ai` rows never loads `image-searcher.md`; a deck with only `web` rows never loads `image-generator.md`. A mixed deck loads both, processes each row through its own path, and writes both `image_prompts.json` and `image_sources.json`.
@@ -532,21 +531,23 @@ A deck with only `ai` rows never loads `image-searcher.md`; a deck with only `we
> **Default — bounded multimodal web thumbnail selection**: when either the current agent or an available isolated reviewer can inspect images, add `--save-candidates` to the single or batch web command. Author explicit `query_variants` for materially different official translations, spellings, aliases, or Chinese names; the tool aggregates and deduplicates them, then saves only the first ranked page (8 previews by default), writes `candidates/<stem>/review_sheet.jpg`, marks the batch row `Needs-Selection`, and downloads no original. Run [`web-image-review`](stages/web-image-review.md): dispatch exactly one isolated reviewer for all current sheets when supported, passing only each row's locked Reference/Crop Policy plus candidate sidecar/sheet paths; otherwise the active image owner reads that stage and reviews locally. Only a stage-selected passing candidate may be used with `--promote` to download one original and write provenance (pass the same `--batch images/image_queries.json` to reconcile its row to `Sourced`). If none passes and `has_more_candidates` is true, advance that row to `next_candidate_page` before changing the query. Only after the pool is exhausted may the row receive materially different query variants and return to `Pending`. When no available context has vision, omit `--save-candidates`: best-only mode may download only a strict metadata-verified candidate, records `selection_method: metadata-ranked`, and otherwise stops at `Needs-Manual` without claiming visual confirmation. > **Default — bounded multimodal web thumbnail selection**: when either the current agent or an available isolated reviewer can inspect images, add `--save-candidates` to the single or batch web command. Author explicit `query_variants` for materially different official translations, spellings, aliases, or Chinese names; the tool aggregates and deduplicates them, then saves only the first ranked page (8 previews by default), writes `candidates/<stem>/review_sheet.jpg`, marks the batch row `Needs-Selection`, and downloads no original. Run [`web-image-review`](stages/web-image-review.md): dispatch exactly one isolated reviewer for all current sheets when supported, passing only each row's locked Reference/Crop Policy plus candidate sidecar/sheet paths; otherwise the active image owner reads that stage and reviews locally. Only a stage-selected passing candidate may be used with `--promote` to download one original and write provenance (pass the same `--batch images/image_queries.json` to reconcile its row to `Sourced`). If none passes and `has_more_candidates` is true, advance that row to `next_candidate_page` before changing the query. Only after the pool is exhausted may the row receive materially different query variants and return to `Pending`. When no available context has vision, omit `--save-candidates`: best-only mode may download only a strict metadata-verified candidate, records `selection_method: metadata-ranked`, and otherwise stops at `Needs-Manual` without claiming visual confirmation.
> **Retained-page fallback**: only after that normal search is exhausted, a vision-capable image owner may open one relevant research page and test one inline-image URL at a time with `--from-url`. Never use retained pages as the initial pool or bulk-download them; without vision, skip this fallback. > **Adopted-page fallback**: only after that normal search is exhausted, a vision-capable image owner may follow [`topic-research`](stages/topic-research.md) § Hand-off to fetch one relevant `source_url` as a Markdown + companion-image source package, review it, and copy only accepted files into `<project>/images/`. Never auto-expand facts URLs or promote the whole package; without vision, skip this fallback.
> **Default — short provider query (may override for a complete entity name or necessary disambiguation)**: keep §VIII `Reference` as the locked subject/focal/crop intent and author a separate concrete `image_queries.json.query`. Search/review never rewrites the Design Spec or lock to fit a candidate. > **Default — short provider query (may override for a complete entity name or necessary disambiguation)**: keep §VIII `Reference` as the locked subject/focal/crop intent and author a separate concrete `image_queries.json.query`. Search/review never rewrites the Design Spec or lock to fit a candidate.
> **Default — one sheet for compatible AI spots or decorative lettering elements (may override for different cell shape, detail, quality, or semantics)**: prefer one grid sheet for a same-family set; independent `ai` rows remain valid. A lettering sheet records every exact stable string and contains no scene or page chrome. When selected, choose a grid matching the planned cells, keep the sheet unplaced, and place/project each transparent `slice` row. Contract: [image-generator.md](../references/image-generator.md) §4.3. > **Default — one sheet per compatible AI visual family (may override for different cell shape, detail, quality, or semantics)**: group illustration elements or exact lettering strings by a coherent visual identity rather than an identical effect recipe. A family may include recurring chrome, dominant anchors, supporting figures, and accents; split only when element geometry, quality, or semantics conflict. Give the model each element's page/reuse job, relative visual weight, and energy, then apply the lettering boundary in [image-generator.md](../references/image-generator.md) §5.3. Keep every sheet unplaced and place/project only successful transparent `slice` rows. Contract: [image-generator.md](../references/image-generator.md) §4.3.
> ⚠️ **Honor the Design Spec's confirmed image source before running any generation command**: the `ai` generation path (Path A = `image_gen.py` API / Path B = host-native tool / Offline Manual) is **not** auto-only — the production value recorded in `design_spec.md §I` wins. `host-native` forces Path B even when `IMAGE_BACKEND` is configured; `api` forces Path A; `manual` forces offline. Never reopen `result.json` here, and never run `image_gen.py --manifest` when the recorded value is `host-native` or `manual`. Full selection rule: [image-generator.md](../references/image-generator.md) §7 Path Selection. > ⚠️ **Honor the Design Spec's confirmed image source before running any generation command**: the `ai` generation path (Path A = `image_gen.py` API / Path B = host-native tool / Offline Manual) is **not** auto-only — the production value recorded in `design_spec.md §I` wins. `host-native` forces Path B even when `IMAGE_BACKEND` is configured; `api` forces Path A; `manual` forces offline. Never reopen `result.json` here, and never run `image_gen.py --manifest` when the recorded value is `host-native` or `manual`. Full selection rule: [image-generator.md](../references/image-generator.md) §7 Path Selection.
> 🚧 **Default exhausted-automation GATE**: `auto` tries Path A then Path B but does not silently enter Offline Manual. When both are unavailable/exhausted—or a confirmed `api` / `host-native` path remains unavailable after its retry—apply `image-generator.md` §7's single recovery decision: ask whether to repair and retry the same path, generate the listed files manually, or cancel the affected AI images and repair the plan. Only confirmed `manual` may create `Needs-Manual` rows. Quick instead applies its own non-interactive no-AI replan after automated exhaustion.
Workflow: Workflow:
1. Extract all resource rows from the design spec. First separate rows whose `Reference` starts `Derived from <canonical bare filename>; treatment=` so they cannot re-enter ordinary ai/web/slice acquisition; reject source/output equality, a derivative parent, chains, cycles, or self-reference; then group canonical rows by `Acquire Via`. Every Pending/Failed canonical acquisition row and Pending derivative must reach a terminal state before Executor starts. 1. Extract all resource rows from the design spec. First separate rows whose `Reference` starts `Derived from <canonical bare filename>; treatment=` so they cannot re-enter ordinary ai/web/slice acquisition; reject source/output equality, a derivative parent, chains, cycles, or self-reference; then group canonical rows by `Acquire Via`. Every Pending/Failed canonical acquisition row and Pending derivative must reach a terminal state before Executor starts.
2. Generate prompts (ai rows) and/or run search (web rows) per [image-base.md](../references/image-base.md) §3 dispatch table 2. Generate prompts (ai rows) and/or run search (web rows) per [image-base.md](../references/image-base.md) §3 dispatch table
2.5. **Slice any illustration or lettering sheets (only if `slice` rows exist).** For each generated `ai` **sheet** row, run `slice_images.py` (grid + the element `--names` matching the `slice` rows, `--trim --alpha`) so every transparent element file lands in `images/`; mark each `slice` row `Generated`. A sheet still in `Needs-Manual` cannot be sliced — leave its `slice` rows `Needs-Manual` and surface them at the Step 7 readiness gate. Contract: [image-generator.md](../references/image-generator.md) §4.3. 2.5. **Slice any illustration or lettering sheets (only if `slice` rows exist).** For each generated `ai` **sheet** row, run `slice_images.py` with the matching grid/`--names`, `--trim --alpha`, the exact key HEX named in its prompt as `--bg`, and `--strict-alpha`. Mark each `slice` row `Generated` only after exit 0; a strict keying failure writes no replacement outputs and returns the affected sheet to image preparation. A sheet still in `Needs-Manual` cannot be sliced — leave its `slice` rows `Needs-Manual` and surface them at the Step 7 readiness gate. Contract: [image-generator.md](../references/image-generator.md) §4.3.
2.6. **Materialize planned prepared derivatives.** After each named canonical source reaches a usable terminal state, preserve it and write the separately named derivative only from its declared treatment. Use `image_treat.py` for per-pixel blur, desaturation/grayscale, duotone, brightness, or contrast; that row inherits the canonical `Acquire Via` and terminal class. Use `image-generator.md` §4.4 only for registered clean-base/layer work; a supplied final asset is `user / Existing`, while generated/reconstructed output remains `ai / Generated`. A standalone cutout must be prepared RGBA, a flat-key slice, or supplied by the active host; otherwise mark it `Needs-Manual`. Do not present `image_treat.py` as photo background removal. Do not bake crop/clip, rotation/mirror, opacity, frame, shadow, scrim/wash, vignette, or overlap into a bitmap. Any derivative of a web source copies that source's license/attribution record to the new filename. A parent without a usable status leaves the child `Needs-Manual`. 2.6. **Materialize planned prepared derivatives.** After each named canonical source reaches a usable terminal state, preserve it and write the separately named derivative only from its declared treatment. Use `image_treat.py` for per-pixel blur, desaturation/grayscale, duotone, brightness, or contrast; that row inherits the canonical `Acquire Via` and terminal class. Use `image-generator.md` §4.4 only for registered clean-base/layer work; a supplied final asset is `user / Existing`, while generated/reconstructed output remains `ai / Generated`. A standalone cutout must be prepared RGBA, a flat-key slice, or supplied by the active host; otherwise follow its owning source's terminal rule, including the Default AI recovery decision before `Needs-Manual`. Do not present `image_treat.py` as photo background removal. Do not bake crop/clip, rotation/mirror, opacity, frame, shadow, scrim/wash, vignette, or overlap into a bitmap. Any derivative of a web source copies that source's license/attribution record to the new filename. A parent without a usable status leaves the child in the same unresolved or manual state.
3. Verify every processed acquisition/derivative row reaches its source-class terminal status under [`svg-image-embedding.md`](../references/svg-image-embedding.md); no `Pending`, `Failed`, or web `Needs-Selection` remains. On `auto`, follow the owning fallback chain. For confirmed `api` or `host-native`, retry only that path, then mark unresolved rows `Needs-Manual` without switching provider. 3. Verify every processed acquisition/derivative row reaches its source-class terminal status under [`svg-image-embedding.md`](../references/svg-image-embedding.md); no `Pending`, `Failed`, or web `Needs-Selection` remains. On `auto`, follow the owning automated fallback chain. For confirmed `api` or `host-native`, retry only that path. Any unresolved Default AI row stops at the recovery decision above; do not mark it `Needs-Manual` or switch provider before the user's choice.
4. Re-derive image facts after canonical acquisition, slicing, and prepared derivatives are final — `python3 ${SKILL_DIR}/scripts/analyze_images.py <project_path>/images` — so `analysis/image_analysis.csv` reflects every image the Executor may place. Image facts are regenerated on use, never a stale store (see Step 4's image-facts note). 4. Re-derive image facts after canonical acquisition, slicing, and prepared derivatives are final — `python3 ${SKILL_DIR}/scripts/analyze_images.py <project_path>/images` — so `analysis/image_analysis.csv` reflects every image the Executor may place. Image facts are regenerated on use, never a stale store (see Step 4's image-facts note).
**✅ Internal checkpoint — acquisition complete**: verify conditional AI/web sidecars, all required slice outputs, terminal status for every resource row, and a refreshed `image_analysis.csv`. Do not print this checklist. On success, auto-proceed under the compact status rule above. **✅ Internal checkpoint — acquisition complete**: verify conditional AI/web sidecars, all required slice outputs, terminal status for every resource row, and a refreshed `image_analysis.csv`. Do not print this checklist. On success, auto-proceed under the compact status rule above.
@@ -560,7 +561,7 @@ Workflow:
- [ ] **Next**: open a fresh chat window and input `继续生成 projects/<project_name>` to enter the execution session via the [`resume-execute`](stages/resume-execute.md) stage. - [ ] **Next**: open a fresh chat window and input `继续生成 projects/<project_name>` to enter the execution session via the [`resume-execute`](stages/resume-execute.md) stage.
``` ```
> On acquisition failure, follow [image-base.md](../references/image-base.md) §6 without halting. Web rows continue through materially different query/provider/license/URL strategies; after exhaustion, mark `Needs-Manual`, report, and continue. > On web acquisition failure, follow [image-base.md](../references/image-base.md) §6 without halting. Continue through materially different query/provider/license/URL strategies; after exhaustion, mark `Needs-Manual`, report, and continue. AI rows use the separate Default recovery gate above.
--- ---
@@ -763,7 +764,7 @@ enabled,
`notes/total.md` also exists and covers every page; when it is disabled, notes `notes/total.md` also exists and covers every page; when it is disabled, notes
artifacts are not gate requirements. artifacts are not gate requirements.
🚧 **Image readiness GATE**: When any required resource row is `Needs-Manual`, every expected file and derived slice output MUST exist under `<project_path>/images/` before the first active Step 7 sub-step. If any file is absent, pause and list the exact filenames. After the files arrive, rerun `analyze_images.py`, replace each dashed placeholder in `svg_output/`, reconcile every `no-crop` container to the measured native ratio, then rerun the final SVG quality check so the gate covers the changed sources. 🚧 **Image readiness GATE**: When any required resource row is `Needs-Manual`, every expected file and derived slice output MUST exist under `<project_path>/images/` before the first active Step 7 sub-step. If any file is absent, pause and list the exact filenames; do not run `finalize_svg.py`, `svg_to_pptx.py`, or any other export path, and never ship the dashed placeholder. After the files arrive, rerun `analyze_images.py`, replace each dashed placeholder in `svg_output/`, reconcile every `no-crop` container to the measured native ratio, then rerun the final SVG quality check so the gate covers the changed sources.
After the separate readiness gate above has supplied every required manual file, the final SVG quality check closes each usable terminal §VIII row through `spec_lock.md images`, the exact locked file, and a real `<image href>`; it rejects unplanned/wrong-path references and also validates Sourced provenance/license records, image-specific visible credits, and effective per-placement pixel scale under `meet` / `slice` / `none`. After the separate readiness gate above has supplied every required manual file, the final SVG quality check closes each usable terminal §VIII row through `spec_lock.md images`, the exact locked file, and a real `<image href>`; it rejects unplanned/wrong-path references and also validates Sourced provenance/license records, image-specific visible credits, and effective per-placement pixel scale under `meet` / `slice` / `none`.
@@ -25,10 +25,11 @@ Global stop/continue rules for all four top-level routes, plus concrete failure
| Execution context is fresh, resumed, restarted, compacted/summary-only, external, or unknown | Yes until rebuilt | Read complete Design Spec, then lock, once; reload triggered inputs and latest completed SVG when mid-deck | No | Step 6 current page | | Execution context is fresh, resumed, restarted, compacted/summary-only, external, or unknown | Yes until rebuilt | Read complete Design Spec, then lock, once; reload triggered inputs and latest completed SVG when mid-deck | No | Step 6 current page |
| Step 3 rejects a legacy or incomplete template contract | Yes | Stop template consumption; create a new current workspace through Create Template from the original PPTX/reference, then return with its exact workspace root | Only when required source evidence or template choices are unavailable | Create Template → Generate PPTX Step 3 | | Step 3 rejects a legacy or incomplete template contract | Yes | Stop template consumption; create a new current workspace through Create Template from the original PPTX/reference, then return with its exact workspace root | Only when required source evidence or template choices are unavailable | Create Template → Generate PPTX Step 3 |
| Native formula marker validation or LaTeX compilation failure | Yes for the affected page | Repair the marker metadata or source LaTeX and rerun the SVG checker; there is no formula-image fallback | Clarify the intended equation only when the source is ambiguous | Active SVG authoring step | | Native formula marker validation or LaTeX compilation failure | Yes for the affected page | Repair the marker metadata or source LaTeX and rerun the SVG checker; there is no formula-image fallback | Clarify the intended equation only when the source is ambiguous | Active SVG authoring step |
| AI image generation failure | No | `auto`: follow A → B → Offline Manual. Explicit `api` / `host-native`: retry only that path, then mark the row `Needs-Manual` without switching automated providers | Only when missing files are required before export | Step 5 / Step 7 image readiness gate | | AI image generation failure | Default blocks at the recovery decision; Quick does not | Default `auto`: try A → B, then ask once to repair/retry, generate manually, or cancel/replan affected AI images; explicit `api` / `host-native`: retry only that path, then ask the same question. Quick removes the exhausted AI/dependent-slice jobs, replans them with native editable text/SVG or already prepared non-AI assets, and continues | Default chooses one outcome; manual files are still required before export | Default: Step 5 decision → same path, Step 4 affected-plan repair, or Step 7 image readiness gate; Quick: current resource plan |
| Web image search/download failure | No | Adjust query/source per image-searcher rules, then mark `Needs-Manual` if unresolved | Only if the resource is required and no acceptable substitute exists | Step 5 | | Web image search/download failure | No | Adjust query/source per image-searcher rules, then mark `Needs-Manual` if unresolved | Only if the resource is required and no acceptable substitute exists | Step 5 |
| Slice sheet missing | Yes for derived slice rows | Wait for parent sheet; run `slice_images.py`; rerun image analysis | Yes when sheet was manual/offline | Step 5 slice handling / Step 7 image readiness gate | | Slice sheet missing | Yes for derived slice rows | Wait for parent sheet; run `slice_images.py`; rerun image analysis | Yes when sheet was manual/offline | Step 5 slice handling / Step 7 image readiness gate |
| Residual `Pending` or `Failed` image row before Executor | Yes | Re-run path or mark `Needs-Manual` | Only if file must be supplied manually | Step 5 terminal-state check | | Strict-alpha slice failure | Yes for every named output | Return the parent to image preparation; correct an evidenced key/tolerance mismatch, then enlarge cells or split incompatible shape families and regenerate when content reaches an edge. A generated parent never substitutes for its missing slices | Only after the selected automated recovery is exhausted | Quick §2 resource closure / Default Step 5 slice handling |
| Residual `Pending` or `Failed` image row before Executor | Yes | Re-run the owning path; Default AI follows its three-outcome decision and reaches `Needs-Manual` only after manual confirmation; Quick automated AI follows its declared no-AI replan | Only when the owning rule requires a new choice or manual file | Step 5 terminal-state check |
| User replaces/adds images after analysis | No | Re-run `analyze_images.py` before reading image facts | No | Step 4/5/6 image-fact read | | User replaces/adds images after analysis | No | Re-run `analyze_images.py` before reading image facts | No | Step 4/5/6 image-fact read |
| Live preview fails to start | No | Continue generation; report that preview is unavailable | Only if user requires browser preview | Step 6 or `live-preview` Step 1 | | Live preview fails to start | No | Continue generation; report that preview is unavailable | Only if user requires browser preview | Step 6 or `live-preview` Step 1 |
| Live preview closed by user | No | Continue generation | No | Restart through `live-preview` only if requested | | Live preview closed by user | No | Continue generation | No | Restart through `live-preview` only if requested |
@@ -54,11 +55,11 @@ Global stop/continue rules for all four top-level routes, plus concrete failure
| Derived artifact stale | Regenerate it from its owning source. | | Derived artifact stale | Regenerate it from its owning source. |
| Required manual artifact missing | Pause and name the exact required artifacts; resume only after they exist. | | Required manual artifact missing | Pause and name the exact required artifacts; resume only after they exist. |
| Validation or export failure | Fix the owning source artifact, then rerun the failed operation and affected downstream operations only. | | Validation or export failure | Fix the owning source artifact, then rerun the failed operation and affected downstream operations only. |
| Confirmed execution choice cannot be honored | Keep the confirmed requirement visible. Retry the confirmed provider, mode, voice, effect, or path only as its owning workflow allows; if it remains unavailable, stop, request a new decision, or hand off through the owning workflow's declared manual fallback (e.g. `Needs-Manual` with a user summary). Never omit it or switch to another automated value or path silently. | | Confirmed execution choice cannot be honored | Keep the confirmed requirement visible. Retry the confirmed provider, mode, voice, effect, or path only as its owning workflow allows; if it remains unavailable, stop, request a new decision, or use the owning workflow's declared recovery. Quick's AI-generation exception is an explicit no-AI replan that preserves the communication job; it never substitutes another image source. |
**Missing values**: For a field in an existing artifact, follow only the exact requiredness, inference procedure, or fixed default declared by its owning schema or workflow; an active omission with no such rule stops at the owning boundary. Empty values, inactive conditional fields, whole artifacts, derived artifacts, and file-format attributes keep their own declared semantics—do not extend a fallback by analogy. Owning rules label their fallbacks with two terms used across this repository: a **declared-inference / declared-procedure fallback** states its missing condition and a bounded procedure that needs no new user decision; a **fixed compatibility default** states the exact fallback value, applied with one warning. **Missing values**: For a field in an existing artifact, follow only the exact requiredness, inference procedure, or fixed default declared by its owning schema or workflow; an active omission with no such rule stops at the owning boundary. Empty values, inactive conditional fields, whole artifacts, derived artifacts, and file-format attributes keep their own declared semantics—do not extend a fallback by analogy. Owning rules label their fallbacks with two terms used across this repository: a **declared-inference / declared-procedure fallback** states its missing condition and a bounded procedure that needs no new user decision; a **fixed compatibility default** states the exact fallback value, applied with one warning.
**Forbidden — silent downgrade**: Do not skip a required gate because a downstream command might tolerate the missing file, and do not change a confirmed execution value merely to keep the route moving. Fix, pause, or request a new decision at the owning boundary. **Forbidden — silent downgrade**: Do not skip a required gate because a downstream command might tolerate the missing file, and do not change a confirmed execution value merely to keep the route moving. Fix, pause, request a new decision, or apply an explicit profile-owned recovery at the owning boundary; Quick's documented AI-to-no-AI replan is the narrow exception.
**Proactive production resolution**: Keep final Stage-2 raw fields as evidence. **Proactive production resolution**: Keep final Stage-2 raw fields as evidence.
Resolve durable outcomes as explicit instruction → final Stage 2 → workflow defaults Resolve durable outcomes as explicit instruction → final Stage 2 → workflow defaults
@@ -92,7 +92,7 @@ Prepare source facts before initialization:
| Input | Action | | Input | Action |
|---|---| |---|---|
| Topic or requirements without supporting facts | Run [`topic-research`](../stages/topic-research.md) immediately and retain its Markdown supplement, fact-provenance JSON, and adopted webpage inputs for import | | Topic or requirements without supporting facts | Run [`topic-research`](../stages/topic-research.md) immediately and retain its Markdown supplement plus fact-provenance JSON; adopted webpage URLs remain inside that pair and are not import inputs |
| One or more PNG / JPEG / WebP files representing page frames under Image to PPTX | Do not call `source_to_md.py`; normalize single-page files and multi-frame contact sheets into the canonical ordered frame roster through that profile, then import the originals below | | One or more PNG / JPEG / WebP files representing page frames under Image to PPTX | Do not call `source_to_md.py`; normalize single-page files and multi-frame contact sheets into the canonical ordered frame roster through that profile, then import the originals below |
| PDF / DOCX / Office document / XLSX / XLSM / PPTX / EPUB / HTML / LaTeX / RST / web URL | Run `python3 ${SKILL_DIR}/scripts/source_to_md.py <file_or_URL_or_dir> [<file_or_URL_or_dir> ...]` | | PDF / DOCX / Office document / XLSX / XLSM / PPTX / EPUB / HTML / LaTeX / RST / web URL | Run `python3 ${SKILL_DIR}/scripts/source_to_md.py <file_or_URL_or_dir> [<file_or_URL_or_dir> ...]` |
| CSV / TSV | Read directly as a plain-text table source | | CSV / TSV | Read directly as a plain-text table source |
@@ -121,9 +121,9 @@ After reading every direct and converted source, assess factual sufficiency:
**Sufficiency test**: research only when the requested outcome would otherwise **Sufficiency test**: research only when the requested outcome would otherwise
require inventing, omitting, or leaving unsupported an externally verifiable require inventing, omitting, or leaving unsupported an externally verifiable
claim. File presence or length does not establish sufficiency. Research records claim. File presence or length does not establish sufficiency. Research records
the needed facts and adopted webpages. Those pages import as text-only evidence; the needed facts and adopted webpage URLs in its research pair. Project
independent AI / web / slice acquisition remains part of the initialization fetches none of those pages; independent AI / web / slice
resource preparation below. acquisition remains part of the resource preparation below.
**Conditional video-delivery context**: when the intended use is recorded, **Conditional video-delivery context**: when the intended use is recorded,
self-running, or video-directed—or an explicit final/literal narration script self-running, or video-directed—or an explicit final/literal narration script
@@ -184,9 +184,11 @@ python3 ${SKILL_DIR}/scripts/project_manager.py import-sources \
[projects/<research_slug>.md projects/<research_slug>.facts.json] [projects/<research_slug>.md projects/<research_slug>.facts.json]
``` ```
The facts JSON owns retained URLs. `project_manager.py` imports them The facts JSON is the sole URL authority, not a download queue.
automatically in text-only mode and fails incomplete reconciliation; do not `project_manager.py` imports it as an ordinary file and never expands its
repeat URLs or add page images to `<project>/images/`. `source_url` values. If normal web-image search is exhausted, follow
[`topic-research`](../stages/topic-research.md) § Hand-off to fetch one relevant
webpage package, review it, and copy only accepted images into the runtime pool.
Only inputs already under the repository's `projects/` tree move into the Only inputs already under the repository's `projects/` tree move into the
target project; every external path is copied and remains untouched. Use target project; every external path is copied and remains untouched. Use
@@ -276,9 +278,9 @@ the roster after the whole-roster check:
- an ordinary body-content frame and a density judgment for every page, adapted to the canvas and any user / template / style geometry; use `anchor`, `dense`, `breathing`, or an equivalent active-context distinction instead of one uniform fill level; - an ordinary body-content frame and a density judgment for every page, adapted to the canvas and any user / template / style geometry; use `anchor`, `dense`, `breathing`, or an equivalent active-context distinction instead of one uniform fill level;
- for each page not bound to literal supplied geometry, a primary visual zone and page-scale composition direction tied to its core message; use cards or equal grids when the content relationship calls for them, not as the automatic page grammar; - for each page not bound to literal supplied geometry, a primary visual zone and page-scale composition direction tied to its core message; use cards or equal grids when the content relationship calls for them, not as the automatic page grammar;
- for each page, preserve its semantic units, source-stated qualitative relationships, intended entry, and outcome so §3 can make the sole Structure decision before geometry; - for each page, preserve its semantic units, source-stated qualitative relationships, intended entry, and outcome so §3 can make the sole Structure decision before geometry;
- when useful, one transient deck-level visual motif with an identity or - when useful, a transient deck-level visual motif system with an identity or
communication job, a recognizable invariant, and deliberate variation across communication job, a recognizable invariant, and a reuse mode: fixed chrome,
applicable page roles; omit it when restraint serves the deck better; adaptive variation, or both; omit it when restraint serves the deck better;
- the resource decisions needed for immediate preparation. Required operational - the resource decisions needed for immediate preparation. Required operational
image manifests may carry filenames, page relationship, status, and image manifests may carry filenames, page relationship, status, and
generation/crop/focal cues, but do not create a general resource roster or an generation/crop/focal cues, but do not create a general resource roster or an
@@ -326,12 +328,16 @@ the complete carrier menu once and choose only the forms that communicate its
content best. A decision to use none of a carrier is valid; skipping the scan content best. A decision to use none of a carrier is valid; skipping the scan
because Quick is expected to be faster is not. because Quick is expected to be faster is not.
**Mandatory — proactive AI decorative lettering**: Apply **Reference — carriers compose, not compete**: Any page may combine a suitable subset of background paint, native shapes, editable text, photos/scenes, transparent illustration elements, decorative lettering, icons, and visualizations. Outside explicit requirements, no carrier is mandatory or mutually exclusive; choose the combination that makes the page coherent and visually resolved.
[`image-generator.md`](../../references/image-generator.md) §7's
callable-generator test before SVG authoring: a configured `IMAGE_BACKEND` is **Hard rule**: Credentials do not decide image need or the initial carrier plan. Do not inspect backend configuration or probe a provider before planning. Web acquisition retains zero-config providers; actual AI generation capability is resolved only during resource preparation, where the declared Quick no-AI replan below owns automated exhaustion.
Path A and a host-native image-generation tool is Path B; Offline Manual, web
search, or vision-only access does not qualify. When Path A/B is callable and **Default — visual grounding before a zero-image deck (may override when the user forbids images or charts / native SVG fully carry the visual burden)**: When the audience must recognize, experience, compare, or choose an externally verifiable subject, place, product, or setting, plan supplied/extracted or web images. Prepare AI imagery proactively where invented or deliberately stylized expression materially improves a planned visual job; this may be a complete image or transparent elements composed with other page carriers. This is a semantic decision, not an image-count quota.
the user has not forbidden AI, scan the frozen roster for display strings
**Default — prepare a composable illustration family when it strengthens the deck (may omit when no page benefits)**: Resolve the family before SVG authoring. Elements may repeat unchanged as title/corner chrome or vary as dominant anchors, supporting figures, and accents on any suitable page. Batch compatible elements through Illustration Sheets, split only for geometry/detail/quality conflicts, and keep final page composition in SVG under [`image-generator.md`](../../references/image-generator.md) §4.3.
**Mandatory — proactive AI decorative lettering**: When the user has not
forbidden AI, scan the frozen roster for display strings
anywhere in the deck. Exactly two questions decide eligibility: is that wording anywhere in the deck. Exactly two questions decide eligibility: is that wording
stable, and would an artistic treatment communicate better than native type? stable, and would an artistic treatment communicate better than native type?
Page role, string length, line count, and kind of noun never filter candidates — Page role, string length, line count, and kind of noun never filter candidates —
@@ -347,20 +353,26 @@ keep a native title wherever the page needs a searchable, selectable, or
outline-visible heading, with the lettering as its display layer. outline-visible heading, with the lettering as its display layer.
If a suitable set exists, prepare it without If a suitable set exists, prepare it without
a separate request: preserve the exact approved strings, use one ordinary AI a separate request: preserve the exact approved strings, use one ordinary AI
item for a single mark or batch several compatible marks through one item for a single mark or group several marks by compatible visual family and
Illustration Sheet and transparent slices, and keep ordinary title/chrome copy batch each family through its own Illustration Sheet and transparent slices.
native. A prepared wordmark and an editable title are not mutually exclusive: Give the model the marks' role, placement/background relationship, relative
visual weight, and energy; apply `image-generator.md` §5.3's
controlled-default/high-expression boundary. Split a family
only when its cell geometry or quality needs conflict, and keep ordinary
title/chrome copy native. A prepared wordmark
and an editable title are not mutually exclusive:
one page may carry the wordmark as its display layer while its subtitle, chrome, one page may carry the wordmark as its display layer while its subtitle, chrome,
and body stay native text, so a wish to keep that wording editable is answered and body stay native text, so a wish to keep that wording editable is answered
by the native layer rather than by dropping the lettering. Skip a scanned hook by the native layer rather than by dropping the lettering. AI permission is not
only when the user forbade AI or no generator is callable; do not add another coverage: never invent or alter copy, or create lettering merely
disqualifier. Offline Manual to justify AI usage. Once an existing string passes both eligibility questions,
alone does not trigger this proactive path; explicit user-required lettering skip it only when the user forbade AI; do not add another disqualifier. Actual
still follows the normal resource contract. generation capability is resolved during resource preparation, not eligibility.
| Communication job | Available carrier | | Communication job | Available carrier |
|---|---| |---|---|
| Real subject, place, product, evidence, atmosphere, or scene benefits from visual grounding | Supplied/extracted, web, AI, or sliced image | | Real subject, place, product, evidence, atmosphere, or scene benefits from visual grounding | Supplied/extracted, web, AI, or sliced image |
| Reusable title/corner decoration, a dominant illustrated anchor, supporting figure, or accent strengthens one or more page compositions | A coherent AI illustration family prepared as transparent `slice` assets and combined freely with other carriers |
| A compact semantic cue clarifies a category, process, KPI, state, navigation item, or real brand | Prepared project-local icon | | A compact semantic cue clarifies a category, process, KPI, state, navigation item, or real brand | Prepared project-local icon |
| Editable geometry can express a relationship, flow, emphasis, callout, symbol, or diagram | Basic SVG primitive, exact Office preset, Boolean result, then necessary freeform | | Editable geometry can express a relationship, flow, emphasis, callout, symbol, or diagram | Basic SVG primitive, exact Office preset, Boolean result, then necessary freeform |
| Values, categories, time, weights, or duration determine mark geometry | Value-driven chart | | Values, categories, time, weights, or duration determine mark geometry | Value-driven chart |
@@ -415,10 +427,16 @@ Prepare only the resource paths needed by the decided pages:
| Formula | Create no resource file. Retain the exact source LaTeX, then choose ordinary text, an inline native marker, or a block native marker under §3; the registered SVG preview is discarded by native export | | Formula | Create no resource file. Retain the exact source LaTeX, then choose ordinary text, an inline native marker, or a block native marker under §3; the registered SVG preview is discarded by native export |
| AI image | Follow `image-base.md` + `image-generator.md`; apply only the chosen rendering preset or exact custom bases, never blend unselected catalog identities, and keep `image_prompts.json` plus its human-readable sidecar | | AI image | Follow `image-base.md` + `image-generator.md`; apply only the chosen rendering preset or exact custom bases, never blend unselected catalog identities, and keep `image_prompts.json` plus its human-readable sidecar |
| Web image | Follow `image-base.md` + `image-searcher.md`; keep query/status data and `image_sources.json`, including any required on-slide attribution | | Web image | Follow `image-base.md` + `image-searcher.md`; keep query/status data and `image_sources.json`, including any required on-slide attribution |
| Illustration / lettering slice | Generate or obtain the parent sheet, run `slice_images.py --trim --alpha`, and place only the resulting transparent element files; a lettering sheet names every exact stable string and contains no scene or page chrome | | Composable illustration / lettering slice | Generate or obtain the parent sheet, run `slice_images.py --trim --alpha --bg KEY_HEX_FROM_PROMPT --strict-alpha`, and place only outputs from a successful strict cut; one illustration element may serve several pages, while a lettering sheet names every exact stable string and contains no scene or page chrome |
| Registered reconstruction group | Follow `image-generator.md` §4.4; keep full-canvas members registered with `crop=no-crop`, and materialize every required shared-plate member as an independent picture object | | Registered reconstruction group | Follow `image-generator.md` §4.4; keep full-canvas members registered with `crop=no-crop`, and materialize every required shared-plate member as an independent picture object |
| Visualization | Keep Chart values, Table cell topology, and chosen treatment in active context; load the applicable Chart/Table authority in §3 and write native replacement metadata only for an independently selected native-ready object | | Visualization | Keep Chart values, Table cell topology, and chosen treatment in active context; load the applicable Chart/Table authority in §3 and write native replacement metadata only for an independently selected native-ready object |
**Hard rule — planned slice closure**: Every placeable-element sheet carries `slice_grid` plus comma-separated `slice_names` in `image_prompts.json`. Deterministically enumerate those basenames and require every `images/<name>.png` after an exit-0 `slice_images.py --strict-alpha` run before SVG authoring; a `Generated` parent sheet never satisfies its named outputs. A nonzero slice run returns the parent to image preparation: correct only an evidenced key/tolerance mismatch, then enlarge cells or split incompatible shape families and regenerate when content reaches a cell edge. Repeating the same failing grid is not recovery. An explicitly selected manual path retains the marker, sets the affected item to `Needs-Manual` with `last_error`, and blocks Quick SVG/export until every named output is supplied and validated. Exhausted automated AI generation or dependent slicing instead follows the no-AI replan below; never retain an unresolved AI/slice row merely to continue.
**Validation**: Before §3, verify every required file-backed resource has a usable terminal state and every `slice_names` basename resolves to its real PNG output. Any missing name resumes the owning acquisition/slicing step; it cannot be deferred to the final SVG checker.
**Quick exhausted-automation no-AI replan**: Follow [`image-generator.md`](../../references/image-generator.md) §7 when an automated AI path or its required dependent slicing is exhausted: ask no path question, enter no manual fallback, remove the affected AI jobs and stale manifest entries, preserve their communication content with native editable text/SVG or already prepared non-AI assets, and continue the same run. An explicitly selected `manual` path remains subject to the file-readiness gate. To retain AI imagery after automated failure, repair the generation capability and start a new Quick run.
**Image inspection boundary**: acquisition-time suitability review follows the **Image inspection boundary**: acquisition-time suitability review follows the
owning AI/web/slice reference. Once resources reach terminal status, SVG owning AI/web/slice reference. Once resources reach terminal status, SVG
authoring follows `executor-image.md`'s narrow placement inspection: inspect only authoring follows `executor-image.md`'s narrow placement inspection: inspect only
@@ -437,8 +455,9 @@ strategy.
Every required file-backed resource must reach a usable terminal state before Every required file-backed resource must reach a usable terminal state before
its page. Web `Needs-Selection` blocks until one thumbnail is promoted or the its page. Web `Needs-Selection` blocks until one thumbnail is promoted or the
bounded ranked pages and materially different query variants are exhausted; bounded ranked pages and materially different query variants are exhausted;
only then may a vision-capable owner test one retained-page image URL at a time only then may a vision-capable owner fetch one adopted-page source package,
with `--from-url`; never bulk-download or use those pages as the initial pool. review its companion images, and promote only accepted files; never auto-expand
facts URLs or use those packages as the initial pool.
`Needs-Manual` blocks even when an unverified file exists. With no visual `Needs-Manual` blocks even when an unverified file exists. With no visual
capability, only the strict metadata-ranked web path may reach `Sourced`, and capability, only the strict metadata-ranked web path may reach `Sourced`, and
its provenance must say `selection_method: metadata-ranked` rather than imply its provenance must say `selection_method: metadata-ranked` rather than imply
@@ -576,9 +595,10 @@ under [`shared-standards-core.md`](../../references/shared-standards-core.md)
order. Use P01 as the visual-system calibration baseline and continue order. Use P01 as the visual-system calibration baseline and continue
directly through the remaining pages without a first-page checker or directly through the remaining pages without a first-page checker or
confirmation stop. When a motif was confirmation stop. When a motif was
resolved, reuse it selectively and vary scale, crop, density, position, or resolved, follow its reuse mode: exact repetition is valid for deliberate
content interaction instead of cloning one ornament. Keep this choice only in title/corner chrome, while adaptive motifs may vary scale, crop, density,
active context; create no planning artifact or approval stop. After every page position, or content interaction. Keep this choice only in active context;
create no planning artifact or approval stop. After every page
exists, run the one final checker below. Apply other supporting tools and exists, run the one final checker below. Apply other supporting tools and
stages only when their capability is actually needed. stages only when their capability is actually needed.
@@ -676,9 +696,12 @@ or lock.
- [x] All required source/resource preparation is complete - [x] All required source/resource preparation is complete
- [x] One mode and visual style were resolved, and every catalog source actually used was read - [x] One mode and visual style were resolved, and every catalog source actually used was read
- [x] Every page considered the complete visual-carrier menu without a coverage quota - [x] Every page considered suitable carrier combinations without a coverage quota or single-carrier assumption
- [x] Image need was decided independently of credentials; any zero-image deck is backed by an explicit no-image requirement or a roster whose visual burden is fully carried by charts / native SVG
- [x] Every `slice_names` output exists after an exit-0 strict-alpha run, and no page whose chosen composition depends on a slice was authored or exported without it
- [x] Every image-bearing page made its one pre-geometry composition decision - [x] Every image-bearing page made its one pre-geometry composition decision
- [x] Every image decided its own source from that page's subject and job — not inherited from the resolved visual style — and every externally verifiable subject deliberately not shown as itself was stated with its reason - [x] Every image decided its own source from that page's subject and job — not inherited from the resolved visual style — and every externally verifiable subject deliberately not shown as itself was stated with its reason
- [x] Every exhausted automated AI job was replanned under the declared no-AI rule, with its filename, attempted path, concrete error, and replacement carrier retained for final disclosure; N/A when no such replan occurred
- [x] Every selected formula uses the checker-valid ordinary/inline/block form with a matching visible SVG preview and no formula image resource - [x] Every selected formula uses the checker-valid ordinary/inline/block form with a matching visible SVG preview and no formula image resource
- [x] Every selected hyperlink uses a checker-valid inline/whole-object anchor and an exact external or same-deck target - [x] Every selected hyperlink uses a checker-valid inline/whole-object anchor and an exact external or same-deck target
- [x] Resolved SVG pages and their project-local references exist - [x] Resolved SVG pages and their project-local references exist
@@ -688,5 +711,5 @@ or lock.
- [x] Enabled notes were validated/generated and split; enabled custom motion ran through its owning stage - [x] Enabled notes were validated/generated and split; enabled custom motion ran through its owning stage
- [x] One native PPTX exists under `exports/` or the explicit output path - [x] One native PPTX exists under `exports/` or the explicit output path
- [x] No Strategist, confirmation, root project Design Spec, or lock artifact was created - [x] No Strategist, confirmation, root project Design Spec, or lock artifact was created
- [ ] **Next**: Report the base PPTX and any enabled narrated PPTX, raw/mixed/captured MP4, or capture-ready PPTX handoff, plus the resolved mode, visual style, and the image sources actually used - [ ] **Next**: Report the base PPTX and any enabled narrated PPTX, raw/mixed/captured MP4, or capture-ready PPTX handoff, plus the resolved mode, visual style, and the image sources actually used. For every no-AI replan, report the affected AI job, attempted path, concrete error, replacement carrier, and that retaining AI imagery requires repairing generation capability and starting a new Quick run
``` ```
@@ -1,5 +1,5 @@
--- ---
description: Generate source-intake stage that fills factual gaps and retains adopted webpage text evidence before planning or direct SVG authoring. description: Generate source-intake stage that fills factual gaps and records adopted webpage provenance before planning or direct SVG authoring.
--- ---
# Topic Research Stage # Topic Research Stage
@@ -8,16 +8,16 @@ description: Generate source-intake stage that fills factual gaps and retains ad
> Default Generate hands its output to Strategist; Quick Generate's main agent > Default Generate hands its output to Strategist; Quick Generate's main agent
> consumes the same output. Run immediately for topic-only input, or after > consumes the same output. Run immediately for topic-only input, or after
> supplied material is converted and read when it leaves planning-critical > supplied material is converted and read when it leaves planning-critical
> factual gaps. Output is a research supplement plus stable fact provenance for > factual gaps. Output is exactly a research supplement plus stable fact
> project import; its retained webpage URLs are imported as text evidence in > provenance for project import. Adopted webpage URLs remain in the provenance
> the active Generate profile's project-initialization handoff. > file and are not expanded during the project-initialization handoff.
This stage supplies facts needed to build the requested deck and preserves the This stage supplies facts needed to build the requested deck and records the
webpages actually adopted during that research. It makes no deck image webpages actually adopted during that research. It makes no deck image
selection and performs no independent image search or generation. During the selection and performs no independent image search or generation. The facts
handoff, `project_manager.py import-sources` converts each retained URL, archives JSON is provenance, not a page-download queue: `project_manager.py
its Markdown as text evidence, and retains remote inline-image links without import-sources` imports the research pair without fetching its `source_url`
downloading them. Those links are not an initial image pool. values. A page may be fetched later only for the bounded image fallback below.
## When to Run ## When to Run
@@ -102,7 +102,7 @@ inline-image links remain in the Markdown; no image files are downloaded.
| 3 | Reputable reporting or analysis when primary evidence is unavailable | | 3 | Reputable reporting or analysis when primary evidence is unavailable |
| Avoid | Unsourced reposts, unverifiable summaries, and stock-aggregator pages | | Avoid | Unsourced reposts, unverifiable summaries, and stock-aggregator pages |
**Retained webpage boundary**: Record a page URL only in the matching fact's **Adopted webpage boundary**: Record a page URL only in the matching fact's
`source_url`, and only when it materially supports that retained fact. Do not `source_url`, and only when it materially supports that retained fact. Do not
retain a page merely because its images may be useful, and do not add unopened retain a page merely because its images may be useful, and do not add unopened
search results or pages found through a separate image-search pass. search results or pages found through a separate image-search pass.
@@ -153,8 +153,8 @@ IDs are immutable within the file. Correct a claim under the same ID; never reus
## Hand-off ## Hand-off
After project initialization, import the research pair and user-supplied After project initialization, import the research pair and user-supplied
sources. `project_manager.py` reads unique `source_url` values from the v1 facts sources. The facts JSON is imported as an ordinary source file; its `source_url`
JSON automatically; do not repeat those URLs in the command or Markdown. values are never expanded, so this command performs no webpage retrieval.
```bash ```bash
python3 ${SKILL_DIR}/scripts/project_manager.py import-sources \ python3 ${SKILL_DIR}/scripts/project_manager.py import-sources \
@@ -162,25 +162,35 @@ python3 ${SKILL_DIR}/scripts/project_manager.py import-sources \
projects/<research_slug>.md projects/<research_slug>.facts.json projects/<research_slug>.md projects/<research_slug>.facts.json
``` ```
For retained URLs, `project_manager.py` invokes the webpage converter in If planning later exposes a required factual gap, return to this stage and
text-only mode and fails the import when any registered URL cannot be archived. repair the research supplement plus facts JSON before continuing. Do not let
It never copies page images into `<project>/images/`. A URL explicitly supplied Strategist or Quick consume a newly fetched claim without updating that pair.
as initial material keeps normal source-import behavior even when a later fact
cites it; text-only mode applies only to URLs auto-expanded from the facts JSON.
Only after normal web-image providers, ranked thumbnail pages, and materially Only after normal web-image providers, ranked thumbnail pages, and materially
different queries fail may an image owner with visual capability open one different queries fail may an image owner with visual capability select one
relevant retained page, choose one inline-image URL, download it with the relevant `source_url` from the facts JSON and fetch that one webpage package:
existing `image_search.py --from-url`, and inspect it. Try another only after
rejection; never bulk-download a page. Without vision, skip this fallback. ```bash
python3 ${SKILL_DIR}/scripts/source_to_md/web_to_md.py "<source_url>" \
-o <project_path>/sources/<source_slug>.md
```
This writes the page Markdown, conversion profile, and companion
`<source_slug>_files/` image package with `image_manifest.json`. Review that
package, then copy only accepted image files into `<project_path>/images/`;
leave every rejected or unused file in the source package. Fetch another page
only after the current package has no usable image. Do not pass the URL to
`project_manager.py import-sources`, which would promote every companion image
into the runtime pool. Without vision, skip this fallback and retain
`Needs-Manual`.
The imported research pair remains the compact evidence-facing content The imported research pair remains the compact evidence-facing content
authority, not a locked presentation contract. Default Generate has Strategist authority, not a locked presentation contract. Default Generate has Strategist
read both files completely before confirmation and use them with the imported read both files completely before confirmation and use them with the imported
source inventory to select the content, page roster, and image resource plan. source inventory to select the content, page roster, and image resource plan.
Quick Generate has the current agent do the same before its active-context Quick Generate has the current agent do the same before its active-context
content, design, and resource decisions. Reopen an imported webpage Markdown content, design, and resource decisions. A webpage Markdown enters the project
only for missing factual detail or the post-exhaustion single-image fallback. only through the post-exhaustion single-page image fallback above.
```markdown ```markdown
## ✅ Topic Research Complete ## ✅ Topic Research Complete
@@ -188,6 +198,6 @@ only for missing factual detail or the post-exhaustion single-image fallback.
- [x] Research supplement: `projects/<research_slug>.md` (N declared gaps covered) - [x] Research supplement: `projects/<research_slug>.md` (N declared gaps covered)
- [x] Fact provenance: `projects/<research_slug>.facts.json` (N external facts) - [x] Fact provenance: `projects/<research_slug>.facts.json` (N external facts)
- [x] Artifact contract validated: `## Research Brief`, no Markdown source list, `ppt-master.fact-provenance.v1`, unique sequential IDs, and Markdown/JSON agreement - [x] Artifact contract validated: `## Research Brief`, no Markdown source list, `ppt-master.fact-provenance.v1`, unique sequential IDs, and Markdown/JSON agreement
- [x] Retained webpage URLs: N unique `source_url` values in the facts JSON; no page images downloaded - [x] Adopted webpage URLs: N unique `source_url` values in the facts JSON; no webpage automatically imported and no image copied into the runtime pool
- [ ] **Next**: Default returns to [`generate-pptx`](../generate-pptx.md) Step 2; Quick returns to [`quick-generate`](../profiles/quick-generate.md) §2. Import the source artifacts plus research pair, then fully read the imported pair before planning or direct SVG authoring - [ ] **Next**: Default returns to [`generate-pptx`](../generate-pptx.md) Step 2; Quick returns to [`quick-generate`](../profiles/quick-generate.md) §2. Import the source artifacts plus research pair, then fully read the imported pair before planning or direct SVG authoring
``` ```