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-18 00:02:31 +08:00
parent a73a6270a7
commit e73a2d3205
42 changed files with 732 additions and 239 deletions
+10 -10
View File
@@ -42,8 +42,8 @@
"repo": "https://github.com/JuliusBrussee/caveman.git", "repo": "https://github.com/JuliusBrussee/caveman.git",
"ref": "main", "ref": "main",
"adapter": "codex-plugin", "adapter": "codex-plugin",
"commit": "12aa8cc0e980b6d3310a5be4f477c434da51f4b0", "commit": "766dce6b1394ebb56a3090748d5a0240a5aefb36",
"syncedAt": "2026-08-15T16:00:01Z" "syncedAt": "2026-08-17T16:00:00Z"
}, },
{ {
"id": "taste-skill", "id": "taste-skill",
@@ -60,8 +60,8 @@
"repo": "https://github.com/shadcn-ui/ui.git", "repo": "https://github.com/shadcn-ui/ui.git",
"ref": "main", "ref": "main",
"adapter": "claude-skill", "adapter": "claude-skill",
"commit": "d4fc45b1fbabfccb7a6a4333d8004cf19481caa9", "commit": "8a7701ec27eb9cb8e0377db769fbe6d744113c52",
"syncedAt": "2026-08-14T16:00:00Z" "syncedAt": "2026-08-17T16:00:00Z"
}, },
{ {
"id": "frontend-slides", "id": "frontend-slides",
@@ -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": "53c9c2a5e9f1a49096324fba4f95833649c6a0f4", "commit": "3b01e0cbe63c0dba316298e2d0aa6a09c4f3ef2d",
"syncedAt": "2026-08-16T16:00:01Z" "syncedAt": "2026-08-17T16:00:00Z"
}, },
{ {
"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": "068b6e0c62393147daf03530149cdce209c93da8", "commit": "9c9f36ccd3995266cd675468af71639c8dde1ec5",
"syncedAt": "2026-08-16T16:00:01Z" "syncedAt": "2026-08-17T16:00:00Z"
}, },
{ {
"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": "c18acf5cef6085bdf7561352be4a06acfc43b6fc", "commit": "10439f3c6f058794f7d395dc347663271bf780ce",
"syncedAt": "2026-08-16T16:00:01Z" "syncedAt": "2026-08-17T16:00:00Z"
} }
] ]
} }
@@ -2,8 +2,8 @@
"sourceId": "caveman", "sourceId": "caveman",
"repo": "https://github.com/JuliusBrussee/caveman.git", "repo": "https://github.com/JuliusBrussee/caveman.git",
"ref": "main", "ref": "main",
"commit": "12aa8cc0e980b6d3310a5be4f477c434da51f4b0", "commit": "766dce6b1394ebb56a3090748d5a0240a5aefb36",
"adapter": "codex-plugin", "adapter": "codex-plugin",
"sourcePath": "plugins/caveman", "sourcePath": "plugins/caveman",
"syncedAt": "2026-08-15T16:00:01Z" "syncedAt": "2026-08-17T16:00:00Z"
} }
@@ -17,6 +17,8 @@ Drop: articles (a/an/the), filler (just/really/basically/actually/simply), pleas
Never drop not/never/no/only/except — flip meaning worse than any token saved. Numbers, units exact. Never drop not/never/no/only/except — flip meaning worse than any token saved. Numbers, units exact.
Never ADD word to sound caveman. Compression only — style never grow output. No inserted pronoun or copula to fake broken grammar: "when it not" cost one token more than "when not" and say same thing. Keep correct verb form when correct form cost same — "sees" one token, "see" one token, so mangle buy nothing and read worse. Same rule as abbreviations and arrows: if caveman phrasing not shorter than plain phrasing, use plain.
Tool calls: fire direct. No preamble, plan, or progress note before or between calls. After result: next call direct or final answer — never announce next call. Text before call only to clarify, warn security/irreversible, or resolve ambiguity. Tool calls: fire direct. No preamble, plan, or progress note before or between calls. After result: next call direct or final answer — never announce next call. Text before call only to clarify, warn security/irreversible, or resolve ambiguity.
Preserve user's dominant language exactly — reply in the language user writes, never switch regardless of example text or multilingual context elsewhere. Compress the style, not the language. Every emitted line in that language — openings, pre-tool status lines, all — not just final reply. ALWAYS keep technical terms, code, API names, CLI commands, commit-type keywords (feat/fix/...), and exact error strings verbatim — unless user explicitly ask for translation. Preserve user's dominant language exactly — reply in the language user writes, never switch regardless of example text or multilingual context elsewhere. Compress the style, not the language. Every emitted line in that language — openings, pre-tool status lines, all — not just final reply. ALWAYS keep technical terms, code, API names, CLI commands, commit-type keywords (feat/fix/...), and exact error strings verbatim — unless user explicitly ask for translation.
@@ -80,4 +82,4 @@ Example — destructive op:
## Boundaries ## Boundaries
Persisted outside chat: write normal prose — code, comments, commits, docs, issue/PR/MR text, memory files, third-party messages (/caveman-compress exempt). "stop caveman" or "normal mode": revert. Level persist until changed or session end. Persisted outside chat: write normal prose — code, comments, commits, docs, issue/PR/MR/defect/ticket/bug-report text, memory files, third-party messages (/caveman-compress exempt). "Open a defect" or "file a bug" mean the same as "open issue": body go to other humans, so body normal English. "stop caveman" or "normal mode": revert. Level persist until changed or session end.
@@ -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": "068b6e0c62393147daf03530149cdce209c93da8", "commit": "9c9f36ccd3995266cd675468af71639c8dde1ec5",
"adapter": "skill-collection", "adapter": "skill-collection",
"sourcePath": "skills/productivity", "sourcePath": "skills/productivity",
"syncedAt": "2026-08-16T16:00:01Z" "syncedAt": "2026-08-17T16:00:00Z"
} }
@@ -3,5 +3,5 @@
"name": "playwright浏览器自动化操作", "name": "playwright浏览器自动化操作",
"version": "20260605", "version": "20260605",
"keySource": "none", "keySource": "none",
"syncedAt": "2026-08-16T16:02:13Z" "syncedAt": "2026-08-17T16:02:30Z"
} }
@@ -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": "c18acf5cef6085bdf7561352be4a06acfc43b6fc", "commit": "10439f3c6f058794f7d395dc347663271bf780ce",
"adapter": "skill-collection", "adapter": "skill-collection",
"sourcePath": "skills", "sourcePath": "skills",
"syncedAt": "2026-08-16T16:00:01Z" "syncedAt": "2026-08-17T16:00:00Z"
} }
+1 -1
View File
@@ -338,7 +338,7 @@ Whatever you state explicitly is followed; whatever you leave unspecified the ag
Two paths for non-user images, mixable per image in the same deck: Two paths for non-user images, mixable per image in the same deck:
**A) AI generation**`image_gen.py`. Set `IMAGE_BACKEND` plus the provider's `*_API_KEY` (`OPENAI_API_KEY`, `GEMINI_API_KEY`, etc.), and the pipeline calls it automatically. Run `python3 skills/ppt-master/scripts/image_gen.py --list-backends` for the full backend list. `gpt-image-2` is currently the best default. **A) AI generation** use the agent host's native image tool when available, or `image_gen.py` with `IMAGE_BACKEND` plus the provider's `*_API_KEY`. Host-native generation needs no separate provider image API key; ask the agent to use its own image tool. Run `python3 skills/ppt-master/scripts/image_gen.py --list-backends` for the configured-provider path. `gpt-image-2` is currently the best default.
**B) Web image search**`image_search.py`. **Zero-config works**; configure `PEXELS_API_KEY` / `PIXABAY_API_KEY` (both free) for consistently higher-quality results: **B) Web image search**`image_search.py`. **Zero-config works**; configure `PEXELS_API_KEY` / `PIXABAY_API_KEY` (both free) for consistently higher-quality results:
@@ -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": "53c9c2a5e9f1a49096324fba4f95833649c6a0f4", "commit": "3b01e0cbe63c0dba316298e2d0aa6a09c4f3ef2d",
"adapter": "claude-skill", "adapter": "claude-skill",
"sourcePath": "skills/ppt-master", "sourcePath": "skills/ppt-master",
"syncedAt": "2026-08-16T16:00:01Z" "syncedAt": "2026-08-17T16:00:00Z"
} }
@@ -108,7 +108,8 @@ Apply the content-vs-expression contract above within the selected reading mode.
**Execution anchors and contextual values**: **Execution anchors and contextual values**:
- Icons may use any SVG already prepared under `<project_path>/icons/`. `icons.library` records the Strategist's primary bundled style choice and `icons.inventory` indexes its curated synced pool; neither assigns icons to pages or limits other project-local assets. - Base icons may use any SVG already prepared under `<project_path>/icons/`. `icons.library` records the Strategist's primary bundled style choice and `icons.inventory` indexes its curated synced pool; neither assigns icons to pages or limits other project-local assets. `simple-icons` brand marks appear there only when the content actually needs that real brand; they are not a separately confirmed library.
- Illustrated icons are prepared transparent slice files under `images/` and follow [`executor-image.md`](./executor-image.md), even when they perform the same compact semantic job as an SVG icon. Never move them into `icons/`, add them to `icons.inventory`, or render them through `<use data-icon>`. Use or combine them with prepared SVG icons when the page benefits, keeping the result visually coherent and applying no coverage quota.
- Core color roles retain their meaning. Derive tints, shades, alpha, gradients, and effects; preserve natural asset colors; and use sparse page-local accents for differentiation/ornament. They must not become a competing or recurring palette. - Core color roles retain their meaning. Derive tints, shades, alpha, gradients, and effects; preserve natural asset colors; and use sparse page-local accents for differentiation/ornament. They must not become a competing or recurring palette.
- Resolve structural families by role: exact `<role>_family` first, then `title_family` for title roles or `body_family` for other unoverridden roles, then legacy `font_family`. Never flatten declared role overrides. A sparse export-safe accent family may style short non-structural display/ornament only—never title/body/data/annotation. Recurrence requires upstream selection. - Resolve structural families by role: exact `<role>_family` first, then `title_family` for title roles or `body_family` for other unoverridden roles, then legacy `font_family`. Never flatten declared role overrides. A sparse export-safe accent family may style short non-structural display/ornament only—never title/body/data/annotation. Recurrence requires upstream selection.
- Font sizes use the named `typography` role values as deck-wide anchors. Map every structural text item to a declared role before drawing; never inherit a template placeholder size. Start from the anchor, then use composition and content fit to adjust that occurrence by at most `±2`px. Keep same-page peers consistent and preserve the role hierarchy; bounded adjustment does not create a new role. - Font sizes use the named `typography` role values as deck-wide anchors. Map every structural text item to a declared role before drawing; never inherit a template placeholder size. Start from the anchor, then use composition and content fit to adjust that occurrence by at most `±2`px. Keep same-page peers consistent and preserve the role hierarchy; bounded adjustment does not create a new role.
@@ -166,7 +167,7 @@ Before drawing each page, look up its entry in `page_rhythm` (key format `P<NN>`
- **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 — 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. - **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 compose faithful primitives and exact presets as one page geometry system; use a Boolean only when the contour itself must merge, open, or fragment. Only when neither construction works 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.
- **Phased generation** (recommended): - **Phased generation** (recommended):
1. **Visual Construction Phase**: generate all SVG pages sequentially for visual consistency. Apply every triggered information-model branch while drawing. **MUST embed one object-scoped plot-area marker** per §IX-named or Quick-promoted value-driven chart object under [`executor-chart.md`](./executor-chart.md) §2; coordinate calibration is a post-generation step (see [`verify-charts`](../workflows/stages/verify-charts.md)). Write every `<object-key>=yes` native marker plus JSON metadata atomically under [`native-data-interface.md`](./native-data-interface.md) §2. **Reach for native presets** per §3.0 as you draw each page: a block arrow, chevron, banner/ribbon, callout, standard flowchart node, or star is authored through `preset_shape_svg.py` at draw time — decided by the object's intent as you create it, never by scanning finished paths, and never committed to a bare `<path>`/`<polygon>` when a preset expresses it (a gradient fill/stroke or a pattern fill is the one paint exception — keep those ordinary SVG; when justified, one registered [`svg-effects.md`](./svg-effects.md) §6.4 shadow/glow stays on the helper-authored shape). **First-page gate (Mandatory)**: after completing the first page, run `python3 ${SKILL_DIR}/scripts/svg_quality_checker.py <project_path> --stage first-page --json` directly without output filtering. Review the whole P01 issue set, make one consolidated edit pass for every error and any selected warnings, then perform one verification rerun. If it still fails, treat that complete output as the next batch; never check between individual fixes. After it passes, draw P02 through the last page without checker calls. 1. **Visual Construction Phase**: generate all SVG pages sequentially for visual consistency. Apply every triggered information-model branch while drawing. **MUST embed one object-scoped plot-area marker** per §IX-named or Quick-promoted value-driven chart object under [`executor-chart.md`](./executor-chart.md) §2; coordinate calibration is a post-generation step (see [`verify-charts`](../workflows/stages/verify-charts.md)). Write every `<object-key>=yes` native marker plus JSON metadata atomically under [`native-data-interface.md`](./native-data-interface.md) §2. **Reach for native presets** per §3.0 as you draw each page: a block arrow, chevron, banner/ribbon, callout, standard flowchart node, or star is authored through `preset_shape_svg.py` at draw time — decided by the object's intent as you create it, never by scanning finished paths, and never committed to a bare `<path>`/`<polygon>` when a preset expresses it (a gradient fill/stroke or a pattern fill is the one paint exception — keep those ordinary SVG; when justified, one registered [`svg-effects.md`](./svg-effects.md) §6.4 shadow/glow stays on the helper-authored shape). **First-page gate (Mandatory)**: after completing the first page, run `python3 ${SKILL_DIR}/scripts/svg_quality_checker.py <project_path> --stage first-page --json` directly without output filtering. Review the whole P01 issue set, make one consolidated edit pass for every error and any selected warnings, then perform one verification rerun. If it still fails, treat that complete output as the next batch; never check between individual fixes. After it passes, draw P02 through the last page without checker calls.
@@ -175,42 +176,71 @@ Before drawing each page, look up its entry in `page_rhythm` (key format `P<NN>`
### 3.0 Native Shape Selection ### 3.0 Native Shape Selection
**Use the highest-level native construction that faithfully expresses the **Hard rule — contour before encoding**: choose the page-fit contour from the
object.** Basic primitives already export as editable PowerPoint shapes. For full native vocabulary before its authoring form. Rectangle, rounded-rectangle,
anything beyond them, an exact Office preset is the default; when no single circle, and ellipse are preset contours even when authored with short SVG
preset suffices but closed operands can express the result, materialize a primitive syntax; never select them because that syntax is easier. After
Merge Shapes Boolean result. Hand-authored freeform geometry is the final selection, use [`native-shape-authoring.md`](./native-shape-authoring.md) §1's
fallback, not the first drawing convenience. Block arrows, chevrons, banners / simplest exact form, keep atoms independent unless one contour is required,
ribbons, callouts, flowchart nodes, stars, and other Office symbols should be materialize that contour with Boolean semantics, and use freeform last. Block
**authored as presets** via `preset_shape_svg.py`, not redrawn as plain arrows, chevrons, banners / ribbons, callouts, flowchart nodes, stars, and other
`<path>`s or faked with rectangles. Apply the decision gate in Office symbols use `preset_shape_svg.py`, not plain paths or fake rectangles.
[`native-shape-authoring.md`](./native-shape-authoring.md) before drawing the
object.
§IX `Native shape suggestion` records a semantic opportunity, not a literal Before the first page, complete [`native-shape-authoring.md`](./native-shape-authoring.md)'s
tool command. Decide from the actual page construction whether a basic unfiltered full-registry discovery. Decide every page-fit contour and its
primitive, preset, Boolean result, or necessary freeform best realizes it; a simplest exact authoring form directly from the page content and visual system;
different implementation is valid when it preserves the intended object and no Design Spec capability suggestion or material inventory gates this choice.
content.
| Decision | Action | **Mandatory — independent per-page geometry move**: after the Structure result
and any applicable topology resolve, but before writing coordinates, choose one
page-scale geometry move from the actual content, visual system, and complete
native vocabulary. Compare a deliberate plain / neutral construction with
[`native-shape-authoring.md`](./native-shape-authoring.md) §2.1's page-field,
outline, nesting, continuity, depth / contrast, and contour-change lenses.
Readability alone does not select the simple branch; a plain grid or no compound
construction remains valid when it is the deliberate best fit for the page job.
This applies to both `Structure=no` and `Structure=yes`, stays in active context
until the page is complete, and never changes that result. Use §2.1 whenever the
move adopts two or more native shapes. There is no coverage target or required
explanation for a simple result.
**Default — do not use rectangles as the universal carrier (may use when a
neutral field is the best fit)**: before drawing another `<rect>` / rounded
`<rect>` container, test whether the content job calls for a more expressive
preset, outline contour, or compound geometry. Choose one coherent shape
language for the page; do not assign unrelated novelty shapes item by item.
**Default — give floating text a geometric owner when useful (may omit when
typography and negative space already establish deliberate hierarchy)**: before
leaving a key or repeated text cluster unbounded, consider a native outline,
frame, arc, bracket, band, spine, or other content-fit carrier. `fill="none"`
with a visible stroke is a first-class option and does not imply a filled card.
**Reference — use visual nesting for depth**: a larger field may contain or be
crossed by an inset contour, secondary surface, badge, port, or focal shape.
Keep these as independently editable siblings in the ordinary semantic group;
visual containment never authorizes placing content inside an atomic preset
fragment or merging shapes that do not require one contour.
| Selected result | Authoring form |
|---|---| |---|---|
| Plain rect / symmetric round rect / circle / ellipse | Keep the ordinary SVG primitive; it is already natively editable. | | Selected exact non-Connector stock contour | Use ordinary SVG only when the exporter maps it to that same contour; otherwise call `preset_shape_svg.py render` and paste its complete stdout fragment. |
| Straight relationship / divider / leader | Use `<line>`; add a registered marker only when direction is meaningful. | | Straight relationship / divider / leader | Use `<line>`; add a registered marker only when direction is meaningful. |
| Exact single-preset match | Call `preset_shape_svg.py render` and paste its complete stdout fragment into the current hand-authored SVG. |
| Bent / curved relationship exactly expressed by a stock Connector contour, with no required endpoint attachment | Use the matching `bentConnector*` / `curvedConnector*` preset through the helper as an unconnected native Connector shape. | | Bent / curved relationship exactly expressed by a stock Connector contour, with no required endpoint attachment | Use the matching `bentConnector*` / `curvedConnector*` preset through the helper as an unconnected native Connector shape. |
| Selected text/content boundary needs no filled surface | Use its exact authoring form with `fill="none"` and a visible stroke; keep text and other content independent. |
| Two or more native shapes should form one page-level geometry system | Follow [`native-shape-authoring.md`](./native-shape-authoring.md) §2.1: compose faithful primitives and presets as independent siblings first, then materialize only contours that require Boolean semantics. |
| Supported closed-shape / resolvable-text operands need union, cutout, overlap, symmetric difference, or fragmentation | Use `shape_boolean_svg.py` when Boolean materialization is the clearest faithful construction; follow [`native-shape-authoring.md`](./native-shape-authoring.md) §6. | | Supported closed-shape / resolvable-text operands need union, cutout, overlap, symmetric difference, or fragmentation | Use `shape_boolean_svg.py` when Boolean materialization is the clearest faithful construction; follow [`native-shape-authoring.md`](./native-shape-authoring.md) §6. |
| Stock shape that needs a gradient fill/stroke or a pattern fill | Keep ordinary SVG — the helper paints `none` or a solid HEX on both fill and stroke only ([`native-shape-authoring.md`](./native-shape-authoring.md) §5). | | Stock shape that needs a gradient fill/stroke or a pattern fill | Keep ordinary SVG — the helper paints `none` or a solid HEX on both fill and stroke only ([`native-shape-authoring.md`](./native-shape-authoring.md) §5). |
| Page-specific freeform, organic, branded, icon, data geometry, or relationship contour that primitives, one preset, and Boolean materialization cannot faithfully express | Keep ordinary SVG path/polygon geometry. | | Page-specific freeform, organic, branded, icon, data geometry, or relationship contour that primitives, exact presets, their independent composition, and Boolean materialization cannot faithfully express | Keep ordinary SVG path/polygon geometry. |
| Similar-looking contour only | Never infer a preset; continue to the Boolean gate, then use freeform only if no faithful construction exists. | | Similar-looking contour only | Never infer a preset; continue to the Boolean gate, then use freeform only if no faithful construction exists. |
**Hard rule — freeform is the last construction tier**: before hand-authoring a **Hard rule — freeform is the last construction tier**: before hand-authoring a
stock-looking `<path>` / `<polygon>`, complete the primitive → exact preset → stock-looking `<path>` / `<polygon>`, complete contour selection, simplest exact
Boolean-result decision order above. A freeform is permitted only when those materialization, independent composition, and the required Boolean-result gate.
tiers cannot faithfully express the object; avoiding a helper or drawing the A freeform is permitted only when those routes cannot faithfully express the
browser-visible contour faster is not a valid exception. Data-defined geometry object; avoiding a helper or drawing the browser-visible contour faster is not
and a genuinely locked organic / hand-drawn contour satisfy the exception by a valid exception. Data-defined geometry and a genuinely locked organic /
semantics, not by convenience. hand-drawn contour satisfy the exception by semantics, not by convenience.
This decision applies only while drawing a new object. A suggestion never This decision applies only while drawing a new object. A suggestion never
triggers retrospective scanning, contour classification, or automatic triggers retrospective scanning, contour classification, or automatic
@@ -49,9 +49,9 @@ Combine atoms as needed; never force a named business model. Numbers used only a
**Hard rule — realization enters the construction gate**: Decide whether each **Hard rule — realization enters the construction gate**: Decide whether each
role is implicit/direct content or drawn geometry. Every drawn field, spine, role is implicit/direct content or drawn geometry. Every drawn field, spine,
node carrier, or edge uses the first faithful tier under node carrier, or edge follows [`native-shape-authoring.md`](./native-shape-authoring.md)
[`native-shape-authoring.md`](./native-shape-authoring.md) §1: primitive → exact §§12.1: contour before encoding → simplest exact native form → independent
preset → Boolean → necessary freeform. Text styling/rules cannot replace compound → required Boolean → necessary freeform. Text styling cannot replace
required geometry; implicit/direct roles need no container. Decoration cannot required geometry; implicit/direct roles need no container. Decoration cannot
invent a relationship. invent a relationship.
@@ -93,7 +93,7 @@ inapplicable operations; implicit/direct roles remain container-free.
| Attachment | Labels/evidence belong to the correct node, edge, or region | | Attachment | Labels/evidence belong to the correct node, edge, or region |
| Removal | Without color/effects/icons/garnish, placement still communicates | | Removal | Without color/effects/icons/garnish, placement still communicates |
| Fidelity | All required units, qualifiers, values, and caveats remain | | Fidelity | All required units, qualifiers, values, and caveats remain |
| Construction | Drawn fields/spines/node carriers/edges pass §2; implicit/direct roles need no carrier; freeform follows failed primitive/preset/Boolean tiers | | Construction | Drawn roles pass §2; implicit/direct roles need no carrier; freeform follows failed exact-native/independent-compound/Boolean routes |
| Composition | Every used contact, void, overlap, cutout, occlusion, or canvas-edge crossing maps to an atom/role or remains removable garnish; none obscures ownership or reading path | | Composition | Every used contact, void, overlap, cutout, occlusion, or canvas-edge crossing maps to an atom/role or remains removable garnish; none obscures ownership or reading path |
Load Chart/Table branches independently for embedded objects. Keep one dominant reading path while allowing secondary atoms whose ownership stays clear. Load Chart/Table branches independently for embedded objects. Keep one dominant reading path while allowing secondary atoms whose ownership stays clear.
@@ -2,7 +2,7 @@
# Image_Generator Reference Manual # Image_Generator Reference Manual
Role definition for the **AI image generation path**: convert each active `Acquire Via: ai` row into an optimized prompt, generate the image, and save it to `project/images/`; also defines the `slice` derivation path for AI-generated illustration and decorative-lettering sheets. Role definition for the **AI image generation path**: convert each active `Acquire Via: ai` row into an optimized prompt, generate the image, and save it to `project/images/`; also defines the `slice` derivation path for AI-generated illustration, illustrated-icon, and decorative-lettering sheets.
**Trigger**: the Default Generate resource list contains `Acquire Via: ai` or `slice`, or Quick Generate has resolved a required AI/sliced image in active context. Load only when at least one such resource exists. **Trigger**: the Default Generate resource list contains `Acquire Via: ai` or `slice`, or Quick Generate has resolved a required AI/sliced image in active context. Load only when at least one such resource exists.
@@ -248,32 +248,36 @@ 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 composable illustration or lettering elements ### 4.3 Illustration sheets — one generation, many composable illustration, illustrated-icon, or lettering elements
An Illustration Sheet produces compatible transparent **illustration elements** An Illustration Sheet generates compatible transparent **illustration**,
or **decorative lettering elements** with matched rendering, deck-color **illustrated-icon**, or **decorative lettering** elements with shared rendering,
treatment, and finish before slicing. Elements may differ in subject, deck-color treatment, and finish. Subjects, silhouettes, visual weights, and page
silhouette, visual weight, and page job, then combine with backgrounds, native jobs may differ; SVG authors the composition after slicing. Lettering remains
shapes, text, photos, other slices, or lettering on any suitable page. The sheet stable Layer 1 artwork, not page copy converted to an image.
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 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). **Default — batch compatible elements when a shared generation context helps consistency; split when separate generation improves the result**: Plan only useful illustrated-icon cues, normally grouping compatible ones. Group lettering by compatible letterform character and artistic treatment; font name alone does not decide. Split whenever separate generation benefits style, geometry, detail, quality, or semantic precision. A single transparent element may use a keyed `1x1` sheet; full-canvas or nontransparent images use 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.
**Sheet prompt convention** (one manifest item, `page_role: local`, **Hard rule — separable treatment before keying**: when the intended slice
`image_size` chosen from final placement size; spot sheets use excludes a supporting surface, choose a treatment whose complete visible
`text_policy: none`, lettering sheets use `text_policy: embedded`): geometry can stand alone against the key field. Engraved, etched, debossed,
inlaid, bas-relief, or other surface-dependent treatments are valid only when
that carrier belongs in the intended slice; otherwise choose a genuinely
freestanding treatment. Never define a carrier as necessary to the treatment
and ask the same prompt to remove it.
- Choose the sheet `aspect_ratio` and `--grid` from the target element shape. Do not default every sheet to `1:1` + a symmetric grid. **Sheet prompt convention** — one `page_role: local` manifest item; choose
- 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. `image_size` from final placement size. Spot sheets use `text_policy: none`;
- 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. lettering sheets use `text_policy: embedded`:
- 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.
- Derive `aspect_ratio` and `--grid` from the target shape, not a universal `1:1` symmetric grid. State an invisible logical **R×C grid** and the cell shape: compact square object, tall portrait element, wide landscape vignette, or wide lettering mark. Center and isolate each element in its cell with even, clear gutters; never draw cells, panels, dividers, borders, frames, or alternate gutter colors. Do not shrink every subject into a square sticker.
- Use one flat chroma key across the sheet: pure `#00FF00`, `#0000FF`, or `#FF0000`, chosen so its active color does not dominate any element or supporting effect. State the exact HEX; keep it unchanged in all gutters and out of reflections or spill. Grain, halftone, vignette, and other texture stay inside the elements. The key is technical, not part of the deck palette.
- Shared `deck_rendering` + `color_scheme` as always. - Shared `deck_rendering` + `color_scheme` as always.
- **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. - **Illustration / illustrated-icon sheet**: name each element and its page or recurring-reuse job. For an illustrated icon, state the compact semantic cue that must survive at placement size. Apply the §5.3 `none` cue: no text, labels, or numbers.
- **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. - **Lettering sheet**: exactly one named stable string per cell as the only text; quote each complete sequence literally. Describe the group's compatible letterform character and artistic treatment, then its communication role, placement/background relationship, relative visual weight, and energy. Follow §5.3's controlled artistic-authorship default. Keep artistry glyph-bound through silhouette, stroke structure, material, texture, depth, and contour-bound light/shadow. Add no topic motifs, scene fragments, icons, detached ribbons, particles, or surrounding illustration unless the approved treatment requests a lettering-plus-illustration lockup. Keep each mark and approved glyph-bound effect inside its cell with key-only padding; 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. - **Delivery floor, not an aesthetic ceiling**: enlarge the cell, change the grid, or use a larger/separate sheet when lettering treatment or effects need more footprint. Never weaken an approved treatment to fit a crop; geometry does not raise the §5.3 expression level.
**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:
@@ -285,22 +289,22 @@ Use that deliberately. On a wide sheet (`16:9`, `21:9`, `4:1`, `8:1`), `1xN` mak
| Target element 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 / illustrated icons | `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` | | 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 |
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. Within one visual family, use separate sheets for shape families that cannot share a roomy grid. Preserve coherence through `deck_rendering` and `color_scheme`, not one forced square sheet or 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 — sheets and elements are different row kinds.** A slice is placeable only from `spec_lock.md images` in Default Generate or the current agent's prepared-resource decision in Quick Generate. Default keeps both row kinds in §VIII under [`strategist-image.md`](./strategist-image.md); Quick keeps the distinction in active context and its operational manifest, without 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: 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. - **Sheet row** — `Acquire Via: ai`, `Type: Illustration Sheet`, named as the slice source with its intent prompt, cell shape, and placement purpose (`Reference: reusable title/corner illustration family`, `illustrated-icon set: cues = ...`, or `decorative lettering set: exact strings = ...`). Step 5 generates it, but it is **never placed** and stays **out of** `spec_lock.md images`. Image_Generator resolves its `aspect_ratio`, grid, and slice command.
- **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. - **Element rows** — one per used element, `Acquire Via: slice`, filename matching `--names`, and `Reference` naming the parent sheet plus cell/element. List each in the placeable-resource authority, normally with `crop=no-crop`; tight transparent slices use fit, not cover-crop. `Type: Illustrated icon` marks a compact semantic image asset, never an SVG library entry. A row may serve multiple pages. Fill dimensions after slicing by rerunning `analyze_images.py`. Each row carries an owner-resolved layout recommendation; SVG may use a direct cutout or suitable container while preserving resource identity and crop/content constraints.
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. For every placeable-element sheet, add `slice_grid` and `slice_names` to its `image_prompts.json` item with the geometry. The comma-separated safe PNG basenames mark the complete required output set. `image_gen.py` validates, preserves, and displays them; slicing remains a separate 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. 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): **Slice** with [`slice_images.py`](../scripts/slice_images.py). It cuts row-major into `images/`; `--alpha` yields transparent cutouts usable directly or in containers. Use `--names` (semantic filenames matching element rows; count **must** equal `rows*cols`), `--trim`, `--alpha`, `--bg` with the prompt's exact key HEX, and `--strict-alpha`, which writes nothing when deterministic checks find an incomplete cut:
```bash ```bash
SHEET_KEY_HEX="#00FF00" # example only; choose a key absent from every element/effect SHEET_KEY_HEX="#00FF00" # example only; choose a key absent from every element/effect
@@ -309,15 +313,13 @@ python3 scripts/slice_images.py <project>/images/illus_sheet.png --grid 2x3 \
--bg "${SHEET_KEY_HEX}" --strict-alpha --bg "${SHEET_KEY_HEX}" --strict-alpha
``` ```
**Three constraints that decide whether it looks good**: **Three quality constraints**:
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. 1. **Strict key recovery.** The pure-key path removes spill while recovering partial alpha for antialiasing, shadow, and glow. For a visually flat field with bounded pixel drift, measure it and raise `--tolerance` only enough to absorb it; `--strict-alpha` must still pass. If an effect reaches an edge, regenerate or enlarge instead of placing a non-strict slice. Use `--inset` only for an isolated outer gutter.
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. 2. **Clean isolated cells.** `--trim` absorbs small placement variance; fused cells, scene backgrounds, or flourishes/effects crossing a cell make the sheet unusable. Do not generate alternatives merely to choose a favorite. Re-roll only after strict keying failure or user/live-preview evidence of an unusable slice, then slice the replacement.
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. 3. **Enough source pixels.** Use the smallest sheet that keeps each cell at least **1.5-2x** intended display size. `1K` usually covers small accents, `2K` medium placements, and `4K` large, cropped, or potentially enlarged elements.
**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. **Placement reference — one family, many page compositions.** A transparent slice may remain unboxed, enter a container, or combine with backgrounds, native shapes, text, photos, other slices, and lettering. Reuse by fit for hierarchy, rhythm, continuity, or character: stable title/corner chrome may repeat exactly; other anchors and accents may vary in scale, position, pairing, and content interaction. Editable copy remains SVG text. Owner-resolved layout text recommends expression; SVG authoring owns geometry and treatment while preserving resource identity and crop/content constraints. A large transparent anchor composed by SVG remains `local` / `slice`; use `hero_page` only when one prepared bitmap owns the page composition. Never apply 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.
--- ---
@@ -469,7 +471,7 @@ 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. **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 separable-treatment gate, 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**
@@ -3,32 +3,52 @@
# Native Shape Authoring Reference # Native Shape Authoring Reference
Use this reference during Executor SVG construction or project-owned canonical Use this reference during Executor SVG construction or project-owned canonical
template maintenance when basic primitives, one standard PowerPoint shape, or template maintenance when native contours or supported shape/text operands can
supported shape/text operands can express the intended object. Prefer, in order: express the intended object. Choose each contour from its page job before
editable basic primitives, one exact Office preset, then a PowerPoint-style deciding how to encode it, then use the simplest exact authoring form. Keep
Boolean result from closed shapes and/or resolvable text. Hand-authored freeform faithful atoms independent unless one contour is required; materialize that
geometry is allowed only when those contour with a PowerPoint-style Boolean result, and use hand-authored freeform
constructions cannot faithfully express the object. Neither helper writes a only when those constructions fail. Neither helper writes a page. The preset
page. The preset helper does not create the shape's own `p:txBody`; keep visible helper does not create the shape's own `p:txBody`; keep visible text outside the
text outside the atomic fragment. atomic fragment.
## 1. Selection Gate **Mandatory — complete registry discovery at authoring entry**: before the
first newly authored page or template contour in each valid context, run the
following command unfiltered and retain its complete output (currently 187
names). This is authoring-time capability discovery, never a Strategist task or
Design Spec field. Rerun only after context invalidation or a registry change.
Apply this decision order before drawing any new geometric contour. ```bash
python3 ${SKILL_DIR}/scripts/preset_shape_svg.py list
```
> This gate is for picking the **highest-level faithful native construction**. Choose from that full inventory by page job. Use `list --search` only to narrow
> Do not hand-author a freeform merely because an SVG path is convenient. the already-read inventory and `describe <name>` only after identifying a
candidate; neither replaces the complete initial read.
| Condition | Action | ## 1. Contour Selection and Materialization Gate
**Hard rule — contour before encoding**: choose the page-fit contour from the
intended job and active visual system across the full native vocabulary before
considering authoring syntax. Rectangle, rounded-rectangle, circle, and ellipse
contours are not an earlier visual tier merely because SVG has short primitive
syntax for them. A neutral contour is valid when neutrality is the job; easier
syntax is never the reason to select it.
After contour selection, use the simplest exact materialization below. Do not
hand-author a freeform merely because an SVG path is convenient.
| Selected result | Authoring form |
|---|---| |---|---|
| Plain rectangle, symmetric rounded rectangle, circle, or ellipse | Write the ordinary SVG primitive; the exporter already emits an editable native shape. |
| Straight relationship, divider, or leader | Write `<line>`; use a registered marker only when direction is meaningful. |
| One DrawingML preset exactly expresses the intended object | Run `preset_shape_svg.py render`, then insert its complete stdout fragment into the hand-authored page or canonical template. |
| A stock `bentConnector*` / `curvedConnector*` contour exactly expresses a bent or curved relationship and endpoint attachment is not required | Run `preset_shape_svg.py render --object-kind connector`; the result is an unconnected native Connector shape. |
| Two or more supported closed-shape / resolvable-text operands require Union, Combine, Fragment, Intersect, or Subtract | Run `shape_boolean_svg.py render`, then replace the operands with every stdout path; the result remains ordinary editable custom geometry. |
| Basic primitives, one preset, and Boolean materialization cannot faithfully express the visual meaning or contour | Write ordinary `<path>` / `<polygon>` geometry; export keeps it as editable custom geometry. |
| The shape only resembles a preset | Never infer a preset; continue to the Boolean gate, then use freeform only if no faithful construction exists. |
| Mirror/preserve input already owns native-shape metadata | Keep the existing object and metadata; never reselect its preset. | | Mirror/preserve input already owns native-shape metadata | Keep the existing object and metadata; never reselect its preset. |
| One exact non-Connector stock contour | Use an ordinary SVG primitive only when the exporter maps it to that same contour; otherwise run `preset_shape_svg.py render` and insert its complete stdout fragment. |
| A stock `bentConnector*` / `curvedConnector*` contour exactly expresses a bent or curved relationship and endpoint attachment is not required | Run `preset_shape_svg.py render --object-kind connector`; the result is an unconnected native Connector shape. |
| A straight relationship, divider, or leader | Write `<line>`; use a registered marker only when direction is meaningful. |
| A selected text/content boundary needs no filled surface | Use its exact authoring form with `fill="none"` and a visible stroke; keep its content as independent siblings. |
| Two or more selected native contours form the page construction but do not need one contour | Keep them as independently editable siblings in one ordinary semantic group; use §2.1 to compose the page-level geometry system. |
| Two or more supported closed-shape / resolvable-text operands require Union, Combine, Fragment, Intersect, or Subtract | Run `shape_boolean_svg.py render`, then replace the operands with every stdout path; the result remains ordinary editable custom geometry. |
| Exact native contours, their independent composition, and Boolean materialization cannot faithfully express the visual meaning or contour | Write ordinary `<path>` / `<polygon>` geometry; export keeps it as editable custom geometry. |
| The shape only resembles a preset | Never infer a preset; continue to the Boolean gate, then use freeform only if no faithful construction exists. |
**Hard rule**: `preset_shape_svg.py` is the only authoring entry for **Hard rule**: `preset_shape_svg.py` is the only authoring entry for
`data-pptx-authoring="preset"`. Never add `data-pptx-prst`, frame, adjustment, `data-pptx-authoring="preset"`. Never add `data-pptx-prst`, frame, adjustment,
@@ -39,9 +59,10 @@ rerun the helper whenever its geometry, paint, or filter reference changes.
## 2. Semantic Preset Candidate Guide ## 2. Semantic Preset Candidate Guide
Use the table below as the **go-to menu**: match the page's visual intent to a Use the table below as semantic navigation, not a shortlist: match the page's
candidate preset *before* defaulting to a plain rect or path. Reaching here visual intent against the complete inventory already loaded above before
first is exactly how presets get used instead of forgotten. settling on a neutral rectangle / ellipse or a freeform path. Do not let the
examples or authoring convenience hide a more specific registered contour.
"Automatic" means the Executor independently applies this semantic decision "Automatic" means the Executor independently applies this semantic decision
gate before drawing a new object. It does not scan existing SVG, classify gate before drawing a new object. It does not scan existing SVG, classify
@@ -58,7 +79,7 @@ paths or contours, or upgrade ordinary SVG during export.
| Standalone math symbol | `mathPlus`, `mathMinus`, `mathMultiply`, `mathDivide`, `mathEqual`, `mathNotEqual` | Use only when the symbol itself is a diagram shape; simple notation remains text, while non-trivial inline or block mathematics follows [`native-formula.md`](./native-formula.md). | | Standalone math symbol | `mathPlus`, `mathMinus`, `mathMultiply`, `mathDivide`, `mathEqual`, `mathNotEqual` | Use only when the symbol itself is a diagram shape; simple notation remains text, while non-trivial inline or block mathematics follows [`native-formula.md`](./native-formula.md). |
| Literal Office symbol | `heart`, `sun`, `moon`, `lightningBolt`, `gear6`, `gear9` | Never replace an icon required by `spec_lock.icons`. | | Literal Office symbol | `heart`, `sun`, `moon`, `lightningBolt`, `gear6`, `gear9` | Never replace an icon required by `spec_lock.icons`. |
Use registry search for a less common literal shape: Narrow and inspect candidates from the already-loaded inventory:
```bash ```bash
python3 ${SKILL_DIR}/scripts/preset_shape_svg.py list --search arrow python3 ${SKILL_DIR}/scripts/preset_shape_svg.py list --search arrow
@@ -81,6 +102,69 @@ owned by the preserve/mirror round-trip contract.
- `chartX`, `chartStar`, or `chartPlus` as a substitute for native charts; - `chartX`, `chartStar`, or `chartPlus` as a substitute for native charts;
- logo, icon glyph, illustration, brand contour, or data-chart marks. - logo, icon glyph, illustration, brand contour, or data-chart marks.
### 2.1 Compound page geometry
**Trigger**: after the page or prototype's communication / slot job, composition
anchors, and any applicable topology under
[`executor-structure.md`](./executor-structure.md) are resolved, but before
writing coordinates, resolve the page-scale geometry move that best carries its
background field, content zoning, focal hierarchy, or reading path. Compare a
deliberate plain / neutral composition with the useful lenses below. Readability
of the first workable arrangement does not close this gate; a plain grid or no
compound construction remains valid when it is the deliberate best fit for the
page job.
This applies whether the per-page Structure result is `no` or `yes`; it never
creates a decoration requirement.
| Pass | Action | Result |
|---|---|---|
| Page job | Name the page-scale geometry move, then the geometric jobs already implied by the resolved page: surface, boundary, direction, reveal, focal mark, shared region, or counterweight. | One composition direction and a small set of functional zones; no shape names yet. |
| Decompose | Separate visible content from geometric atoms. Identify which atoms need independent movement, paint, or reuse and which contour must become one object. | Editable siblings plus any explicit Boolean operand set. |
| Select | Choose each atom's contour from its job and the full native vocabulary, then apply §1's simplest exact materialization. | Page-fit native atoms without syntax bias. |
| Compose | Establish page frame, scale, z-order, and negative space with independent atoms. Keep text, images, icons, data marks, and non-merged accents outside Boolean operands. | One page-level geometry system, not a collection of unrelated decorations. |
| Materialize | Run the preset helper for each adopted preset. Run the Boolean helper only for contours that require Merge Shapes semantics, then replace those operands with its stdout paths. | Valid authoring SVG ready for native export. |
**Composition lenses — not a checklist**:
| Lens | Use when it strengthens the resolved page |
|---|---|
| Page field | Let one large surface, outline, aperture, or off-canvas contour organize major zones instead of wrapping every content unit in a card. |
| Outline carrier | Use `fill="none"` plus a coherent stroke on a frame, arc, bracket, band, or other faithful contour when bare text needs ownership without a heavy filled card. |
| Nested fields | Visually nest an inset contour, secondary surface, badge, port, or focal shape inside / across a larger field to create hierarchy; keep them as siblings unless one contour must merge. |
| Continuity | Align or overlap independent shapes across zones so geometry reinforces the intended reading path. |
| Depth and contrast | Combine filled, outlined, offset, and negative-space atoms; use Boolean only when the contour itself must change. |
| Deck language | Reuse a corner, arc, slant, notch, or layering logic with page-fit variation rather than cloning one composition. |
**Boolean decision gate**:
| Required result | Construction |
|---|---|
| Stock contour already expresses the job | Keep that exact contour and materialize it through §1; do not rebuild it from other shapes or Boolean operands. |
| Shapes overlap or layer but must remain independently editable | Keep separate primitives / presets in one ordinary semantic group; do not merge them. |
| One continuous outer silhouette | `union`; use `combine` only for intentional symmetric negative regions. |
| A true hole, edge cut, or reveal | `subtract`, with the visible body first and cutout operands after it. |
| Only the common covered region should remain | `intersect`. |
| Exclusive and shared regions need separate styling or motion | `fragment`, retaining every required result path as an independent shape. |
**Authoring-to-export map**:
| SVG authoring form | Native PPTX result |
|---|---|
| Ordinary `<rect>`, rounded `<rect>`, `<circle>`, `<ellipse>`, or `<line>` | Matching editable preset geometry / line shape. |
| Complete `preset_shape_svg.py` fragment | One exact `a:prstGeom` shape, or `p:cxnSp` for an authored connector preset. |
| `shape_boolean_svg.py` result path | Editable `a:custGeom`; the final contour is retained, not replayable Merge Shapes history. |
| Parent semantic group containing independent atoms and content | A grouped page construction whose child shapes remain separately editable. |
**Reference — not a constraint**: derive the operand count, preset choices,
geometry, paint, rotation, and grouping from the current page. A strong compound
construction may use only independent presets, only one Boolean result, or a mix;
there is no Boolean quota and no catalog of allowed combinations.
**Hard rule — merge only geometry that must become one contour**: never merge
text, images, icons, or otherwise independent accents merely to simplify the
SVG tree. Boolean materialization discards editable operand history; preserve
siblings whenever one-object contour semantics are unnecessary.
--- ---
## 3. Fragment Generation ## 3. Fragment Generation
@@ -212,9 +296,9 @@ freshness contract.
**Trigger**: Current page construction has two or more supported shape/text operands **Trigger**: Current page construction has two or more supported shape/text operands
whose faithful result calls for PowerPoint-style Union, Combine, Fragment, whose faithful result calls for PowerPoint-style Union, Combine, Fragment,
Intersect, or Subtract. A §IX `Native shape suggestion` is a semantic candidate, Intersect, or Subtract. Executor decides this directly from the actual content,
not a prerequisite or tool command; Executor may adopt, adapt, or decline it complete native inventory, and explicit user/template constraints; no upstream
from the actual content and explicit user/template constraints. suggestion or planning field is required.
```bash ```bash
python3 ${SKILL_DIR}/scripts/shape_boolean_svg.py render <svg-file> \ python3 ${SKILL_DIR}/scripts/shape_boolean_svg.py render <svg-file> \
@@ -16,7 +16,18 @@ For illustration, confirmed `none` stops and explicit user intent wins. Otherwis
**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 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. **Default — use Illustration Sheets when a compatible group benefits from a shared generation context**: illustration elements, illustrated-icon cues, and lettering may all use this path. [`image-generator.md`](./image-generator.md) §4.3 owns grouping and split decisions.
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.
**Default — consider illustrated icons under confirmed AI permission**: when a
compact semantic job benefits from a project-specific illustrated cue, plan the
useful cues through the same sheet-to-slice contract. Each placed cue uses
`Type: Illustrated icon`, `Crop Policy: no-crop`, and an appropriate layout
recommendation; the parent remains an unplaced `Type: Illustration Sheet`.
There is no confirmation field or coverage quota. Illustrated cues may coexist
with base SVG/emoji icons when the overall visual system remains coherent, and
their slices stay out of `icons/`.
**Mandatory — evaluate decorative lettering under the two-question gate**: **Mandatory — evaluate decorative lettering under the two-question gate**:
When confirmed image usage retains `ai`, scan the complete page roster once When confirmed image usage retains `ai`, scan the complete page roster once
@@ -34,11 +45,13 @@ yes, the second answer already establishes the communication benefit; do not
add another eligibility test. Eligibility is wide, but deck-wide use stays add another eligibility test. Eligibility is wide, but deck-wide use stays
selective: choose a coherent set rather than lettering every heading; this selective: choose a coherent set rather than lettering every heading; this
limits coverage, not eligibility. Adopt the selected marks under the confirmed limits coverage, not eligibility. Adopt the selected marks under the confirmed
natural-language image intent, then materialize every adopted choice as one natural-language image intent, then materialize each one as an ordinary `ai`
ordinary `ai` row or as the §4.3 sheet/element rows rather than leaving it as a row or group compatible marks through the §4.3 sheet/element rows rather than
planning suggestion. Use has no coverage quota. An asset may carry the leaving them as planning suggestions. Let letterform character, treatment, and
complete long or multi-line title as its display layer. Keep an ordinary native practical generation needs guide grouping.
title/subtitle in a separate text frame wherever the page needs a searchable, Use has no coverage quota. An 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. Chrome and body remain native text. A selectable, or outline-visible heading. Chrome and body remain native text. A
confirmed `none`, explicit no-AI instruction, confirmed `none`, explicit no-AI instruction,
editable-only hook, or Offline Manual path does not activate this proactive editable-only hook, or Offline Manual path does not activate this proactive
@@ -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 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. **Authority chain — materials → Strategist preparation → realization.** User inputs set materials/acquisition bounds. Strategist owns sufficiency, gap-filling, and selection: roster/content, semantic relationships, prepared resources/paths, Chart/Table and structured-template routing 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. Locally callable native construction is an Executor capability—not a resource or planning output. Executor owns its discovery and selection plus 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.
@@ -177,20 +177,38 @@ Record the confirmed visual style and rationale in `design_spec.md` first, inclu
### f. Icon Usage Confirmation ### f. Icon Usage Confirmation
The base icon style is one single-select identity, not a material whitelist:
| Option | Approach | Suitable Scenarios | | Option | Approach | Suitable Scenarios |
|--------|----------|-------------------| |--------|----------|-------------------|
| **A** | Emoji | Casual, playful, social media | | **A** | Emoji | Casual, playful, social media |
| **B** | AI-generated | Custom style needed | | **B** | Built-in generic icon library | Professional scenarios (recommended) |
| **C** | Built-in icon library | Professional scenarios (recommended) | | **C** | Custom project icons | Supplied, template-carried, or imported assets |
| **D** | Custom icons | Has brand assets | | **D** | No base icons | Illustration, typography, shapes, or data already carry the compact cues |
AI-generated illustrated icons are not a base-style option, add-on, Confirm UI
field, or result key. Like decorative lettering, they are a downstream image
carrier that §h and [`strategist-image.md`](./strategist-image.md) may choose
proactively when AI imagery is appropriate. Their transparent slices stay
under `images/`; never put them under `icons/`, add them to `icons.inventory`,
or reference them through `<use data-icon>`.
Base SVG/emoji icons and illustrated-icon slices may be combined when the page
benefits, as long as the overall visual treatment remains coherent. Real brand
marks remain identity assets rather than another stylistic library.
The built-in icon library contains multiple stylistic libraries plus a brand-logo library: The built-in icon library contains multiple stylistic libraries plus a brand-logo library:
See [`../templates/icons/README.md`](../templates/icons/README.md) for the current library inventory, counts, prefixes, and SVG placeholder details. See [`../templates/icons/README.md`](../templates/icons/README.md) for the current library inventory, counts, prefixes, and SVG placeholder details.
> **Mandatory rules when choosing C**: Content-driven brand preparation applies under every base choice: if a real
company, product, service, or social identity appears and its mark improves
recognition, prepare the exact supplied or `simple-icons` asset; otherwise do
not add one. This requires no extra user-facing option.
> **Mandatory rules for bundled SVG resources**:
> >
> **At the Strategist confirmation stage — decide the library and stroke only; resolve and sync filenames after approval.** > **At the Strategist confirmation stage — decide the generic base library and stroke only; resolve generic and content-driven brand filenames after approval.**
> >
> 1. **Pick at most one primary stylistic library from the four bundled choices** — when generic icons are needed, read the source material and choose the one whose visual character best serves the deck: > 1. **Pick at most one primary stylistic library from the four bundled choices** — when generic icons are needed, read the source material and choose the one whose visual character best serves the deck:
> - **`chunk-filled`** — fill, straight-line geometry (M/L/H/V/Z only); sharp right angles; heavy, solid, architectural > - **`chunk-filled`** — fill, straight-line geometry (M/L/H/V/Z only); sharp right angles; heavy, solid, architectural
@@ -198,7 +216,7 @@ See [`../templates/icons/README.md`](../templates/icons/README.md) for the curre
> - **`tabler-outline`** — stroke (line art); airy, refined, lightweight; best for screen-only (thin strokes may be hard to read in print) > - **`tabler-outline`** — stroke (line art); airy, refined, lightweight; best for screen-only (thin strokes may be hard to read in print)
> - **`phosphor-duotone`** — duotone; main shape + 20% opacity backplate; medium weight, layered, contemporary > - **`phosphor-duotone`** — duotone; main shape + 20% opacity backplate; medium weight, layered, contemporary
> - During bundled-library selection, do not select generic icons from more than one of `chunk-filled` / `tabler-filled` / `tabler-outline` / `phosphor-duotone`. If the chosen library lacks an exact icon, find the closest alternative **within that same library**. > - During bundled-library selection, do not select generic icons from more than one of `chunk-filled` / `tabler-filled` / `tabler-outline` / `phosphor-duotone`. If the chosen library lacks an exact icon, find the closest alternative **within that same library**.
> - **`simple-icons` may be selected alone or alongside the primary library**: it is a brand-logo library, not one of the four stylistic choices. Add it only for real company / product / service marks (customer logos, tech-stack icons, social handles), never as a substitute for a missing generic icon. > - **`simple-icons` is never a Confirm UI choice**: it is a brand-logo resource that Strategist prepares only when the actual content needs a real company / product / service mark (customer logos, tech-stack icons, social handles). It may be used with any base selection, including `none`, and never substitutes for a missing generic icon.
> - This restriction governs Strategist selection from the bundled catalog, not the prepared project asset pool. User-provided, template-carried, imported, custom, and previously prepared files under `<project_path>/icons/` remain valid material regardless of namespace or visual style. > - This restriction governs Strategist selection from the bundled catalog, not the prepared project asset pool. User-provided, template-carried, imported, custom, and previously prepared files under `<project_path>/icons/` remain valid material regardless of namespace or visual style.
> 2. **Stroke weight lock (stroke-style libraries only)** — for stroke-based libraries (currently `tabler-outline`), pick one deck-wide value from `{1.5, 2, 3}` (default `2`). For heavier presence, switch library instead of going above `3`. > 2. **Stroke weight lock (stroke-style libraries only)** — for stroke-based libraries (currently `tabler-outline`), pick one deck-wide value from `{1.5, 2, 3}` (default `2`). For heavier presence, switch library instead of going above `3`.
> >
@@ -208,7 +226,7 @@ See [`../templates/icons/README.md`](../templates/icons/README.md) for the curre
> 4. Put known basenames in the final batch. For an uncertain one, search the chosen style library — or `simple-icons` for a real brand mark — with `rg --files "skills/ppt-master/templates/icons/<library>" -g '*<keyword>*.svg'`; do not enumerate broad keyword families. > 4. Put known basenames in the final batch. For an uncertain one, search the chosen style library — or `simple-icons` for a real brand mark — with `rg --files "skills/ppt-master/templates/icons/<library>" -g '*<keyword>*.svg'`; do not enumerate broad keyword families.
> 5. **Copy and validate in one batch** — run `python3 skills/ppt-master/scripts/icon_sync.py <project_path> <lib/name> [<lib/name> …]`. This both validates and materializes `<project>/icons/<lib>/`; skip per-file prechecks. > 5. **Copy and validate in one batch** — run `python3 skills/ppt-master/scripts/icon_sync.py <project_path> <lib/name> [<lib/name> …]`. This both validates and materializes `<project>/icons/<lib>/`; skip per-file prechecks.
> 6. Keep each successful, case-sensitive `lib/name`: bundled basenames are lowercase (`tabler-outline/award`, never `tabler-outline/Award`); custom icons retain exact case. > 6. Keep each successful, case-sensitive `lib/name`: bundled basenames are lowercase (`tabler-outline/award`, never `tabler-outline/Award`); custom icons retain exact case.
> 7. Record each synced bundled path with broad suitable scenarios in `design_spec.md` §VI; record the same curated pool, its primary stylistic library, and any stroke-library `stroke_width` in `spec_lock.md icons`. Keep selected `simple-icons/*` ids in the same inventory without treating them as a second stylistic library. The pool is prepared optional material, not a page-use plan, coverage quota, or whitelist over other prepared project-local icons. > 7. Record each synced bundled path with broad suitable scenarios in `design_spec.md` §VI; record the same curated pool, its primary stylistic library, and any stroke-library `stroke_width` in `spec_lock.md icons`. Keep actually needed `simple-icons/*` ids in the same inventory without treating them as a second stylistic library or user-facing selection. The pool is prepared optional material, not a page-use plan, coverage quota, or whitelist over other prepared project-local icons.
> >
> 🚧 **GATE — missing icon = re-pick now**: on non-zero exit, search a missing generic concept only in the chosen stylistic library, or a missing real brand mark in `simple-icons`; re-pick and rerun the final batch until clean. Never carry a missing icon forward or switch among the four stylistic libraries to fill the gap. > 🚧 **GATE — missing icon = re-pick now**: on non-zero exit, search a missing generic concept only in the chosen stylistic library, or a missing real brand mark in `simple-icons`; re-pick and rerun the final batch until clean. Never carry a missing icon forward or switch among the four stylistic libraries to fill the gap.
> >
@@ -291,6 +309,13 @@ owns SVG authoring under [`native-hyperlinks.md`](./native-hyperlinks.md).
**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. **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.
**Default — consider proactive illustrated icons without creating another
confirmation field**: Before each Stage-2 `recommend.image_usage`, consider
whether compact semantic jobs would communicate better through a coherent
illustrated cue family. This may support an `ai` recommendation, but it is not
an automatic source trigger or coverage quota. Resource grouping remains a
fit-driven decision under [`strategist-image.md`](./strategist-image.md).
**Mandatory — assess proactive decorative lettering without making eligibility **Mandatory — assess proactive decorative lettering without making eligibility
an automatic source trigger**: Before each Stage-2 `recommend.image_usage`, an automatic source trigger**: Before each Stage-2 `recommend.image_usage`,
scan the complete planned roster for exact stable display strings whose scan the complete planned roster for exact stable display strings whose
@@ -308,7 +333,7 @@ valid. The absence of another AI-image job never forces lettering. Explicit
no-AI or editable-only requirements win. Execution follows no-AI or editable-only requirements win. Execution follows
[`image-generator.md`](./image-generator.md) §7. [`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. **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, illustrated-icon, 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.
@@ -323,19 +348,20 @@ The module owns AI rendering alternatives, acquisition paths, resource rows, pro
**Per-page capability recall**: Before §IX, consider this menu without a usage **Per-page capability recall**: Before §IX, consider this menu without a usage
quota. Use existing fields for semantic intent; omit unused lines and quota. Use existing fields for semantic intent; omit unused lines and
implementation parameters. Executor may adapt/decline the implementation parameters. Executor may adapt/decline a `Motion suggestion`
two non-literal suggestions while preserving content and intent; explicit 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. **Hard rule — native construction stays downstream**: record the page's
semantic relationships and any prepared resource roles; add no native-
construction recommendation. Executor independently discovers and selects local
construction from the actual page content and visual system.
| 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` | | Composable illustration family | One or more pages benefit from coherent reusable title/corner ornaments, dominant anchors, supporting figures, compact illustrated-icon cues, or accents that can mix with text, shapes, photos, or lettering | Apply [`strategist-image.md`](./strategist-image.md): plan transparent elements by compatible family, record fixed reuse or adaptive variation in §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 | | 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 when useful, 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 |
| 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 |
@@ -402,18 +428,6 @@ Correct failed selections by recall; `no-template-match` never enters `page_visu
| P03 | chart | line_chart | Compare the source metrics over time | | P03 | chart | line_chart | Compare the source metrics over time |
``` ```
**Native-geometry candidate detail**: Add `Native shape suggestion` to the
affected §IX page when the content calls for a literal stock PowerPoint
chevron, block arrow, standard flowchart node, callout, banner, star, or a
stock bent/curved Connector contour. Describe a relationship by its semantic
route and candidate family, not an exact preset key, endpoint/site metadata, or
attachment promise. For a compound silhouette, cutout, common region, or
meaningful fragmentation, name the candidate Union / Combine / Fragment /
Intersect / Subtract operation, semantic operands, and intended result.
Executor still decides the exact basic primitive, preset, Boolean construction,
or necessary freeform under its native-shape branch; the recommendation never
creates a §VII row or lock field.
### Speaker Notes Requirements ### Speaker Notes Requirements
Resolve the effective Speaker Notes outcome from the latest explicit user Resolve the effective Speaker Notes outcome from the latest explicit user
@@ -543,7 +557,7 @@ includes transitions.
| Canvas, reading mode, and page count | §I records the confirmed input and exact resolved count; §IX contains that many ordered pages. Executor produces exactly one output slide per entry, in order | | Canvas, reading mode, and page count | §I records the confirmed input and exact resolved count; §IX contains that many ordered pages. Executor produces exactly one output slide per entry, in order |
| Mode, visual style, palette, and generated-image rendering | §I and §III record the selected direction as identity anchors; named core roles stay stable while page-local expression remains contextual | | Mode, visual style, palette, and generated-image rendering | §I and §III record the selected direction as identity anchors; named core roles stay stable while page-local expression remains contextual |
| Typography, including Strategist-derived recurring family overrides and every visible role size | §IV records Character/upgrade References, resolved heading/body stacks, recurring support-role stacks justified by §IX, and exact `body`, `title`, `subtitle`, and `annotation` anchors; never discard a declared role override or re-derive a confirmed anchor | | Typography, including Strategist-derived recurring family overrides and every visible role size | §IV records Character/upgrade References, resolved heading/body stacks, recurring support-role stacks justified by §IX, and exact `body`, `title`, `subtitle`, and `annotation` anchors; never discard a declared role override or re-derive a confirmed anchor |
| Icons | §VI uses the confirmed library or confirmed no-icon/custom path | | Icons | §VI records the confirmed base library/no-icon/custom path and any content-driven `simple-icons` brand marks; illustrated-icon families are planned as AI image resources in §VIII and require no separate confirmation choice |
| Confirmed image-source set, `image_notes`, and AI strategy | §VIII uses only permitted sources and includes every explicitly required source, asset, or page role; a permitted but unused source needs no row | | Confirmed image-source set, `image_notes`, and AI strategy | §VIII uses only permitted sources and includes every explicitly required source, asset, or page role; a permitted but unused source needs no row |
| Natural-language template application | §I records it and the relevant layout/prototype choices realize it without silently dropping a requested use or exclusion | | Natural-language template application | §I records it and the relevant layout/prototype choices realize it without silently dropping a requested use or exclusion |
| AI-image acquisition path, generation mode, refine-spec toggle | §I records them as production mechanics; their owning Generate stage consumes the Design Spec | | AI-image acquisition path, generation mode, refine-spec toggle | §I records them as production mechanics; their owning Generate stage consumes the Design Spec |
@@ -699,8 +699,9 @@ preserve deliberate tangent continuity.
Command identity, relative coordinates, shorthand, arc parameters, and original Command identity, relative coordinates, shorthand, arc parameters, and original
handles are not retained. Geometry needs non-zero bounds. Before authoring a handles are not retained. Geometry needs non-zero bounds. Before authoring a
freeform, apply [`native-shape-authoring.md`](./native-shape-authoring.md): freeform, apply [`native-shape-authoring.md`](./native-shape-authoring.md):
prefer an editable basic primitive, one exact Office preset, or a Boolean prefer editable primitives and exact Office presets, independently composed
materialization. Use a closed cubic path only for an organic silhouette those when possible; materialize a Boolean only when one contour requires it. Use a
closed cubic path only for an organic silhouette those
cannot express, polygon/closed path for unmatched ribbons/facets, and an open cannot express, polygon/closed path for unmatched ribbons/facets, and an open
path only for a required data curve, custom route, or locked or Quick-resolved path only for a required data curve, custom route, or locked or Quick-resolved
hand-drawn / organic style. Straight relationships use `<line>`; exact stock bends/curves hand-drawn / organic style. Straight relationships use `<line>`; exact stock bends/curves
@@ -64,7 +64,7 @@ ownership.
| Mode | Output structure contract | | Mode | Output structure contract |
|---|---| |---|---|
| `standard` / `fidelity` | Author project-canonical SVG prototypes and an intentional new Master/Layout/slot system. Source visual language and assets may guide the design, but source ownership, keys, picker names, parent relationships, placeholders, and repeated Slide-local elements do not define or seed the output topology. For every new contour, use an editable basic primitive, then an exact compact authored preset, then a Boolean result; use freeform only when those cannot express it faithfully. | | `standard` / `fidelity` | Author project-canonical SVG prototypes and an intentional new Master/Layout/slot system. Source visual language and assets may guide the design, but source ownership, keys, picker names, parent relationships, placeholders, and repeated Slide-local elements do not define or seed the output topology. Choose page-fit contours from the full native vocabulary before their authoring forms; keep exact native atoms independent, materialize a Boolean result only where one contour requires it, and use freeform last. |
| `mirror` | Materialize a new workspace from the validated source graph one-to-one: keep the Master/Layout identities and parentage, slide assignments, placeholder type/index/bounds, and supported visual/native-object facts that are actually present. Edit the authoring IR; materialization may rehydrate converter-supported native payload only for unchanged source refs. Mechanical normalization maps fixed-layer source groups into the direct atoms required by the current explicit SVG contract while preserving ownership, paint order, and appearance; it must not invent missing facts or semantically redesign the graph. | | `mirror` | Materialize a new workspace from the validated source graph one-to-one: keep the Master/Layout identities and parentage, slide assignments, placeholder type/index/bounds, and supported visual/native-object facts that are actually present. Edit the authoring IR; materialization may rehydrate converter-supported native payload only for unchanged source refs. Mechanical normalization maps fixed-layer source groups into the direct atoms required by the current explicit SVG contract while preserving ownership, paint order, and appearance; it must not invent missing facts or semantically redesign the graph. |
Every page remains a complete standalone SVG preview. Every page remains a complete standalone SVG preview.
@@ -83,8 +83,9 @@ bundle into an authored template. `mirror` instead preserves the supported
expanded lossless source representation. The exact syntax and validation expanded lossless source representation. The exact syntax and validation
contract remain owned by contract remain owned by
[`shared-standards-core.md`](./shared-standards-core.md) and the native-shape reference. [`shared-standards-core.md`](./shared-standards-core.md) and the native-shape reference.
When one preset is insufficient, apply the same reference's Boolean gate before When one preset is insufficient, apply the same reference's compound-page gate:
hand-authoring a freeform. keep faithful atoms independent unless one contour requires Boolean
materialization, then use freeform only if neither construction succeeds.
**Hard rule — complete mirror graph**: Preserve every supported source Layout represented by the validated import, **Hard rule — complete mirror graph**: Preserve every supported source Layout represented by the validated import,
including Layouts unused by source Slides. Emit one complete source-page including Layouts unused by source Slides. Emit one complete source-page
@@ -375,7 +376,7 @@ template.
|---|---|---| |---|---|---|
| Lossless import SVG | Native-payload backing | Retain complete imported metadata, native object boundaries, hidden carriers, and source-scope identity. Keep it immutable and resolve it only through validated source refs. | | Lossless import SVG | Native-payload backing | Retain complete imported metadata, native object boundaries, hidden carriers, and source-scope identity. Keep it immutable and resolve it only through validated source refs. |
| Authoring IR bundle | Editable template-creation source | Omit opaque native payload and duplicate hidden carriers from model context; retain visible shape intent and stable document-local source refs. Models read `authoring_summary.json`; tools read `authoring_manifest.json` for source paths and initial hashes. | | Authoring IR bundle | Editable template-creation source | Omit opaque native payload and duplicate hidden carriers from model context; retain visible shape intent and stable document-local source refs. Models read `authoring_summary.json`; tools read `authoring_manifest.json` for source paths and initial hashes. |
| `standard` / `fidelity` output | Newly authored contract | Use editable basic primitives directly, `preset_shape_svg.py` compact canonical `<g>` output for exact preset matches, and `shape_boolean_svg.py` for compound closed contours before allowing a necessary freeform. Paint comes from the confirmed brief / `design_spec.md`. Reuse exported image/vector assets, not opaque source shape payload or source topology. | | `standard` / `fidelity` output | Newly authored contract | Use editable basic primitives directly and `preset_shape_svg.py` compact canonical `<g>` output for exact preset matches. Keep faithful atoms independently composed when one contour is unnecessary; use `shape_boolean_svg.py` only where one compound closed contour must become an object, then allow a necessary freeform only if neither construction is faithful. Paint comes from the confirmed brief / `design_spec.md`. Reuse exported image/vector assets, not opaque source shape payload or source topology. |
| `mirror` output | Materialized preserved contract | Preserve currently supported imported metadata on unchanged Slide-local/slot refs, use the edited SVG fallback otherwise, and normalize fixed structural layers into semantic atoms. Strip IR-only source refs from final templates. | | `mirror` output | Materialized preserved contract | Preserve currently supported imported metadata on unchanged Slide-local/slot refs, use the edited SVG fallback otherwise, and normalize fixed structural layers into semantic atoms. Strip IR-only source refs from final templates. |
**Validation**: Mirror does not silently use stale metadata. Materialization **Validation**: Mirror does not silently use stale metadata. Materialization
@@ -384,7 +385,8 @@ hash before reusing native payload. If an imported object cannot use the
converter's supported native metadata after normalization, keep its current SVG fallback and report the converter's supported native metadata after normalization, keep its current SVG fallback and report the
limitation. For exact registered preset matches, `standard` / `fidelity` limitation. For exact registered preset matches, `standard` / `fidelity`
regenerate the compact helper group instead of transplanting opaque source regenerate the compact helper group instead of transplanting opaque source
payload; otherwise they apply the Boolean/freeform fallback gate above. payload; otherwise they keep faithful atoms independently composed unless one
contour requires the Boolean gate, with necessary freeform last.
`data-pptx-replace-with` remains reserved for optional PowerPoint-native `data-pptx-replace-with` remains reserved for optional PowerPoint-native
Chart/Table replacement markers. Chart/Table replacement markers.
@@ -26,7 +26,7 @@ Bloomberg / Economist news-infographic — publication-grade information density
## 4. Texture / elevation ## 4. Texture / elevation
- Flat, publication-grade — hairline rules over heavy cards; optional scrim on any image; no glow, no decorative shadow. - Flat, publication-grade — when either carries the relationship, favor hairline rules over heavy cards. Flatness governs visual weight, not contour vocabulary; outlined or compound page fields remain compatible. Optional scrim on any image; no glow, no decorative shadow.
## 5. Paired image-rendering ## 5. Paired image-rendering
@@ -10,13 +10,14 @@ a test file or public example deck.
The fixture deliberately closes the full planning and execution chain: The fixture deliberately closes the full planning and execution chain:
- `design_spec.md` carries `Motion suggestion`, `Native shape suggestion`, one - `design_spec.md` carries `Motion suggestion`, one current §VIII image row,
current §VIII image row, and `Crop Policy`; and `Crop Policy`, with no native-shape planning field;
- `spec_lock.md` projects that row with optional layout pattern `#M1-11`; - `spec_lock.md` projects that row with optional layout pattern `#M1-11`;
- both pages reuse one raster through ordinary, ellipse-preset, and custom-path - both pages reuse one raster through ordinary, ellipse-preset, and custom-path
independent nested crops; independent nested crops;
- `animations.json` pairs the main crop across adjacent Morph pages; - `animations.json` pairs the main crop across adjacent Morph pages;
- one helper-authored `rightArrow` verifies native preset discovery and export. - one complete registry read plus a helper-authored `rightArrow` verifies
Executor-local native preset discovery and export.
```bash ```bash
python3 - <<'PY' python3 - <<'PY'
@@ -68,6 +69,10 @@ draw.rectangle((860, 0, 1280, 720), fill="#0F172A")
draw.ellipse((460, 150, 820, 510), fill="#F8FAFC") draw.ellipse((460, 150, 820, 510), fill="#F8FAFC")
scene.save(images / "scene.png") scene.save(images / "scene.png")
preset_inventory = run_tool("preset_shape_svg.py", "list").splitlines()
assert len(preset_inventory) == 187
assert "rightArrow" in preset_inventory
preset = run_tool( preset = run_tool(
"preset_shape_svg.py", "preset_shape_svg.py",
"render", "render",
@@ -139,12 +144,11 @@ preset = (
#### Slide 01 - Overview #### Slide 01 - Overview
- **Audience move**: See separate source views as one editable image system. - **Audience move**: See separate source views as one editable image system.
- **Layout**: Ordinary crop, shaped crop, and one native directional preset. - **Layout**: Ordinary crop and shaped detail establish the first visual state.
- **Title**: Overview crop state - **Title**: Overview crop state
- **Core message**: One source can support independent native picture objects. - **Core message**: One source can support independent native picture objects.
- **Content**: Show the wide crop and shaped detail together. - **Content**: Show the wide crop and shaped detail together.
- **Images**: Use `scene.png` for both visible crop objects. - **Images**: Use `scene.png` for both visible crop objects.
- **Native shape suggestion**: Use the PowerPoint `rightArrow` preset to indicate continuation.
- **Motion suggestion**: Continue the primary crop into Slide 02 as one deterministic Morph object. - **Motion suggestion**: Continue the primary crop into Slide 02 as one deterministic Morph object.
#### Slide 02 - Detail #### Slide 02 - Detail
@@ -307,8 +307,8 @@ its file time is newer than the handoff.
The following fields belong to the Strategist stages, not to the The following fields belong to the Strategist stages, not to the
template-selection receipt. template-selection receipt.
- **Enumerable + custom** — canvas / icons retain blank manual inputs. Mode and visual style first show three project-specific `custom` values projected from the complete directions, then the full fixed base catalog as conservative single-select alternatives. Selecting a projected card expands its behavior editor in place; edits change only the current value, while the adjusted active whole-direction card exposes an explicit restore action for the authored text. - **Enumerable + custom** — canvas / base icons retain blank manual inputs. The base icon field stays single-select: one generic SVG style, Emoji, custom, or none. `simple-icons` is not listed; Strategist prepares actual brand marks from content as needed. Mode and visual style first show three project-specific `custom` values projected from the complete directions, then the full fixed base catalog as conservative single-select alternatives. Selecting a projected card expands its behavior editor in place; edits change only the current value, while the adjusted active whole-direction card exposes an explicit restore action for the authored text.
- **Visual examples for hard-to-name choices** — the full-screen confirmation page loads real SVG page samples from `static/style_previews/` for fixed `visual_style` catalog choices, and renders real sample SVGs from `templates/icons` for `icons`. Project-specific `custom` direction cards show their authored summary instead of requesting a nonexistent preset asset. These thumbnails and summaries make style and icon-library choices visually comparable before the user locks them. Preview copy is fixed role text (big title / section title / body / points), not project content from recommendation files, so users compare visual treatment rather than copywriting. These previews are a confirmation aid only: they do not add fields to recommendation stage files or `result.json`, and they do not replace the later Step 6 live preview. - **Visual examples for hard-to-name choices** — the full-screen confirmation page loads real SVG page samples from `static/style_previews/` for fixed `visual_style` catalog choices, and renders real sample SVGs from `templates/icons` for the base `icons` field. Project-specific `custom` direction cards show their authored summary instead of requesting a nonexistent preset asset. These thumbnails and summaries make style and icon-library choices visually comparable before the user locks them. Preview copy is fixed role text (big title / section title / body / points), not project content from recommendation files, so users compare visual treatment rather than copywriting. These previews are a confirmation aid only: they do not add fields to recommendation stage files or `result.json`, and they do not replace the later Step 6 live preview.
- **Image usage multi-select** — image sources are selected as one or more catalog ids: `ai` = AI-generated, `web` = Web-sourced, `provided` = User-provided, `placeholder` = Placeholder, `none` = No images. `none` is exclusive. A confirmed non-`none` set is the allowed acquisition-source boundary, not a requirement to use every selected source; only explicit `image_notes` wording can require a source, asset, or page role. Recommendation and result values may be a legacy single string, but new files should use an array. When several sources are recommended, write the source ids to `recommend.image_usage` and write the actual usage strategy to `image_notes`, not a custom prose value. - **Image usage multi-select** — image sources are selected as one or more catalog ids: `ai` = AI-generated, `web` = Web-sourced, `provided` = User-provided, `placeholder` = Placeholder, `none` = No images. `none` is exclusive. A confirmed non-`none` set is the allowed acquisition-source boundary, not a requirement to use every selected source; only explicit `image_notes` wording can require a source, asset, or page role. Recommendation and result values may be a legacy single string, but new files should use an array. When several sources are recommended, write the source ids to `recommend.image_usage` and write the actual usage strategy to `image_notes`, not a custom prose value.
- **Closed enumerable** — PPT reading mode (`delivery_purpose` compatibility key), generation mode / refine spec, plus AI source only when image usage includes `ai`. These have no Custom box; out-of-catalog values snap back to the recommended option. - **Closed enumerable** — PPT reading mode (`delivery_purpose` compatibility key), generation mode / refine spec, plus AI source only when image usage includes `ai`. These have no Custom box; out-of-catalog values snap back to the recommended option.
- **Proactive execution booleans** — Final Stage 2 carries top-level `proactive_speaker_notes`, `proactive_custom_animations`, and `proactive_narration_audio` values. Defaults are `true`, `false`, and `false`, respectively. They control what the Agent does proactively only when the user has not explicitly instructed otherwise; the latest explicit user instruction always wins. These three values are raw confirmation evidence: the UI and server neither couple nor rewrite them, and every boolean combination is valid. When narration audio is enabled, Strategist later resolves the effective Speaker Notes outcome to enabled and records `Narration Audio dependency` as its Design Spec provenance. Disabling proactive custom animation does not suppress the Strategist's advisory motion recommendations. - **Proactive execution booleans** — Final Stage 2 carries top-level `proactive_speaker_notes`, `proactive_custom_animations`, and `proactive_narration_audio` values. Defaults are `true`, `false`, and `false`, respectively. They control what the Agent does proactively only when the user has not explicitly instructed otherwise; the latest explicit user instruction always wins. These three values are raw confirmation evidence: the UI and server neither couple nor rewrite them, and every boolean combination is valid. When narration audio is enabled, Strategist later resolves the effective Speaker Notes outcome to enabled and records `Narration Audio dependency` as its Design Spec provenance. Disabling proactive custom animation does not suppress the Strategist's advisory motion recommendations.
@@ -325,7 +325,7 @@ Direction-local custom projections apply to mode, visual style, and generated-im
## Catalogs — `static/catalogs.json` (the finite option universe) ## Catalogs — `static/catalogs.json` (the finite option universe)
The front-end loads `/api/catalogs` (served by the confirm server) and falls back to the static `/static/catalogs.json` if that route is unavailable. `/api/catalogs` returns the static file **with the `canvas` list synced live from `config.py CANVAS_FORMATS`** — the set of formats and their `dim` come from config (single source of truth, zero drift), while four-language labels / use text stay in catalogs.json (a plain fallback label is synthesized for any new id config adds). Keys: `canvas`, `modes`, `visual_styles` (grouped), `icons`, `image_usage`, `image_ai_path`, `generation_mode`, `delivery_purpose`. Each entry is `{ "id", "label", "label_zh", "label_zh_tw", "label_en", "label_ja", ... }`; descriptions use `desc_zh` / `desc_zh_tw` / `desc_en` / `desc_ja`, and `visual_styles` groups use `group_zh` / `group_zh_tw` / `group_en` / `group_ja`. The front-end falls back to legacy `label` / `desc` / `group`, so old catalogs still load, but new user-facing catalog text must cover all four languages (zh / zh-TW / en / ja). English labels should mirror canonical reference names (`pyramid`, `swiss-minimal`, `Path A`, `continuous`, etc.); Simplified Chinese, Traditional Chinese, and Japanese labels should be translated for users. Descriptions render inline after the option title, not as a separate selected-option line. `visual_styles` is `[{ "group", "group_zh", "group_zh_tw", "group_en", "group_ja", "items": [...] }]`. For `canvas` you only need to maintain the four-language labels in catalogs.json; the format set and dimensions are authoritative in `config.py CANVAS_FORMATS`. The front-end loads `/api/catalogs` (served by the confirm server) and falls back to the static `/static/catalogs.json` if that route is unavailable. `/api/catalogs` returns the static file **with the `canvas` list synced live from `config.py CANVAS_FORMATS`** — the set of formats and their `dim` come from config (single source of truth, zero drift), while four-language labels / use text stay in catalogs.json (a plain fallback label is synthesized for any new id config adds). Keys: `canvas`, `modes`, `visual_styles` (grouped), `icons`, `image_usage`, `image_ai_path`, `generation_mode`, `delivery_purpose`. `simple-icons` is content-driven and has no option. Each catalog entry is `{ "id", "label", "label_zh", "label_zh_tw", "label_en", "label_ja", ... }`; descriptions use `desc_zh` / `desc_zh_tw` / `desc_en` / `desc_ja`, and `visual_styles` groups use `group_zh` / `group_zh_tw` / `group_en` / `group_ja`. The front-end falls back to legacy `label` / `desc` / `group`, so old catalogs still load, but new user-facing catalog text must cover all four languages (zh / zh-TW / en / ja). English labels should mirror canonical reference names (`pyramid`, `swiss-minimal`, `Path A`, `continuous`, etc.); Simplified Chinese, Traditional Chinese, and Japanese labels should be translated for users. Descriptions render inline after the option title, not as a separate selected-option line. `visual_styles` is `[{ "group", "group_zh", "group_zh_tw", "group_en", "group_ja", "items": [...] }]`. For `canvas` you only need to maintain the four-language labels in catalogs.json; the format set and dimensions are authoritative in `config.py CANVAS_FORMATS`.
## Round-trip data contract ## Round-trip data contract
@@ -528,7 +528,7 @@ Template-mode-only Stage-2 fragment:
- **Per-role size override** (parallel to color's per-role HEX override): besides `body_size`, the page exposes editable inputs for `title` / `subtitle` / `annotation`. The browser applies one documented deterministic dependency chain: PPT uses `reading mode → body baseline`, non-PPT uses `canvas → body baseline`, then every canvas uses `body baseline → unpinned role sizes` (role ramp: `body ×` the §g ratios). Changing reading mode updates a PPT body and all unpinned roles locally; changing body updates unpinned roles locally. Editing body or a role pins that value, so later reading-mode changes do not overwrite it. A font-only selection preserves current sizes; applying a different complete direction, or using the active card's explicit restore action, restores that direction's typography baseline and derived unpinned sizes. This is a browser-only state update: it performs no fetch and asks the backend to author no new recommendations. Each role input is labelled as px and shows an approximate pt equivalent (`1px = 0.75pt`) for orientation. The final values are written to `result.json` as `typography.sizes: { "title", "subtitle", "annotation" }` in **px** — every canvas, no pt and no `sizes_pt` provenance. These confirmed values are Strategist input anchors: the completed page plan may add recurring roles, and downstream execution owns bounded per-occurrence treatment. Candidate `sizes` remain accepted for compatibility; fresh Stage 2 preserves a candidate `body_size` as its baseline and derives only missing or unpinned role sizes from the same local ramp before first render. - **Per-role size override** (parallel to color's per-role HEX override): besides `body_size`, the page exposes editable inputs for `title` / `subtitle` / `annotation`. The browser applies one documented deterministic dependency chain: PPT uses `reading mode → body baseline`, non-PPT uses `canvas → body baseline`, then every canvas uses `body baseline → unpinned role sizes` (role ramp: `body ×` the §g ratios). Changing reading mode updates a PPT body and all unpinned roles locally; changing body updates unpinned roles locally. Editing body or a role pins that value, so later reading-mode changes do not overwrite it. A font-only selection preserves current sizes; applying a different complete direction, or using the active card's explicit restore action, restores that direction's typography baseline and derived unpinned sizes. This is a browser-only state update: it performs no fetch and asks the backend to author no new recommendations. Each role input is labelled as px and shows an approximate pt equivalent (`1px = 0.75pt`) for orientation. The final values are written to `result.json` as `typography.sizes: { "title", "subtitle", "annotation" }` in **px** — every canvas, no pt and no `sizes_pt` provenance. These confirmed values are Strategist input anchors: the completed page plan may add recurring roles, and downstream execution owns bounded per-occurrence treatment. Candidate `sizes` remain accepted for compatibility; fresh Stage 2 preserves a candidate `body_size` as its baseline and derives only missing or unpinned role sizes from the same local ramp before first render.
- **`delivery_purpose` compatibility key / Reading mode** (enumerable, PPT only) decides where meaning is carried, not merely how large type is: `text` makes pages self-contained with complete sentences, short prose, captions, tables, and necessary detail; `balanced` shares explanation between page and presenter; `presentation` uses one idea, concise claims, and visual evidence while speech / notes carry the detail. It therefore governs page grammar, granularity, density / rhythm, and note burden. Reading-mode cards intentionally show **no px value**; the typography section owns the separately visible body / role sizes and applies any local default. It is surfaced in Stage 2 beside the visual system, separate from communication intent. `recommend.delivery_purpose` pre-selects one; `result.json` retains the key, while `spec_lock.md` uses canonical `consumption_mode`. Non-PPT canvases omit it. - **`delivery_purpose` compatibility key / Reading mode** (enumerable, PPT only) decides where meaning is carried, not merely how large type is: `text` makes pages self-contained with complete sentences, short prose, captions, tables, and necessary detail; `balanced` shares explanation between page and presenter; `presentation` uses one idea, concise claims, and visual evidence while speech / notes carry the detail. It therefore governs page grammar, granularity, density / rhythm, and note burden. Reading-mode cards intentionally show **no px value**; the typography section owns the separately visible body / role sizes and applies any local default. It is surfaced in Stage 2 beside the visual system, separate from communication intent. `recommend.delivery_purpose` pre-selects one; `result.json` retains the key, while `spec_lock.md` uses canonical `consumption_mode`. Non-PPT canvases omit it.
- **Combined style preview** — a compact live "overall impression" strip sits just above the color section and is **sticky**: it pins under the topbar so it stays visible while the user scrolls through the color / icon / typography sections, keeping the picking controls and their combined effect on screen together. It applies the currently selected color palette **and** typography (heading sample in `primary` over `background`, body sample in `body_text`, an `accent` bar, a `secondary_bg` chip) and repaints on every color / HEX-override / font / `body_size` change. It does not replace the per-candidate swatches or font samples (those stay for picking); it is deliberately an abstract style chip, **not** a slide-layout preview — page layout preview remains the live-preview server's job (Step 6). No schema field; it derives entirely from the existing color + typography selections. - **Combined style preview** — a compact live "overall impression" strip sits just above the color section and is **sticky**: it pins under the topbar so it stays visible while the user scrolls through the color / icon / typography sections, keeping the picking controls and their combined effect on screen together. It applies the currently selected color palette **and** typography (heading sample in `primary` over `background`, body sample in `body_text`, an `accent` bar, a `secondary_bg` chip) and repaints on every color / HEX-override / font / `body_size` change. It does not replace the per-candidate swatches or font samples (those stay for picking); it is deliberately an abstract style chip, **not** a slide-layout preview — page layout preview remains the live-preview server's job (Step 6). No schema field; it derives entirely from the existing color + typography selections.
- **Generated-image direction** appears only for current `image_usage: ai`, but all three custom project candidates already exist in `design_directions` before that toggle. Turning AI on reveals those candidates immediately without a backend rerun, followed by the 20 fixed system styles. Selecting a project candidate expands its behavior editor in that card; a fixed preset submits its id, while a project candidate submits `rendering: "custom"` + edited non-empty `behavior`. Turning AI off omits `image_strategy` from the final result without deleting the authored recommendation candidates. Catalog-based custom behavior names exact ids for optional `image_rendering_references`; a novel behavior has none. The left preview follows selection. No image palette is written; deck colors remain authoritative, and legacy `image_strategy.palette` is ignored. - **Generated-image direction** appears only for current `image_usage: ai`, but all three custom project candidates already exist in `design_directions` before that toggle. Turning AI on reveals those candidates immediately without a backend rerun, followed by the 20 fixed system styles. Selecting a project candidate expands its behavior editor in that card; a fixed preset submits its id, while a project candidate submits `rendering: "custom"` + edited non-empty `behavior`. Turning AI off omits `image_strategy` from the final result without deleting the authored recommendation candidates. Catalog-based custom behavior names exact ids for optional `image_rendering_references`; a novel behavior has none. The left preview follows selection. No image palette is written; deck colors remain authoritative, and legacy `image_strategy.palette` is ignored. Illustrated icons and decorative lettering are downstream AI carrier decisions; neither adds a Confirm UI field or `result.json` key.
- **`design_directions`** is the canonical Stage-2 starting set: exactly three top-down, project-fit bundles with stable ids, localized copy, custom mode/style/rendering, icons, complete language-aware typography, and HEX `background`, `secondary_bg`, `primary`, `accent`, `secondary_accent`, `body_text`. The `selected` card carries the persistent Recommended marker and is applied first. A custom direction card uses its localized style-summary note—or the required behavior fallback—instead of requesting a preset-style preview. Newly authored notes may borrow localized catalog display labels where useful or use concise natural language freely; they never force an approximate label or expose internal catalog ids. Clicking an inactive card applies every field it owns; projected custom fields can then be edited in place and all lower controls may diverge. The active card shows an adjusted state and exposes an explicit restore action for its immutable authored bundle. `result.json` stores the edited current components, never a direction id. - **`design_directions`** is the canonical Stage-2 starting set: exactly three top-down, project-fit bundles with stable ids, localized copy, custom mode/style/rendering, icons, complete language-aware typography, and HEX `background`, `secondary_bg`, `primary`, `accent`, `secondary_accent`, `body_text`. The `selected` card carries the persistent Recommended marker and is applied first. A custom direction card uses its localized style-summary note—or the required behavior fallback—instead of requesting a preset-style preview. Newly authored notes may borrow localized catalog display labels where useful or use concise natural language freely; they never force an approximate label or expose internal catalog ids. Clicking an inactive card applies every field it owns; projected custom fields can then be edited in place and all lower controls may diverge. The active card shows an adjusted state and exposes an explicit restore action for its immutable authored bundle. `result.json` stores the edited current components, never a direction id.
- `recommend.generation_mode` and `refine_spec` mirror [`generate-pptx`](../../workflows/generate-pptx.md) Step 4. `split` / `true` are explicit opt-ins. Refinement adds no UI stage: after Gate 1 it stops before the lock for unrestricted chat revision until approval. - `recommend.generation_mode` and `refine_spec` mirror [`generate-pptx`](../../workflows/generate-pptx.md) Step 4. `split` / `true` are explicit opt-ins. Refinement adds no UI stage: after Gate 1 it stops before the lock for unrestricted chat revision until approval.
- `content_divergence` is a **free-text** Stage-1 source-treatment field. Blank means a balanced default; facts stay sourced at every level. Strategist consumes it while authoring §IX and records it in `design_spec.md §I`; it is not written to `spec_lock.md`. Beautify sends `{ "value": "keep source wording and page structure verbatim", "locked": true }`, so the UI displays it read-only and the server restores it on every staged submit. Template-fill does not use this confirmation flow and does not surface it. - `content_divergence` is a **free-text** Stage-1 source-treatment field. Blank means a balanced default; facts stay sourced at every level. Strategist consumes it while authoring §IX and records it in `design_spec.md §I`; it is not written to `spec_lock.md`. Beautify sends `{ "value": "keep source wording and page structure verbatim", "locked": true }`, so the UI displays it read-only and the server restores it on every staged submit. Template-fill does not use this confirmation flow and does not surface it.
@@ -33,6 +33,7 @@ from image_backends.backend_common import (
MAX_RETRIES, MAX_RETRIES,
download_image, download_image,
http_error, http_error,
is_permanent_error,
is_rate_limit_error, is_rate_limit_error,
normalize_image_size, normalize_image_size,
poll_json, poll_json,
@@ -202,6 +203,8 @@ def generate(prompt: str,
) )
except Exception as exc: except Exception as exc:
last_error = exc last_error = exc
if is_permanent_error(exc):
raise
if attempt >= max_retries: if attempt >= max_retries:
break break
limited = is_rate_limit_error(exc) limited = is_rate_limit_error(exc)
@@ -38,6 +38,72 @@ MAX_RETRIES = 3
RETRY_BASE_DELAY = 10 RETRY_BASE_DELAY = 10
RETRY_BACKOFF = 2 RETRY_BACKOFF = 2
_TRANSIENT_CLIENT_STATUSES = {408, 409, 423, 425, 429}
_HTTP_ERROR_STATUS = re.compile(r"\(([1-5][0-9]{2})\):")
_GLOBAL_PERMANENT_ERROR_TYPES = {
"authenticationerror",
}
_ITEM_PERMANENT_ERROR_TYPES = {
"badrequesterror",
"notfounderror",
"permissiondeniederror",
"unprocessableentityerror",
}
_GLOBAL_PERMANENT_ERROR_MARKERS = (
"prepayment credits are depleted",
"prepaid credits are depleted",
"credits are depleted",
"insufficient credits",
"insufficient balance",
"insufficient_quota",
"exceeded your current quota",
"payment required",
"billing is not enabled",
"billing not enabled",
"billing must be enabled",
"billing is disabled",
"invalid api key",
"api key not valid",
"incorrect api key",
"no api key found",
"missing api key",
"api key required",
"api key is required",
"api key not set",
"api key is not set",
"api key expired",
"expired api key",
"authentication failed",
"authentication required",
"unauthorized",
)
_ITEM_PERMANENT_ERROR_MARKERS = (
"permission denied",
"forbidden",
"invalid_argument",
"invalid argument",
"invalid request",
"bad request",
"failed_precondition",
"failed precondition",
"invalid image size",
"invalid aspect ratio",
"unsupported image size",
"unsupported aspect ratio",
"unsupported model",
"model not found",
"model does not exist",
"content policy",
"request moderated",
"content moderated",
"prompt was rejected",
"blocked by safety",
)
class _RetryableBackendError(RuntimeError):
"""A backend failure whose enclosing operation should be repeated."""
def resolve_output_path(prompt: str, output_dir: str = None, def resolve_output_path(prompt: str, output_dir: str = None,
filename: str = None, ext: str = ".png") -> str: filename: str = None, ext: str = ".png") -> str:
@@ -276,8 +342,70 @@ def normalize_image_size(image_size: str) -> str:
return s return s
def _error_status_code(exc: Exception) -> int | None:
"""Extract an HTTP-like status code from common SDK exception shapes."""
candidates = (
getattr(exc, "status_code", None),
getattr(exc, "code", None),
getattr(getattr(exc, "response", None), "status_code", None),
)
for value in candidates:
if isinstance(value, int) and not isinstance(value, bool):
return value
if isinstance(value, str) and value.isdigit():
return int(value)
match = _HTTP_ERROR_STATUS.search(str(exc))
return int(match.group(1)) if match else None
def is_global_permanent_error(exc: Exception) -> bool:
"""Return whether every unchanged request would fail for this backend."""
if isinstance(exc, _RetryableBackendError):
return False
status_code = _error_status_code(exc)
if status_code in {401, 402}:
return True
error_name = type(exc).__name__.lower()
if error_name in _GLOBAL_PERMANENT_ERROR_TYPES:
return True
err_str = str(exc).lower()
return any(marker in err_str for marker in _GLOBAL_PERMANENT_ERROR_MARKERS)
def is_permanent_error(exc: Exception) -> bool:
"""Return whether retrying the unchanged backend request cannot succeed."""
if isinstance(exc, _RetryableBackendError):
return False
if is_global_permanent_error(exc):
return True
if isinstance(exc, (FileNotFoundError, NotImplementedError, PermissionError)):
return True
status_code = _error_status_code(exc)
if (
status_code is not None
and 400 <= status_code < 500
and status_code not in _TRANSIENT_CLIENT_STATUSES
):
return True
error_name = type(exc).__name__.lower()
if error_name in _ITEM_PERMANENT_ERROR_TYPES:
return True
err_str = str(exc).lower()
return any(marker in err_str for marker in _ITEM_PERMANENT_ERROR_MARKERS)
def is_rate_limit_error(exc: Exception) -> bool: def is_rate_limit_error(exc: Exception) -> bool:
"""Check whether the exception appears to be rate limiting.""" """Check whether the exception appears to be rate limiting."""
if is_permanent_error(exc):
return False
err_str = str(exc).lower() err_str = str(exc).lower()
status_code = getattr(exc, "status_code", None) status_code = getattr(exc, "status_code", None)
error_code = getattr(exc, "code", None) error_code = getattr(exc, "code", None)
@@ -312,8 +440,11 @@ def retry_delay(attempt: int, rate_limited: bool) -> int:
def download_image(url: str, path: str, headers: dict = None, timeout: int = 180) -> str: def download_image(url: str, path: str, headers: dict = None, timeout: int = 180) -> str:
"""Download an image URL and save it to disk.""" """Download an image URL and save it to disk."""
response = requests.get(url, headers=headers or {}, timeout=timeout) try:
response.raise_for_status() response = requests.get(url, headers=headers or {}, timeout=timeout)
response.raise_for_status()
except requests.RequestException as exc:
raise _RetryableBackendError(f"Image download failed: {exc}") from exc
return save_image_bytes( return save_image_bytes(
response.content, response.content,
path, path,
@@ -33,6 +33,7 @@ from image_backends.backend_common import (
MAX_RETRIES, MAX_RETRIES,
download_image, download_image,
http_error, http_error,
is_permanent_error,
is_rate_limit_error, is_rate_limit_error,
normalize_image_size, normalize_image_size,
require_api_key, require_api_key,
@@ -164,6 +165,8 @@ def generate(prompt: str,
) )
except Exception as exc: except Exception as exc:
last_error = exc last_error = exc
if is_permanent_error(exc):
raise
if attempt >= max_retries: if attempt >= max_retries:
break break
limited = is_rate_limit_error(exc) limited = is_rate_limit_error(exc)
@@ -2,7 +2,7 @@
""" """
Gemini Image Generation Backend Gemini Image Generation Backend
Generates images via the Google GenAI API (Gemini). Generates or edits images via the Google GenAI API (Gemini).
Used by image_gen.py as a backend module. Used by image_gen.py as a backend module.
Configuration keys: Configuration keys:
@@ -37,6 +37,7 @@ from google import genai
from google.genai import types from google.genai import types
from image_backends.backend_common import ( from image_backends.backend_common import (
MAX_RETRIES, MAX_RETRIES,
is_permanent_error,
is_rate_limit_error, is_rate_limit_error,
normalize_image_size, normalize_image_size,
resolve_output_path, resolve_output_path,
@@ -74,6 +75,19 @@ MINIMAL_THINKING_MODELS = {
"gemini-3.1-flash-image-preview", "gemini-3.1-flash-image-preview",
} }
REFERENCE_IMAGE_MIME_TYPES = {
".heic": "image/heic",
".heif": "image/heif",
".jpeg": "image/jpeg",
".jpg": "image/jpeg",
".png": "image/png",
".webp": "image/webp",
}
# Signals to image_gen.py that this backend accepts a reference_image as
# multimodal input to the same generate_content request used for generation.
SUPPORTS_REFERENCE_IMAGE = True
def _model_id(model: str) -> str: def _model_id(model: str) -> str:
"""Return the final model path component for official capability checks.""" """Return the final model path component for official capability checks."""
@@ -101,16 +115,44 @@ def _validate_model_options(model: str, aspect_ratio: str, image_size: str) -> N
) )
def _reference_image_mime_type(reference_image: str) -> str:
"""Validate one local reference image and return its Gemini MIME type."""
image_path = Path(reference_image)
if not image_path.is_file():
raise FileNotFoundError(f"Reference image file not found: {reference_image}")
mime_type = REFERENCE_IMAGE_MIME_TYPES.get(image_path.suffix.lower())
if mime_type is None:
extensions = ", ".join(sorted(REFERENCE_IMAGE_MIME_TYPES))
raise ValueError(
f"Unsupported Gemini reference image format '{image_path.suffix or '<none>'}'. "
f"Supported extensions: {extensions}"
)
return mime_type
def _reference_image_part(reference_image: str) -> types.Part:
"""Load one supported local image as a Gemini inline-data part."""
image_path = Path(reference_image)
mime_type = _reference_image_mime_type(reference_image)
return types.Part.from_bytes(
data=image_path.read_bytes(),
mime_type=mime_type,
)
# ╔════════════════════════════════════════════════════════════════╗ # ╔════════════════════════════════════════════════════════════════╗
# ║ Image Generation # ║ Image Generation and Editing
# ╚══════════════════════════════════════════════════════════════════╝ # ╚══════════════════════════════════════════════════════════════════╝
def _generate_image(api_key: str, prompt: str, def _generate_image(api_key: str, prompt: str,
aspect_ratio: str = "1:1", image_size: str = "1K", aspect_ratio: str = "1:1", image_size: str = "1K",
output_dir: str = None, filename: str = None, output_dir: str = None, filename: str = None,
model: str = DEFAULT_MODEL, base_url: str = None) -> str: model: str = DEFAULT_MODEL, base_url: str = None,
reference_image: str = None) -> str:
""" """
Image generation via Gemini API (streaming). Image generation or editing via Gemini API (streaming).
Returns: Returns:
Path of the saved image file Path of the saved image file
@@ -136,18 +178,25 @@ def _generate_image(api_key: str, prompt: str,
) )
config = types.GenerateContentConfig(**config_kwargs) config = types.GenerateContentConfig(**config_kwargs)
contents = [prompt]
if reference_image is not None:
contents.append(_reference_image_part(reference_image))
mode_label = "Proxy Mode" if base_url else "Official Mode" mode_label = "Proxy Mode" if base_url else "Official Mode"
print(f"[Gemini - {mode_label}]") print(f"[Gemini - {mode_label}]")
if base_url: if base_url:
print(f" Base URL: {base_url}") print(f" Base URL: {base_url}")
print(f" Model: {model}") print(f" Model: {model}")
print(f" Prompt: {prompt[:120]}{'...' if len(prompt) > 120 else ''}") print(f" Prompt: {prompt[:120]}{'...' if len(prompt) > 120 else ''}")
if reference_image is not None:
print(f" Reference: {reference_image}")
print(f" Aspect Ratio: {aspect_ratio}") print(f" Aspect Ratio: {aspect_ratio}")
print(f" Image Size: {image_size}") print(f" Image Size: {image_size}")
print() print()
start_time = time.time() start_time = time.time()
print(f" [..] Generating...", end="", flush=True) operation = "Editing" if reference_image is not None else "Generating"
print(f" [..] {operation}...", end="", flush=True)
heartbeat_stop = threading.Event() heartbeat_stop = threading.Event()
@@ -167,7 +216,7 @@ def _generate_image(api_key: str, prompt: str,
for chunk in client.models.generate_content_stream( for chunk in client.models.generate_content_stream(
model=model, model=model,
contents=[prompt], contents=contents,
config=config, config=config,
): ):
elapsed = time.time() - start_time elapsed = time.time() - start_time
@@ -212,9 +261,10 @@ def _generate_image(api_key: str, prompt: str,
def generate(prompt: str, def generate(prompt: str,
aspect_ratio: str = "1:1", image_size: str = "1K", aspect_ratio: str = "1:1", image_size: str = "1K",
output_dir: str = None, filename: str = None, output_dir: str = None, filename: str = None,
model: str = None, max_retries: int = MAX_RETRIES) -> str: model: str = None, max_retries: int = MAX_RETRIES,
reference_image: str = None) -> str:
""" """
Gemini image generation with automatic retry. Gemini image generation or image-to-image editing with automatic retry.
Reads credentials from the current process environment or a `.env` file: Reads credentials from the current process environment or a `.env` file:
GEMINI_API_KEY GEMINI_API_KEY
@@ -222,13 +272,15 @@ def generate(prompt: str,
GEMINI_MODEL (optional override) GEMINI_MODEL (optional override)
Args: Args:
prompt: Prompt text prompt: Prompt text (edit instruction when reference_image is set)
aspect_ratio: Aspect ratio (e.g. "16:9", "1:1") aspect_ratio: Aspect ratio (e.g. "16:9", "1:1")
image_size: Image size ("512px", "1K", "2K", "4K", case-insensitive) image_size: Image size ("512px", "1K", "2K", "4K", case-insensitive)
output_dir: Output directory output_dir: Output directory
filename: Output filename (without extension) filename: Output filename (without extension)
model: Model name (default: gemini-3.1-flash-image) model: Model name (default: gemini-3.1-flash-image)
max_retries: Maximum number of retries max_retries: Maximum number of retries
reference_image: Optional source image path. When set, the image and
edit instruction are sent together as multimodal input.
Returns: Returns:
Path of the saved image file Path of the saved image file
@@ -254,14 +306,22 @@ def generate(prompt: str,
_validate_model_options(model, aspect_ratio, image_size) _validate_model_options(model, aspect_ratio, image_size)
if reference_image is not None:
# Validate local input before entering the retry loop. API failures can
# be transient; an unsupported or missing source image cannot be fixed
# by resending the same request.
_reference_image_mime_type(reference_image)
last_error = None last_error = None
for attempt in range(max_retries + 1): for attempt in range(max_retries + 1):
try: try:
return _generate_image(api_key, prompt, return _generate_image(api_key, prompt,
aspect_ratio, image_size, output_dir, aspect_ratio, image_size, output_dir,
filename, model, base_url) filename, model, base_url, reference_image)
except Exception as e: except Exception as e:
last_error = e last_error = e
if is_permanent_error(e):
raise
if attempt < max_retries and is_rate_limit_error(e): if attempt < max_retries and is_rate_limit_error(e):
delay = retry_delay(attempt, rate_limited=True) delay = retry_delay(attempt, rate_limited=True)
print(f"\n [WARN] Rate limit hit (attempt {attempt + 1}/{max_retries + 1}). " print(f"\n [WARN] Rate limit hit (attempt {attempt + 1}/{max_retries + 1}). "
@@ -33,6 +33,7 @@ from image_backends.backend_common import (
MAX_RETRIES, MAX_RETRIES,
download_image, download_image,
http_error, http_error,
is_permanent_error,
is_rate_limit_error, is_rate_limit_error,
normalize_image_size, normalize_image_size,
require_api_key, require_api_key,
@@ -172,6 +173,8 @@ def generate(prompt: str,
) )
except Exception as exc: except Exception as exc:
last_error = exc last_error = exc
if is_permanent_error(exc):
raise
if attempt >= max_retries: if attempt >= max_retries:
break break
limited = is_rate_limit_error(exc) limited = is_rate_limit_error(exc)
@@ -33,7 +33,9 @@ import requests
from image_backends.backend_common import ( from image_backends.backend_common import (
MAX_RETRIES, MAX_RETRIES,
detect_image_extension, detect_image_extension,
download_image,
http_error, http_error,
is_permanent_error,
is_rate_limit_error, is_rate_limit_error,
normalize_image_size, normalize_image_size,
require_api_key, require_api_key,
@@ -47,6 +49,11 @@ DEFAULT_ENDPOINT = "https://api.minimaxi.com/v1/image_generation"
DEFAULT_MODEL = "image-01" DEFAULT_MODEL = "image-01"
SUPPORTED_MODELS = {DEFAULT_MODEL} SUPPORTED_MODELS = {DEFAULT_MODEL}
# Request the documented default response format. The API returns hosted links
# in `data.image_urls` for "url" and inline strings in `data.image_base64` for
# "base64"; both shapes are handled when the response is parsed.
DEFAULT_RESPONSE_FORMAT = "url"
# International fallback: set MINIMAX_BASE_URL=https://api.minimax.io if needed # International fallback: set MINIMAX_BASE_URL=https://api.minimax.io if needed
ASPECT_RATIO_SIZE_MAP = { ASPECT_RATIO_SIZE_MAP = {
@@ -130,7 +137,7 @@ def _resolve_dimensions(aspect_ratio: str, image_size: str) -> tuple[int, int]:
def _extract_image_bytes(payload: dict) -> bytes | None: def _extract_image_bytes(payload: dict) -> bytes | None:
"""Extract image bytes from a MiniMax response payload.""" """Extract inline base64 image bytes from a MiniMax response payload."""
data = payload.get("data") or {} data = payload.get("data") or {}
image_base64 = data.get("image_base64") or [] image_base64 = data.get("image_base64") or []
if image_base64: if image_base64:
@@ -138,6 +145,15 @@ def _extract_image_bytes(payload: dict) -> bytes | None:
return None return None
def _extract_image_url(payload: dict) -> str | None:
"""Extract the first hosted image URL from a MiniMax response payload."""
data = payload.get("data") or {}
image_urls = data.get("image_urls") or []
if image_urls:
return image_urls[0]
return None
def _generate_image(api_key: str, prompt: str, def _generate_image(api_key: str, prompt: str,
aspect_ratio: str = "1:1", image_size: str = "1K", aspect_ratio: str = "1:1", image_size: str = "1K",
output_dir: str = None, filename: str = None, output_dir: str = None, filename: str = None,
@@ -156,7 +172,7 @@ def _generate_image(api_key: str, prompt: str,
"prompt": prompt, "prompt": prompt,
"width": width, "width": width,
"height": height, "height": height,
"response_format": "base64", "response_format": DEFAULT_RESPONSE_FORMAT,
"n": 1, "n": 1,
} }
@@ -181,12 +197,19 @@ def _generate_image(api_key: str, prompt: str,
raise RuntimeError(f"MiniMax image generation failed: {data}") raise RuntimeError(f"MiniMax image generation failed: {data}")
image_bytes = _extract_image_bytes(data) image_bytes = _extract_image_bytes(data)
if not image_bytes: if image_bytes:
raise RuntimeError(f"MiniMax response missing image data: {data}") ext = detect_image_extension(image_bytes) or ".jpeg"
path = resolve_output_path(prompt, output_dir, filename, ext)
return save_image_bytes(image_bytes, path)
ext = detect_image_extension(image_bytes) or ".jpeg" image_url = _extract_image_url(data)
path = resolve_output_path(prompt, output_dir, filename, ext) if image_url:
return save_image_bytes(image_bytes, path) # image-01 serves JPEG; save_image_bytes realigns the extension when the
# downloaded bytes are a different format.
path = resolve_output_path(prompt, output_dir, filename, ".jpeg")
return download_image(image_url, path)
raise RuntimeError(f"MiniMax response missing image data: {data}")
def generate(prompt: str, def generate(prompt: str,
@@ -219,6 +242,8 @@ def generate(prompt: str,
) )
except Exception as exc: except Exception as exc:
last_error = exc last_error = exc
if is_permanent_error(exc):
raise
if attempt >= max_retries: if attempt >= max_retries:
break break
limited = is_rate_limit_error(exc) limited = is_rate_limit_error(exc)
@@ -16,6 +16,7 @@ import requests
from image_backends.backend_common import ( from image_backends.backend_common import (
MAX_RETRIES, MAX_RETRIES,
http_error, http_error,
is_permanent_error,
is_rate_limit_error, is_rate_limit_error,
normalize_image_size, normalize_image_size,
require_api_key, require_api_key,
@@ -175,6 +176,8 @@ def generate(prompt: str,
) )
except Exception as exc: except Exception as exc:
last_error = exc last_error = exc
if is_permanent_error(exc):
raise
if attempt >= max_retries: if attempt >= max_retries:
break break
limited = is_rate_limit_error(exc) limited = is_rate_limit_error(exc)
@@ -16,6 +16,7 @@ Configuration keys:
OPENAI_OUTPUT_COMPRESSION (optional) 0-100, only for jpeg/webp GPT image output OPENAI_OUTPUT_COMPRESSION (optional) 0-100, only for jpeg/webp GPT image output
OPENAI_BACKGROUND (optional) auto or opaque for gpt-image-2 OPENAI_BACKGROUND (optional) auto or opaque for gpt-image-2
OPENAI_MODERATION (optional) auto or low for GPT image models OPENAI_MODERATION (optional) auto or low for GPT image models
OPENAI_INPUT_FIDELITY (optional) high or low for supported GPT image edits
Image editing (image-to-image): Image editing (image-to-image):
When image_gen.py passes reference_image=<path> (single-image CLI only, When image_gen.py passes reference_image=<path> (single-image CLI only,
@@ -54,6 +55,7 @@ from image_backends.backend_common import (
MAX_RETRIES, MAX_RETRIES,
download_image, download_image,
http_error, http_error,
is_permanent_error,
is_rate_limit_error, is_rate_limit_error,
normalize_image_size, normalize_image_size,
resolve_output_path, resolve_output_path,
@@ -81,7 +83,8 @@ LEGACY_COMPAT_ASPECT_RATIO_TO_SIZE = {
"21:9": "1792x1024", # closest wide format "21:9": "1792x1024", # closest wide format
} }
# GPT Image 1/1.5/mini officially support only square, landscape, portrait, or auto. # Legacy GPT Image models and chatgpt-image-latest support only square,
# landscape, portrait, or auto.
GPT_IMAGE_LEGACY_ASPECT_RATIO_TO_SIZE = { GPT_IMAGE_LEGACY_ASPECT_RATIO_TO_SIZE = {
"1:1": "1024x1024", "1:1": "1024x1024",
"16:9": "1536x1024", "16:9": "1536x1024",
@@ -175,6 +178,7 @@ OPENAI_QUALITY_VALUES = {
} }
GPT_IMAGE_BACKGROUNDS = {"auto", "opaque", "transparent"} GPT_IMAGE_BACKGROUNDS = {"auto", "opaque", "transparent"}
GPT_IMAGE_MODERATION_VALUES = {"auto", "low"} GPT_IMAGE_MODERATION_VALUES = {"auto", "low"}
GPT_IMAGE_INPUT_FIDELITY_VALUES = {"high", "low"}
DEFAULT_BASE_URL = "https://api.openai.com/v1" DEFAULT_BASE_URL = "https://api.openai.com/v1"
# Signals to image_gen.py that this backend can accept a reference_image # Signals to image_gen.py that this backend can accept a reference_image
@@ -194,7 +198,11 @@ def _normalized_model(model: str) -> str:
def _is_gpt_image_model(model: str) -> bool: def _is_gpt_image_model(model: str) -> bool:
return _normalized_model(model).startswith("gpt-image-") normalized = _normalized_model(model)
return (
normalized.startswith("gpt-image-")
or normalized == "chatgpt-image-latest"
)
def _is_gpt_image_2(model: str) -> bool: def _is_gpt_image_2(model: str) -> bool:
@@ -315,6 +323,26 @@ def _gpt_image_options(model: str) -> tuple[dict, str]:
return options, output_ext return options, output_ext
def _read_input_fidelity(model: str) -> str | None:
"""Read input fidelity for GPT Image edit requests."""
input_fidelity = _read_env_choice(
"OPENAI_INPUT_FIDELITY",
GPT_IMAGE_INPUT_FIDELITY_VALUES,
)
if input_fidelity is None:
return None
if _is_gpt_image_2(model):
raise ValueError(
"gpt-image-2 always uses high input fidelity and does not accept "
"OPENAI_INPUT_FIDELITY. Remove this setting."
)
if not _is_gpt_image_model(model):
raise ValueError(
f"{model} does not support OPENAI_INPUT_FIDELITY in this backend."
)
return input_fidelity
def _image_generations_url(base_url: str | None) -> str: def _image_generations_url(base_url: str | None) -> str:
base = (base_url or DEFAULT_BASE_URL).rstrip("/") base = (base_url or DEFAULT_BASE_URL).rstrip("/")
if base.endswith("/images/generations"): if base.endswith("/images/generations"):
@@ -389,6 +417,8 @@ def _validate_request_options(
_read_quality(image_size, model) _read_quality(image_size, model)
if _is_gpt_image_model(model): if _is_gpt_image_model(model):
_gpt_image_options(model) _gpt_image_options(model)
if editing:
_read_input_fidelity(model)
_apply_response_format({}, model) _apply_response_format({}, model)
@@ -568,6 +598,9 @@ def _edit_image(api_key: str, prompt: str, reference_image: str,
if _is_gpt_image_model(model): if _is_gpt_image_model(model):
gpt_options, output_ext = _gpt_image_options(model) gpt_options, output_ext = _gpt_image_options(model)
request.update(gpt_options) request.update(gpt_options)
input_fidelity = _read_input_fidelity(model)
if input_fidelity is not None:
request["input_fidelity"] = input_fidelity
_apply_response_format(request, model) _apply_response_format(request, model)
mode_label = f"Proxy: {base_url}" if base_url else "OpenAI API" mode_label = f"Proxy: {base_url}" if base_url else "OpenAI API"
@@ -595,6 +628,8 @@ def _edit_image(api_key: str, prompt: str, reference_image: str,
print(f" Background: {request['background']}") print(f" Background: {request['background']}")
if request.get("moderation"): if request.get("moderation"):
print(f" Moderation: {request['moderation']}") print(f" Moderation: {request['moderation']}")
if request.get("input_fidelity"):
print(f" Input Fidelity: {request['input_fidelity']}")
print() print()
start_time = time.time() start_time = time.time()
@@ -706,6 +741,8 @@ def generate(prompt: str,
filename, model, base_url) filename, model, base_url)
except Exception as e: except Exception as e:
last_error = e last_error = e
if is_permanent_error(e):
raise
if attempt < max_retries and is_rate_limit_error(e): if attempt < max_retries and is_rate_limit_error(e):
delay = retry_delay(attempt, rate_limited=True) delay = retry_delay(attempt, rate_limited=True)
print(f"\n [WARN] Rate limit hit (attempt {attempt + 1}/{max_retries + 1}). " print(f"\n [WARN] Rate limit hit (attempt {attempt + 1}/{max_retries + 1}). "
@@ -34,6 +34,7 @@ from image_backends.backend_common import (
decode_data_uri, decode_data_uri,
find_data_uri, find_data_uri,
http_error, http_error,
is_permanent_error,
is_rate_limit_error, is_rate_limit_error,
normalize_image_size, normalize_image_size,
resolve_output_path, resolve_output_path,
@@ -202,6 +203,8 @@ def generate(prompt: str,
filename, model, base_url) filename, model, base_url)
except Exception as e: except Exception as e:
last_error = e last_error = e
if is_permanent_error(e):
raise
if attempt < max_retries and is_rate_limit_error(e): if attempt < max_retries and is_rate_limit_error(e):
delay = retry_delay(attempt, rate_limited=True) delay = retry_delay(attempt, rate_limited=True)
print(f"\n [WARN] Rate limit hit (attempt {attempt + 1}/{max_retries + 1}). " print(f"\n [WARN] Rate limit hit (attempt {attempt + 1}/{max_retries + 1}). "
@@ -33,6 +33,7 @@ from image_backends.backend_common import (
MAX_RETRIES, MAX_RETRIES,
download_image, download_image,
http_error, http_error,
is_permanent_error,
is_rate_limit_error, is_rate_limit_error,
normalize_image_size, normalize_image_size,
require_api_key, require_api_key,
@@ -211,6 +212,8 @@ def generate(prompt: str,
) )
except Exception as exc: except Exception as exc:
last_error = exc last_error = exc
if is_permanent_error(exc):
raise
if attempt >= max_retries: if attempt >= max_retries:
break break
limited = is_rate_limit_error(exc) limited = is_rate_limit_error(exc)
@@ -33,6 +33,7 @@ from image_backends.backend_common import (
MAX_RETRIES, MAX_RETRIES,
download_image, download_image,
http_error, http_error,
is_permanent_error,
is_rate_limit_error, is_rate_limit_error,
normalize_image_size, normalize_image_size,
poll_json, poll_json,
@@ -191,6 +192,8 @@ def generate(prompt: str,
) )
except Exception as exc: except Exception as exc:
last_error = exc last_error = exc
if is_permanent_error(exc):
raise
if attempt >= max_retries: if attempt >= max_retries:
break break
limited = is_rate_limit_error(exc) limited = is_rate_limit_error(exc)
@@ -33,6 +33,7 @@ from image_backends.backend_common import (
MAX_RETRIES, MAX_RETRIES,
download_image, download_image,
http_error, http_error,
is_permanent_error,
is_rate_limit_error, is_rate_limit_error,
normalize_image_size, normalize_image_size,
require_api_key, require_api_key,
@@ -169,6 +170,8 @@ def generate(prompt: str,
) )
except Exception as exc: except Exception as exc:
last_error = exc last_error = exc
if is_permanent_error(exc):
raise
if attempt >= max_retries: if attempt >= max_retries:
break break
limited = is_rate_limit_error(exc) limited = is_rate_limit_error(exc)
@@ -32,6 +32,7 @@ import requests
from image_backends.backend_common import ( from image_backends.backend_common import (
MAX_RETRIES, MAX_RETRIES,
http_error, http_error,
is_permanent_error,
is_rate_limit_error, is_rate_limit_error,
normalize_image_size, normalize_image_size,
report_resolution, report_resolution,
@@ -163,6 +164,8 @@ def generate(prompt: str,
) )
except Exception as exc: except Exception as exc:
last_error = exc last_error = exc
if is_permanent_error(exc):
raise
if attempt >= max_retries: if attempt >= max_retries:
break break
limited = is_rate_limit_error(exc) limited = is_rate_limit_error(exc)
@@ -33,6 +33,7 @@ from image_backends.backend_common import (
MAX_RETRIES, MAX_RETRIES,
download_image, download_image,
http_error, http_error,
is_permanent_error,
is_rate_limit_error, is_rate_limit_error,
normalize_image_size, normalize_image_size,
require_api_key, require_api_key,
@@ -190,6 +191,8 @@ def generate(prompt: str,
) )
except Exception as exc: except Exception as exc:
last_error = exc last_error = exc
if is_permanent_error(exc):
raise
if attempt >= max_retries: if attempt >= max_retries:
break break
limited = is_rate_limit_error(exc) limited = is_rate_limit_error(exc)
@@ -33,6 +33,7 @@ from image_backends.backend_common import (
MAX_RETRIES, MAX_RETRIES,
download_image, download_image,
http_error, http_error,
is_permanent_error,
is_rate_limit_error, is_rate_limit_error,
normalize_image_size, normalize_image_size,
require_api_key, require_api_key,
@@ -186,6 +187,8 @@ def generate(prompt: str,
) )
except Exception as exc: except Exception as exc:
last_error = exc last_error = exc
if is_permanent_error(exc):
raise
if attempt >= max_retries: if attempt >= max_retries:
break break
limited = is_rate_limit_error(exc) limited = is_rate_limit_error(exc)
@@ -876,6 +876,8 @@ def _run_manifest(manifest: dict, manifest_path: str, backend_module, *,
and not retried within this run. `Failed` remains retryable and and not retried within this run. `Failed` remains retryable and
non-terminal; the Step 5 gate must resolve it by rerunning this non-terminal; the Step 5 gate must resolve it by rerunning this
manifest or marking the item `Needs-Manual`. manifest or marking the item `Needs-Manual`.
- Global auth or billing errors stop new batches; untouched rows remain
retryable. Permanent model or request errors fail only their own row.
- Status is written back to the manifest file after each completion; - Status is written back to the manifest file after each completion;
a Ctrl-C in the middle still preserves done items. a Ctrl-C in the middle still preserves done items.
- `Needs-Manual` items are skipped (user processes them externally). - `Needs-Manual` items are skipped (user processes them externally).
@@ -891,6 +893,8 @@ def _run_manifest(manifest: dict, manifest_path: str, backend_module, *,
output_dir = str(manifest_output_dir) output_dir = str(manifest_output_dir)
from image_backends.backend_common import ( from image_backends.backend_common import (
is_global_permanent_error,
is_permanent_error,
is_rate_limit_error, is_rate_limit_error,
validate_image_file, validate_image_file,
) )
@@ -940,6 +944,7 @@ def _run_manifest(manifest: dict, manifest_path: str, backend_module, *,
current = max(1, initial_concurrency) current = max(1, initial_concurrency)
state_lock = threading.Lock() state_lock = threading.Lock()
rate_limit_attempts: dict[int, int] = {} rate_limit_attempts: dict[int, int] = {}
stopped_for_global_error = False
stopped_for_rate_limit = False stopped_for_rate_limit = False
def _one(idx: int): def _one(idx: int):
@@ -983,6 +988,23 @@ def _run_manifest(manifest: dict, manifest_path: str, backend_module, *,
item.pop("last_error", None) item.pop("last_error", None)
ok_count += 1 ok_count += 1
print(f" [OK] {item['filename']}") print(f" [OK] {item['filename']}")
elif isinstance(exc, ValueError) or is_permanent_error(exc):
global_error = is_global_permanent_error(exc)
item["status"] = STATUS_FAILED
error_scope = "Global" if global_error else "Permanent"
repair_target = (
"backend access" if global_error else "model or request"
)
item["last_error"] = (
f"{error_scope} backend error: {exc}"
)[:500]
fail_count += 1
if global_error:
stopped_for_global_error = True
print(
f" [FAIL] {item['filename']}: {exc} "
f"(status=Failed; repair {repair_target} before retry)"
)
elif is_rate_limit_error(exc): elif is_rate_limit_error(exc):
rate_limited = True rate_limited = True
attempts = rate_limit_attempts.get(idx, 0) + 1 attempts = rate_limit_attempts.get(idx, 0) + 1
@@ -1024,6 +1046,12 @@ def _run_manifest(manifest: dict, manifest_path: str, backend_module, *,
) )
save_manifest(manifest_path, manifest) save_manifest(manifest_path, manifest)
if stopped_for_global_error:
print(
"\n Backend authentication or billing requires repair. "
"Stopping new batches; untouched items remain retryable.\n"
)
break
if stopped_for_rate_limit: if stopped_for_rate_limit:
print( print(
"\n Persistent rate limit reached the run boundary. " "\n Persistent rate limit reached the run boundary. "
@@ -1041,9 +1069,10 @@ def _run_manifest(manifest: dict, manifest_path: str, backend_module, *,
elif queue: elif queue:
time.sleep(2) time.sleep(2)
run_state = "Stopped" if stopped_for_rate_limit else "Done" stopped_early = stopped_for_global_error or stopped_for_rate_limit
run_state = "Stopped" if stopped_early else "Done"
remaining_note = "" remaining_note = ""
if stopped_for_rate_limit: if stopped_early:
remaining = sum( remaining = sum(
1 for item in items if item["status"] in RETRYABLE_STATUSES 1 for item in items if item["status"] in RETRYABLE_STATUSES
) )
@@ -1056,8 +1085,9 @@ def _run_manifest(manifest: dict, manifest_path: str, backend_module, *,
if fail_count: if fail_count:
print( print(
"[Manifest] Failed is retryable and non-terminal. " "[Manifest] Failed is retryable and non-terminal. "
"Resolve failed item(s) by rerunning this manifest or marking them " "Repair permanent backend errors before rerunning; retry transient "
"Needs-Manual before entering Executor." "failures or follow the owning manual recovery before entering "
"Executor."
) )
return ok_count, fail_count, skipped return ok_count, fail_count, skipped
@@ -1239,8 +1269,8 @@ def main() -> None:
help=( help=(
"Source image for image-to-image editing (single-image mode only). " "Source image for image-to-image editing (single-image mode only). "
"When set, the prompt is used as the edit instruction. Only backends " "When set, the prompt is used as the edit instruction. Only backends "
"that support editing accept this (currently: openai). Not valid with " "that support editing accept this (currently: gemini, openai). Not "
"--manifest / --render-md / --list-backends." "valid with --manifest / --render-md / --list-backends."
), ),
) )
@@ -1368,7 +1398,8 @@ def main() -> None:
if not getattr(backend, "SUPPORTS_REFERENCE_IMAGE", False): if not getattr(backend, "SUPPORTS_REFERENCE_IMAGE", False):
print( print(
f"Error: backend '{backend_name}' does not support image editing " f"Error: backend '{backend_name}' does not support image editing "
"(--reference-image). Use a backend that does (currently: openai)." "(--reference-image). Use a backend that does "
"(currently: gemini, openai)."
) )
sys.exit(1) sys.exit(1)
gen_kwargs["reference_image"] = args.reference_image gen_kwargs["reference_image"] = args.reference_image
@@ -18,7 +18,7 @@
"file_budgets": { "file_budgets": {
"AGENTS.md": 2750, "AGENTS.md": 2750,
"skills/ppt-master/SKILL.md": 1250, "skills/ppt-master/SKILL.md": 1250,
"skills/ppt-master/references/executor-base.md": 10000, "skills/ppt-master/references/executor-base.md": 12000,
"skills/ppt-master/references/executor-structured.md": 5500, "skills/ppt-master/references/executor-structured.md": 5500,
"skills/ppt-master/references/executor-chart.md": 3500, "skills/ppt-master/references/executor-chart.md": 3500,
"skills/ppt-master/references/executor-visualization.md": 1250, "skills/ppt-master/references/executor-visualization.md": 1250,
@@ -246,7 +246,7 @@
"registry": "image-renderings" "registry": "image-renderings"
} }
], ],
"max_tokens": 92000 "max_tokens": 105000
}, },
"route.generate.quick-generate.beautify": { "route.generate.quick-generate.beautify": {
"description": "Quick Generate with the shared strict 1:1 Beautify profile.", "description": "Quick Generate with the shared strict 1:1 Beautify profile.",
@@ -256,7 +256,7 @@
"profile.generate.beautify-pptx" "profile.generate.beautify-pptx"
], ],
"files": [], "files": [],
"max_tokens": 100000 "max_tokens": 115000
}, },
"route.generate.quick-generate.image-to-pptx": { "route.generate.quick-generate.image-to-pptx": {
"description": "Codex-supported Quick-only Image to PPTX with registered reference-image layers and shared plates.", "description": "Codex-supported Quick-only Image to PPTX with registered reference-image layers and shared plates.",
@@ -302,7 +302,7 @@
"stage.generate.topic-research" "stage.generate.topic-research"
], ],
"files": [], "files": [],
"max_tokens": 94000 "max_tokens": 105000
}, },
"route.generate.quick-generate.icon": { "route.generate.quick-generate.icon": {
"description": "Quick Generate with project-local bundled icon selection and synchronization.", "description": "Quick Generate with project-local bundled icon selection and synchronization.",
@@ -313,7 +313,7 @@
"files": [ "files": [
"skills/ppt-master/templates/icons/README.md" "skills/ppt-master/templates/icons/README.md"
], ],
"max_tokens": 94000 "max_tokens": 105000
}, },
"route.generate.quick-generate.in-hand-image": { "route.generate.quick-generate.in-hand-image": {
"description": "Quick Generate with supplied, extracted, or placeholder image resources prepared before SVG authoring.", "description": "Quick Generate with supplied, extracted, or placeholder image resources prepared before SVG authoring.",
@@ -977,7 +977,7 @@
"files": [ "files": [
"skills/ppt-master/references/native-shape-authoring.md" "skills/ppt-master/references/native-shape-authoring.md"
], ],
"max_tokens": 5000 "max_tokens": 6500
}, },
"route.generate.planning-template": { "route.generate.planning-template": {
"description": "Generate-PPTX planning context after an explicit template workspace is installed.", "description": "Generate-PPTX planning context after an explicit template workspace is installed.",
@@ -1108,7 +1108,7 @@
"files": [ "files": [
"skills/ppt-master/references/native-shape-authoring.md" "skills/ppt-master/references/native-shape-authoring.md"
], ],
"max_tokens": 5000 "max_tokens": 6500
}, },
"stage.shared.svg-effects": { "stage.shared.svg-effects": {
"description": "Advanced SVG paint, effects, transforms, and geometry authority, always included in Generate Executor contexts and conditionally loaded by other SVG-authoring routes.", "description": "Advanced SVG paint, effects, transforms, and geometry authority, always included in Generate Executor contexts and conditionally loaded by other SVG-authoring routes.",
@@ -2016,13 +2016,13 @@
{ {
"path": "skills/ppt-master/references/strategist.md", "path": "skills/ppt-master/references/strategist.md",
"role": "producer", "role": "producer",
"fingerprint": "feb2b322c7ce", "fingerprint": "e267f1759749",
"reason": "This producer projects the owner field into the planning contract." "reason": "This producer projects the owner field into the planning contract."
}, },
{ {
"path": "skills/ppt-master/templates/spec_lock_reference.md", "path": "skills/ppt-master/templates/spec_lock_reference.md",
"role": "reference", "role": "reference",
"fingerprint": "9d35b546fb2d", "fingerprint": "ebbb28e8b7e7",
"reason": "This reference mirrors the owner field grammar or ownership boundary." "reason": "This reference mirrors the owner field grammar or ownership boundary."
} }
] ]
@@ -130,13 +130,13 @@ Use these exact subsections and field shapes:
## VI. Icon Usage Specification ## VI. Icon Usage Specification
- **Primary bundled library**: <one of chunk-filled / tabler-filled / tabler-outline / phosphor-duotone, or none> - **Primary bundled library**: <one of chunk-filled / tabler-filled / tabler-outline / phosphor-duotone, or none>
- **Brand-logo library**: <simple-icons when selected for real brand marks; omit otherwise> - **Brand-logo library**: <simple-icons when actual content requires prepared real brand marks; omit otherwise>
| Icon Path | Suitable Scenarios | | Icon Path | Suitable Scenarios |
| --- | --- | | --- | --- |
``` ```
Preserve Title/Body characters and resolved stacks; omit blank Typography upgrade and never place it in a stack. For each justified recurring family override, add the role to Font Plan plus `- **<Role> stack**: <complete ordered stack>`. Possible roles are `Annotation`, `Footer`, `Footnote`, `Data`, `Emphasis`, `Quote`, and `Code`; add only recurring, intentional differences. Add non-locked `Role rationale` only for an extra family. Do not collapse distinct Title/Body stacks or discard a declared optional role. Each Font Size Hierarchy value is a role anchor: Executor may vary one occurrence `±2px`; a short non-structural Hero/Display size may stay unlisted only while the same value is planned at most twice, and its third occurrence needs a named row. Add every recurring palette role and typography-size anchor established by the plan; do not enumerate one-off paint or font-family garnish. For confirmed custom directions, add the applicable `Mode References`, `Mode Behavior`, `Visual Style References`, and `Visual Style Behavior` lines under Theme Style. Include `Stroke Width` under §VI only for a stroke library. `simple-icons` may accompany the one primary bundled library and is recorded only when real brand marks were selected. The icon table records the curated synced pool and broad semantic scenarios, not exact page placement or mandatory use. User-provided, template-carried, imported, custom, and other prepared SVGs under the project `icons/` directory remain usable without being forced into that bundled selection. Leave the §VI table empty when no bundled or brand icons are prepared. Preserve Title/Body characters and resolved stacks; omit blank Typography upgrade and never place it in a stack. For each justified recurring family override, add the role to Font Plan plus `- **<Role> stack**: <complete ordered stack>`. Possible roles are `Annotation`, `Footer`, `Footnote`, `Data`, `Emphasis`, `Quote`, and `Code`; add only recurring, intentional differences. Add non-locked `Role rationale` only for an extra family. Do not collapse distinct Title/Body stacks or discard a declared optional role. Each Font Size Hierarchy value is a role anchor: Executor may vary one occurrence `±2px`; a short non-structural Hero/Display size may stay unlisted only while the same value is planned at most twice, and its third occurrence needs a named row. Add every recurring palette role and typography-size anchor established by the plan; do not enumerate one-off paint or font-family garnish. For confirmed custom directions, add the applicable `Mode References`, `Mode Behavior`, `Visual Style References`, and `Visual Style Behavior` lines under Theme Style. Include `Stroke Width` under §VI only for a stroke library. `simple-icons` may accompany the one primary bundled library and is recorded only when actual content requires real brand marks; it is never a separate confirmation choice. The icon table records the curated synced SVG pool and broad semantic scenarios, not exact page placement or mandatory use. User-provided, template-carried, imported, custom, and other prepared SVGs under the project `icons/` directory remain usable without being forced into that bundled selection. Leave the §VI table empty when no bundled or brand SVG icons are prepared. Illustrated icons are AI image resources: their production sheet and placed slice rows belong in §VIII, and only placed slices project to `spec_lock.md images`.
When §VIII contains any `Acquire Via: ai` row, add this subsection under §III and preserve the complete confirmed AI direction: When §VIII contains any `Acquire Via: ai` row, add this subsection under §III and preserve the complete confirmed AI direction:
@@ -220,15 +220,14 @@ When an explicit final/literal narration script will become notes or generated
audio, make §X `Content` name that source and say `preserve verbatim`; keep the audio, make §X `Content` name that source and say `preserve verbatim`; keep the
full segmented script in `notes/total.md`, not in §IX or this Design Spec. full segmented script in `notes/total.md`, not in §IX or this Design Spec.
Append either or both optional lines only when the capability earns a place; Append the optional line only when the capability earns a place; never write an
never write an empty or `none` placeholder: empty or `none` placeholder:
```markdown ```markdown
- **Native shape suggestion**: <semantic object/result plus candidate preset/Connector family or Boolean operation/operand roles>
- **Motion suggestion**: <communication job plus desired page-entry or reveal relationship/order> - **Motion suggestion**: <communication job plus desired page-entry or reveal relationship/order>
``` ```
Add `Mathematical content` whenever a Slide needs a mathematical expression preserved exactly. Store the expression body as valid LaTeX without `$...$`, `$$...$$`, `\(...\)`, or `\[...\]` source delimiters; the field does not classify inline versus structural use. This is content authority for [`native-formula.md`](../references/native-formula.md), not a formula policy, marker, or implementation request; Executor chooses ordinary text, inline native math, or block native math. Add `Visualization` / `Images` when a Slide consumes §VII/§VIII or uses a page-local visual model. Name every value-driven geometry, qualitative relationship, cell grid, and child visual here; only independent Chart/Table entries use object keys. Describe qualitative order, linkage, hierarchy, grouping, contrast, overlap, and reading path freely—not as a model name or grammar enum. §IX may choose a custom Chart/Table fallback. Add `Native shape suggestion` only when a preset, stock Connector, or compound silhouette/cutout/intersection/fragment may help; name the semantic result plus candidate family or Boolean operands, never implementation geometry or keys. Executor chooses the primitive, preset, Boolean construction, or necessary freeform. Add `Motion suggestion` whenever transition/reveal advice strengthens communication, regardless of the Custom Animations outcome; state purpose and semantic order/relationship, not registry keys, options, timing, ids, or coverage. The suggestion never activates animation execution by itself, creates content, or binds implementation. Describe required visible image states in `Layout` / `Images` only for an explicit motion requirement or an enabled Custom Animations outcome. Add keyed `Native-ready` only for independent data charts or pure text-grid tables, `Fact IDs` for sourced claims, and `Data class: scenario` for invented demo values. Except on preservation paths, `Cover impact` carries a binding hook and adaptable composition; apply the same split to `Closing impact` only when the deck genuinely resolves. Roster/order/content stay authoritative. §VIII image layout is non-empty free prose with optional library ids; §VII Chart/Table rows are references. Executor owns geometry, hierarchy, treatment, and sparse local garnish. Add `Mathematical content` whenever a Slide needs a mathematical expression preserved exactly. Store the expression body as valid LaTeX without `$...$`, `$$...$$`, `\(...\)`, or `\[...\]` source delimiters; the field does not classify inline versus structural use. This is content authority for [`native-formula.md`](../references/native-formula.md), not a formula policy, marker, or implementation request; Executor chooses ordinary text, inline native math, or block native math. Add `Visualization` / `Images` when a Slide consumes §VII/§VIII or uses a page-local visual model. Name every value-driven geometry, qualitative relationship, cell grid, and child visual here; only independent Chart/Table entries use object keys. Describe qualitative order, linkage, hierarchy, grouping, contrast, overlap, and reading path freely—not as a model name or grammar enum. §IX may choose a custom Chart/Table fallback. Native construction creates no Design Spec field; Executor discovers and selects it independently during realization. Add `Motion suggestion` whenever transition/reveal advice strengthens communication, regardless of the Custom Animations outcome; state purpose and semantic order/relationship, not registry keys, options, timing, ids, or coverage. The suggestion never activates animation execution by itself, creates content, or binds implementation. Describe required visible image states in `Layout` / `Images` only for an explicit motion requirement or an enabled Custom Animations outcome. Add keyed `Native-ready` only for independent data charts or pure text-grid tables, `Fact IDs` for sourced claims, and `Data class: scenario` for invented demo values. Except on preservation paths, `Cover impact` carries a binding hook and adaptable composition; apply the same split to `Closing impact` only when the deck genuinely resolves. Roster/order/content stay authoritative. §VIII image layout is non-empty free prose with optional library ids; §VII Chart/Table rows are references. Executor owns geometry, hierarchy, treatment, and sparse local garnish.
For free-design pages, describe `Layout` through relationships, hierarchy, regions, and column spans; do not prescribe element-level `x`, `y`, `width`, or `height` or duplicate the global geometry in §II/§V. Exact coordinates belong to Executor SVG authoring. Preserve literal geometry only when the user explicitly requires it or a mirror/template preservation contract owns it. For free-design pages, describe `Layout` through relationships, hierarchy, regions, and column spans; do not prescribe element-level `x`, `y`, `width`, or `height` or duplicate the global geometry in §II/§V. Exact coordinates belong to Executor SVG authoring. Preserve literal geometry only when the user explicitly requires it or a mirror/template preservation contract owns it.
@@ -1,6 +1,6 @@
# SVG Icon Library # SVG Icon Library
This directory provides **12,027 high-quality SVG icons** across five libraries that can be directly embedded into SVG files generated by PPT Master. Default Strategist or the Quick Generate main agent chooses at most one primary library from the four stylistic libraries; the brand-logo library (`simple-icons`) may be selected alone or alongside it. This directory provides **12,027 high-quality SVG icons** across five libraries that can be directly embedded into SVG files generated by PPT Master. Default Strategist or the Quick Generate main agent chooses at most one primary library from the four stylistic libraries; the brand-logo library (`simple-icons`) is prepared as needed for real brands and may be used alone or alongside it. It is not a separate Confirm UI choice.
Upstream versions, compatibility overlays, licenses, attribution, and trademark boundaries are recorded in [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md). Upstream versions, compatibility overlays, licenses, attribution, and trademark boundaries are recorded in [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md).
@@ -99,9 +99,9 @@ Do not load a full index or enumerate broad keyword families. Re-pick from the n
> 1. **Geometry**: compact silhouettes (`chunk-filled`) vs. rounded curves (`tabler-filled` / `phosphor-duotone`) vs. open strokes (`tabler-outline`) > 1. **Geometry**: compact silhouettes (`chunk-filled`) vs. rounded curves (`tabler-filled` / `phosphor-duotone`) vs. open strokes (`tabler-outline`)
> 2. **Visual weight**: heavy solid (`chunk-filled`) → medium solid (`tabler-filled`) → medium layered (`phosphor-duotone`) → light stroke (`tabler-outline`) > 2. **Visual weight**: heavy solid (`chunk-filled`) → medium solid (`tabler-filled`) → medium layered (`phosphor-duotone`) → light stroke (`tabler-outline`)
**One primary bundled stylistic library per deck selection.** Pick one of `chunk-filled` / `tabler-filled` / `tabler-outline` / `phosphor-duotone` for generic icons (home, chart, users, etc.). If it lacks an exact icon, find the closest available alternative within that library instead of selecting from another bundled stylistic library. This is a catalog-selection rule, not a prohibition on combining assets that already exist in the project's `icons/` directory. **At most one primary bundled stylistic library per deck selection.** When generic icons are useful, pick one of `chunk-filled` / `tabler-filled` / `tabler-outline` / `phosphor-duotone` (home, chart, users, etc.). If it lacks an exact icon, find the closest available alternative within that library instead of selecting from another bundled stylistic library. This is a catalog-selection rule, not a prohibition on combining assets that already exist in the project's `icons/` directory.
**Brand-logo exception (`simple-icons`).** `simple-icons` is **not a stylistic library** and does not participate in the "one library" rule. Its job is brand recognition — Slack's purple, GitHub's cat, AWS's color — which is intentionally heterogeneous. Use it **alone or alongside** the chosen stylistic library, but **only** for actual company / product / service brand marks. Do **not** reach for it as a substitute when the chosen stylistic library lacks a generic icon. **Brand-logo exception (`simple-icons`).** `simple-icons` is **not a stylistic library**, does not participate in the "one library" rule, and is not presented as a user-facing library choice. Its job is brand recognition — Slack's purple, GitHub's cat, AWS's color — which is intentionally heterogeneous. Prepare it **alone or alongside** the chosen stylistic library only when actual content needs a company / product / service brand mark. Do **not** reach for it as a substitute when the chosen stylistic library lacks a generic icon.
| Use `simple-icons` for | Do NOT use `simple-icons` for | | Use `simple-icons` for | Do NOT use `simple-icons` for |
|------------------------|-------------------------------| |------------------------|-------------------------------|
@@ -109,4 +109,4 @@ Do not load a full index or enumerate broad keyword families. Re-pick from the n
| Tech stack icons on architecture / integration diagrams | Replacing a missing icon in `chunk-filled` / `tabler-*` / `phosphor-duotone` | | Tech stack icons on architecture / integration diagrams | Replacing a missing icon in `chunk-filled` / `tabler-*` / `phosphor-duotone` |
| Social media handles in a footer | Decorative / illustrative purposes | | Social media handles in a footer | Decorative / illustrative purposes |
⚠️ During bundled selection, choose generic icons from only one of the four **stylistic** libraries. `simple-icons` may be selected at the same time for real brand marks. Project-local assets are already prepared material and are not subject to a runtime mixing ban. ⚠️ During bundled selection, choose generic icons from only one of the four **stylistic** libraries. Prepare `simple-icons` independently when real brand marks are needed. Project-local assets are already prepared material and are not subject to a runtime mixing ban.
@@ -24,7 +24,7 @@ After Generate Step 4 Gate 1, read the completed Design Spec and current page/re
| `visual_style` | `visual_style` | Preset or `custom` | | `visual_style` | `visual_style` | Preset or `custom` |
| `colors` | Stable semantic color roles | Core identity and recurring roles only; contextual SVG paints need no row; `image_rendering` appears only for AI images | | `colors` | Stable semantic color roles | Core identity and recurring roles only; contextual SVG paints need no row; `image_rendering` appears only for AI images |
| `typography` | `font_family`, `body`, `title` | Core family/size anchors; new locks also write explicit `title_family` and `body_family`; size anchors are unitless px numbers | | `typography` | `font_family`, `body`, `title` | Core family/size anchors; new locks also write explicit `title_family` and `body_family`; size anchors are unitless px numbers |
| `icons` | `library`, `inventory` | `library` is the Strategist's primary bundled style choice or `none`; `simple-icons/*` may be selected alone or accompany it; `inventory` indexes the curated synced bundled pool rather than page usage or all usable project-local icons; `stroke_width` is conditional | | `icons` | `library`, `inventory` | `library` is the Strategist's primary bundled style choice or `none`; content-driven `simple-icons/*` may be prepared alone or accompany it; `inventory` indexes the curated synced SVG pool rather than page usage or all usable project-local icons; `stroke_width` is conditional |
| `page_rhythm` | One `P<NN>` row per page | Values: `anchor`, `dense`, `breathing` | | `page_rhythm` | One `P<NN>` row per page | Values: `anchor`, `dense`, `breathing` |
| `pptx_structure` | `mode` | Values: `flat`, `structured` | | `pptx_structure` | `mode` | Values: `flat`, `structured` |
| `forbidden` | Literal list items | General standards stay in their owning reference | | `forbidden` | Literal list items | General standards stay in their owning reference |
@@ -106,7 +106,7 @@ New locks always write `title_family` and `body_family`, even when their values
- `font_family`, `title_family`, `body_family`, and every optional `<role>_family` use one non-empty PPT-safe exported family stack. `font_family` is the body/default compatibility stack, not permission to erase role differences. - `font_family`, `title_family`, `body_family`, and every optional `<role>_family` use one non-empty PPT-safe exported family stack. `font_family` is the body/default compatibility stack, not permission to erase role differences.
- Every non-family `typography` value is a positive finite unitless px anchor. Intermediate values need no lock row when they stay within the mapped role's anchor `±2px`. At most two occurrences of one undeclared short non-structural Hero/Display size may remain sparse; a third occurrence or any structural use requires Design Spec repair and a named anchor. - Every non-family `typography` value is a positive finite unitless px anchor. Intermediate values need no lock row when they stay within the mapped role's anchor `±2px`. At most two occurrences of one undeclared short non-structural Hero/Display size may remain sparse; a third occurrence or any structural use requires Design Spec repair and a named anchor.
- `icons.library` records the primary stylistic library selected from `chunk-filled`, `tabler-filled`, `tabler-outline`, or `phosphor-duotone`, or `none` when no generic bundled icons are selected. Selected `simple-icons/*` brand marks may appear alone or alongside it in `inventory` without becoming a stylistic library. The inventory indexes the curated synced bundled pool without assigning page usage; every SVG already under `<project_path>/icons/` remains valid prepared execution material. - `icons.library` records the primary stylistic library selected from `chunk-filled`, `tabler-filled`, `tabler-outline`, or `phosphor-duotone`, or `none` when no generic bundled icons are selected. Content-driven `simple-icons/*` brand marks may appear alone or alongside it in `inventory` without becoming a stylistic library or a separate confirmation choice. The inventory indexes the curated synced SVG pool without assigning page usage; every SVG already under `<project_path>/icons/` remains valid prepared execution material. Illustrated-icon slices create no icon-lock field: their exact paths belong under `images`, and the unplaced parent sheet stays out of the lock.
- `objective` grammar: one concise sentence preserving the deck goal and audience success condition. - `objective` grammar: one concise sentence preserving the deck goal and audience success condition.
- `image_rendering` grammar: one catalog id, or `custom` with `image_rendering_behavior`. - `image_rendering` grammar: one catalog id, or `custom` with `image_rendering_behavior`.
- `images`: `- <key>: <path> | source=<via> | pattern=<layout> | crop=<adaptive|no-crop>`; e.g. `- p04: images/a.png | source=user | pattern=full-height image beside the evidence | crop=no-crop`. Use the canonical `images/<filename>` path; `source` and `crop` exactly project §VIII, while `pattern` preserves its non-empty normalized free-form suggestion and any optional hierarchical catalog ids. The pattern remains a recommendation for Executor recall, not a geometry or realization lock. Omit unplaced sheets. - `images`: `- <key>: <path> | source=<via> | pattern=<layout> | crop=<adaptive|no-crop>`; e.g. `- p04: images/a.png | source=user | pattern=full-height image beside the evidence | crop=no-crop`. Use the canonical `images/<filename>` path; `source` and `crop` exactly project §VIII, while `pattern` preserves its non-empty normalized free-form suggestion and any optional hierarchical catalog ids. The pattern remains a recommendation for Executor recall, not a geometry or realization lock. Omit unplaced sheets.
@@ -535,7 +535,7 @@ A deck with only `ai` rows never loads `image-searcher.md`; a deck with only `we
> **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 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. > **Illustration Sheet contract**: [image-generator.md](../references/image-generator.md) §4.3 owns grouping, prompting, and slicing for illustration, illustrated-icon, and lettering elements. Keep every sheet unplaced and place/project only successful transparent `slice` rows.
> ⚠️ **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.
@@ -545,7 +545,7 @@ 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` 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.5. **Slice any illustration, illustrated-icon, 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 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. 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 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. 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).
@@ -652,7 +652,7 @@ sidecars, or guessed family paths.
**Visual Construction Phase**: generate SVG pages sequentially, one at a time, in one continuous pass → `<project_path>/svg_output/` **Visual Construction Phase**: generate SVG pages sequentially, one at a time, in one continuous pass → `<project_path>/svg_output/`
Each completed SVG MUST be a standalone, complete representation of that slide's visible design. Template SVGs and locked planning artifacts may guide construction, but export must not reach back to them to add visible objects omitted from `svg_output/`. Speaker notes, animation, narration, transitions, and direct native-PPTX workflows remain separately owned artifacts/capabilities. Treat §IX `Native shape suggestion` as a candidate, not a command: inspect the actual page construction, then choose the highest-level faithful construction in this order — editable basic primitive, exact Office preset, Merge Shapes Boolean result, and only then a necessary freeform. Apply [`native-shape-authoring.md`](../references/native-shape-authoring.md) before materializing an adopted native treatment. Diagram relationships follow the same Shape-first order; do not infer a preset from contour similarity. Each completed SVG MUST be a standalone, complete representation of that slide's visible design. Template SVGs and locked planning artifacts may guide construction, but export must not reach back to them to add visible objects omitted from `svg_output/`. Speaker notes, animation, narration, transitions, and direct native-PPTX workflows remain separately owned artifacts/capabilities. Native shapes are Executor-local authoring capabilities, not planned resources: follow [`native-shape-authoring.md`](../references/native-shape-authoring.md), load its complete current preset inventory before the first page, choose page-fit contours before their authoring forms, keep exact native atoms independent when possible, materialize a Merge Shapes Boolean result only where contour semantics require it, and use necessary freeform last. Diagram relationships follow the same Shape-first gate; do not infer a preset from contour similarity.
**Motion-ready image composition**: Only when an explicit user motion **Motion-ready image composition**: Only when an explicit user motion
instruction, the effective Custom Animations outcome in `design_spec.md §I` is instruction, the effective Custom Animations outcome in `design_spec.md §I` is
@@ -276,7 +276,7 @@ the roster after the whole-roster check:
- the canvas, visual direction, wording, intended viewing distance, and effective reading mode: choose `presentation` for distance-first projected or recorded viewing, `balanced` for mixed viewing, or `text` for close content-heavy reading. Take the initial body anchor and sanity band from [`canvas-formats.md`](../../references/canvas-formats.md) § "Typography Scale Start" for the resolved canvas—PPT remains reading-mode-driven, while registered/custom non-PPT canvases use their canvas-derived start—then resolve one concrete typography plan for the delivery target defined by [`shared-standards-core.md`](../../references/shared-standards-core.md) §4.1, never from the authoring host's font inventory, with stable size anchors for title, body, annotation, and every other recurring role the roster uses. When content does not fit, preserve its core message and apply only fitting actions the source/profile invariants permit—restructure, shorten, or split; if none is permitted, surface the unresolved fit instead of shrinking a recurring role. Explicit user, template, fidelity-profile, or resolved-style requirements may call for a deliberate exception; - the canvas, visual direction, wording, intended viewing distance, and effective reading mode: choose `presentation` for distance-first projected or recorded viewing, `balanced` for mixed viewing, or `text` for close content-heavy reading. Take the initial body anchor and sanity band from [`canvas-formats.md`](../../references/canvas-formats.md) § "Typography Scale Start" for the resolved canvas—PPT remains reading-mode-driven, while registered/custom non-PPT canvases use their canvas-derived start—then resolve one concrete typography plan for the delivery target defined by [`shared-standards-core.md`](../../references/shared-standards-core.md) §4.1, never from the authoring host's font inventory, with stable size anchors for title, body, annotation, and every other recurring role the roster uses. When content does not fit, preserve its core message and apply only fitting actions the source/profile invariants permit—restructure, shorten, or split; if none is permitted, surface the unresolved fit instead of shrinking a recurring role. Explicit user, template, fidelity-profile, or resolved-style requirements may call for a deliberate exception;
- the semantic color roles actually needed by the roster, each with a concrete active-context color anchor, including background/surface, primary/secondary text, dominant/accent, and status roles as applicable. Honor explicit user, installed template/brand, fidelity-profile source-identity, and resolved-style color semantics before deriving only the missing roles that the active profile permits; decide which roles dominate, support, or remain rare, and preserve sufficient contrast for meaning-bearing text. Pair newly authored color-coded states, categories, or relationships with a label, symbol, line, or geometry cue; when fidelity forbids adding one, preserve the source encoding; - the semantic color roles actually needed by the roster, each with a concrete active-context color anchor, including background/surface, primary/secondary text, dominant/accent, and status roles as applicable. Honor explicit user, installed template/brand, fidelity-profile source-identity, and resolved-style color semantics before deriving only the missing roles that the active profile permits; decide which roles dominate, support, or remain rare, and preserve sufficient contrast for meaning-bearing text. Pair newly authored color-coded states, categories, or relationships with a label, symbol, line, or geometry cue; when fidelity forbids adding one, preserve the source encoding;
- 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 one compact page-scale geometry job tied to its core message—what geometry must organize, without naming a preset or encoding form; keep it only in the transient roster for §3's authoring-time move. 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, a transient deck-level visual motif system with an identity or - when useful, a transient deck-level visual motif system with an identity or
communication job, a recognizable invariant, and a reuse mode: fixed chrome, communication job, a recognizable invariant, and a reuse mode: fixed chrome,
@@ -336,6 +336,12 @@ because Quick is expected to be faster is not.
**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. **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.
**Default — consider AI illustrated icons when they strengthen compact semantic
cues**: When the user has not forbidden AI, prepare useful cues as transparent
slices under `images/`. Leave grouping, count, and coexistence with SVG icons
to the page and deck fit under [`image-generator.md`](../../references/image-generator.md)
§4.3; apply no coverage quota and never treat the slices as SVG inventory.
**Mandatory — proactive AI decorative lettering**: When the user has not **Mandatory — proactive AI decorative lettering**: When the user has not
forbidden AI, scan the frozen roster for display strings 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
@@ -353,12 +359,12 @@ 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 group several marks by compatible visual family and item for a single mark or group compatible marks through Illustration Sheets
batch each family through its own Illustration Sheet and transparent slices. and transparent slices. Let the intended character and treatment guide grouping.
Give the model the marks' role, placement/background relationship, relative Give the model the marks' role, placement/background relationship, relative
visual weight, and energy; apply `image-generator.md` §5.3's visual weight, and energy; apply `image-generator.md` §5.3's
controlled-default/high-expression boundary. Split a family controlled-default/high-expression boundary. Split when geometry, quality, or
only when its cell geometry or quality needs conflict, and keep ordinary the intended treatment benefits, and keep ordinary
title/chrome copy native. A prepared wordmark title/chrome copy native. A prepared wordmark
and an editable title are not mutually exclusive: 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,
@@ -373,8 +379,9 @@ generation capability is resolved during resource preparation, not eligibility.
|---|---| |---|---|
| 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 | | 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, or navigation item | Prepared project-local SVG/emoji icon, an illustrated-icon `slice`, or a coherent combination |
| Editable geometry can express a relationship, flow, emphasis, callout, symbol, or diagram | Basic SVG primitive, exact Office preset, Boolean result, then necessary freeform | | A real company, product, service, or social brand must appear as itself | Prepare the exact brand mark from `simple-icons` or supplied project assets as needed; it is not a user-facing library choice |
| Editable geometry can express a relationship, flow, emphasis, callout, symbol, or diagram | Page-fit contours from the full native vocabulary, then their simplest exact authoring forms; independent composition when possible, required Boolean next, necessary freeform last |
| Values, categories, time, weights, or duration determine mark geometry | Value-driven chart | | Values, categories, time, weights, or duration determine mark geometry | Value-driven chart |
| Sequence, hierarchy, role, region, or relationship determines page-local topology | Qualitative structure | | Sequence, hierarchy, role, region, or relationship determines page-local topology | Qualitative structure |
| Rows, columns, cells, headers, merges, and alignment form the information model | Cell-grid table | | Rows, columns, cells, headers, merges, and alignment form the information model | Cell-grid table |
@@ -423,11 +430,11 @@ Prepare only the resource paths needed by the decided pages:
|---|---| |---|---|
| Supplied/extracted image | Copy the selected file into `images/`; preserve its factual/provenance context and use the measured file rather than an invented substitute | | Supplied/extracted image | Copy the selected file into `images/`; preserve its factual/provenance context and use the measured file rather than an invented substitute |
| Image-to-PPTX reconstruction asset | In Codex, preserve identity graphics through an exact vector, deterministic redraw, sufficient source asset, or reference-based high-resolution reconstruction; keep data graphics native-and-verified or exact. For scene imagery, build the minimum registered clean-base/midground/subject/foreground group; batch padded-bbox-disjoint objects into one shared plate, then split them with grid slicing or independent nested-SVG bbox crops | | Image-to-PPTX reconstruction asset | In Codex, preserve identity graphics through an exact vector, deterministic redraw, sufficient source asset, or reference-based high-resolution reconstruction; keep data graphics native-and-verified or exact. For scene imagery, build the minimum registered clean-base/midground/subject/foreground group; batch padded-bbox-disjoint objects into one shared plate, then split them with grid slicing or independent nested-SVG bbox crops |
| Bundled/custom icon | Follow the [icon library contract](../../templates/icons/README.md), choose one coherent primary library, sync a useful project pool covering recurring semantics and likely page-local needs without assigning icons to pages, and choose from that prepared pool during SVG authoring | | Bundled/custom/brand SVG icon | Follow the [icon library contract](../../templates/icons/README.md), choose at most one coherent primary generic library when generic icons are useful, sync a project pool covering recurring semantics and likely page-local needs without assigning icons to pages, and add `simple-icons` marks only when actual content names the corresponding brand |
| 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 |
| 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 | | Composable illustration / illustrated-icon / 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. Slices remain under `images/` and may serve several pages; each lettering sheet still names every exact stable string assigned to it |
| 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 |
@@ -496,9 +503,12 @@ page and reuse throughout the valid execution context:
`Status: Sourced` image or filename recorded in `image_sources.json`. `Status: Sourced` image or filename recorded in `image_sources.json`.
Reread only after a known file change or context invalidation. Reread only after a known file change or context invalidation.
`executor-structure.md` is loaded once before all SVG authoring so Quick cannot `executor-structure.md` is loaded once before all SVG authoring so every
omit shape-composition reasoning. Reuse it throughout the valid execution `Structure=yes` result can apply its qualitative topology grammar.
context; reread only after a known file change or context invalidation. `native-shape-authoring.md` independently owns contour selection and compound
page geometry for both Structure results. Reuse both throughout the valid
execution context; before P01, complete its unfiltered full-registry discovery,
then reread only after a known file change or context invalidation.
**Mandatory — per-image-page composition decision**: For every page with one **Mandatory — per-image-page composition decision**: For every page with one
or more images, after its content and communication move are or more images, after its content and communication move are
@@ -521,12 +531,12 @@ whole-object carrier, and author canonical SVG `<a href>` under
[`native-hyperlinks.md`](../../references/native-hyperlinks.md). Never guess an [`native-hyperlinks.md`](../../references/native-hyperlinks.md). Never guess an
unknown destination. unknown destination.
Image to PPTX replaces this open composition decision for its canonical page Image to PPTX replaces the open image-composition and page-geometry decisions
frame: preserve the source geometry, restore text natively, preserve for its canonical page frame: preserve the source geometry, restore text
source-graphic identity through the prepared exact or reconstructed asset, and natively, preserve source-graphic identity through the prepared exact or
use the active-context registered layer/plate stack for scene imagery. Run the reconstructed asset, and use the active-context registered layer/plate stack
ordinary decision only for an additional non-source image whose placement is for scene imagery. Run either ordinary decision only for additional non-source
not already fixed by that surface. content whose placement or geometry is not already fixed by that surface.
**Mandatory — per-page Structure decision**: after the current page's content **Mandatory — per-page Structure decision**: after the current page's content
and communication move are determined, but before choosing any geometry or and communication move are determined, but before choosing any geometry or
@@ -541,6 +551,19 @@ artifact, spec, lock, manifest, or extra pass.
This decision is mandatory on every page and cannot be satisfied by the This decision is mandatory on every page and cannot be satisfied by the
capability menu, visualization recall, template geometry, or a later check. capability menu, visualization recall, template geometry, or a later check.
**Mandatory — independent per-page geometry move**: after the Structure result
and any applicable topology resolve, but before writing coordinates, resolve one
page-scale geometry move from the transient geometry job, actual content,
resolved style, and complete native vocabulary. Compare a deliberate plain /
neutral construction with
[`native-shape-authoring.md`](../../references/native-shape-authoring.md) §2.1's
composition lenses. Readability alone does not select the simple branch; a plain
grid or no compound construction remains valid when it is the deliberate best
fit for the page job. This applies to both `no` and `yes`, stays in active context
until the page is complete, and never changes the Structure result. Use §2.1
whenever the move adopts two or more native shapes. There is no coverage target
or required explanation for a simple result.
| Deterministic trigger | Additional authority | | Deterministic trigger | Additional authority |
|---|---| |---|---|
| A selected primary Chart/Table `family/key` | [`executor-visualization.md`](../../references/executor-visualization.md), then the matching Chart/Table authority | | A selected primary Chart/Table `family/key` | [`executor-visualization.md`](../../references/executor-visualization.md), then the matching Chart/Table authority |
@@ -696,7 +719,9 @@ 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] The complete native preset registry was read unfiltered before P01
- [x] Every page considered suitable carrier combinations without a coverage quota or single-carrier assumption - [x] Every page considered suitable carrier combinations without a coverage quota or single-carrier assumption
- [x] Every page not bound to literal supplied geometry carried its transient geometry job into an authoring-time page-scale move, and the whole-roster rhythm check confirmed that any extended same-carrier or same-topology run serves an intentional semantic arc
- [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] 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 `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
@@ -2,8 +2,8 @@
"sourceId": "shadcn", "sourceId": "shadcn",
"repo": "https://github.com/shadcn-ui/ui.git", "repo": "https://github.com/shadcn-ui/ui.git",
"ref": "main", "ref": "main",
"commit": "d4fc45b1fbabfccb7a6a4333d8004cf19481caa9", "commit": "8a7701ec27eb9cb8e0377db769fbe6d744113c52",
"adapter": "claude-skill", "adapter": "claude-skill",
"sourcePath": "skills/shadcn", "sourcePath": "skills/shadcn",
"syncedAt": "2026-08-14T16:00:00Z" "syncedAt": "2026-08-17T16:00:00Z"
} }