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-07-22 00:01:44 +08:00
parent db8558fa17
commit c7fdb8ba4a
75 changed files with 1223 additions and 998 deletions
+8 -8
View File
@@ -33,8 +33,8 @@
"repo": "https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git", "repo": "https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git",
"ref": "main", "ref": "main",
"adapter": "claude-skill", "adapter": "claude-skill",
"commit": "b484e8338c25b9cea3a25981a992d2817188971a", "commit": "1307d97a72e6c1cda572cb65471ae5ce82995218",
"syncedAt": "2026-07-20T16:00:00Z" "syncedAt": "2026-07-21T16:00:00Z"
}, },
{ {
"id": "caveman", "id": "caveman",
@@ -51,8 +51,8 @@
"repo": "https://github.com/Leonxlnx/taste-skill.git", "repo": "https://github.com/Leonxlnx/taste-skill.git",
"ref": "main", "ref": "main",
"adapter": "skill-collection", "adapter": "skill-collection",
"commit": "7c397f22d3af6f2b3f1925eb147d8e8801086151", "commit": "98565e65bc3274ddf6eb0838734341714057178b",
"syncedAt": "2026-07-18T16:00:01Z" "syncedAt": "2026-07-21T16:00:00Z"
}, },
{ {
"id": "shadcn", "id": "shadcn",
@@ -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": "b05ac551468098ed7b4c1c21d9d0d413b4230c79", "commit": "07d83e5a31a2830cc6c314377463e523a76e28fc",
"syncedAt": "2026-07-20T16:00:00Z" "syncedAt": "2026-07-21T16:00:00Z"
}, },
{ {
"id": "next-skills", "id": "next-skills",
@@ -105,8 +105,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": "d3e41fb496aeddeb448ba454bd663c36ea8183a5", "commit": "93c59f02f81cfdeb0fc397412897024ae7c68074",
"syncedAt": "2026-07-20T16:00:00Z" "syncedAt": "2026-07-21T16:00:00Z"
} }
] ]
} }
@@ -3,5 +3,5 @@
"name": "playwright浏览器自动化操作", "name": "playwright浏览器自动化操作",
"version": "20260605", "version": "20260605",
"keySource": "none", "keySource": "none",
"syncedAt": "2026-07-20T16:01:28Z" "syncedAt": "2026-07-21T16:01:43Z"
} }
@@ -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": "d3e41fb496aeddeb448ba454bd663c36ea8183a5", "commit": "93c59f02f81cfdeb0fc397412897024ae7c68074",
"adapter": "skill-collection", "adapter": "skill-collection",
"sourcePath": "skills", "sourcePath": "skills",
"syncedAt": "2026-07-20T16:00:00Z" "syncedAt": "2026-07-21T16:00:00Z"
} }
+1 -1
View File
@@ -19,7 +19,7 @@ English | [中文](./README_CN.md)
<a href="https://www.kimi.com/code/?aff=ppt-master"><img src="https://gcdn.moonshot.cn/growth-cdn/sponsor/kimi-en.png" alt="Kimi" width="100%"></a> <a href="https://www.kimi.com/code/?aff=ppt-master"><img src="https://gcdn.moonshot.cn/growth-cdn/sponsor/kimi-en.png" alt="Kimi" width="100%"></a>
</p> </p>
Thanks to [Kimi](https://www.kimi.com/code/?aff=ppt-master) for sponsoring this project! [Kimi K2.7](https://platform.kimi.ai/docs/guide/kimi-k2-7-code-quickstart) is an open-source agentic model developed by Moonshot AI. With PPT Master, Kimi can understand source materials such as PDFs, DOCX files, and web pages, identify key points, structure the narrative, and generate a natively editable PPTX that you can continue refining in PowerPoint. Thanks to [Kimi](https://www.kimi.com/code/?aff=ppt-master) for sponsoring this project! [Kimi K3](https://platform.kimi.ai/docs/guide/kimi-k3-quickstart) is the world's first open 3T-class model, featuring native vision and a 1-million-token context window. With PPT Master, K3 can understand source materials such as PDFs, DOCX files, and web pages, identify key points, structure the narrative, and generate a natively editable PPTX that you can continue refining in PowerPoint.
**Try [Kimi Code](https://www.kimi.com/code/?aff=ppt-master), or access the API through the Kimi Open Platform ([中文站](https://platform.kimi.com?aff=ppt-master) | [Global](https://platform.kimi.ai?aff=ppt-master)).** **Try [Kimi Code](https://www.kimi.com/code/?aff=ppt-master), or access the API through the Kimi Open Platform ([中文站](https://platform.kimi.com?aff=ppt-master) | [Global](https://platform.kimi.ai?aff=ppt-master)).**
@@ -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": "b05ac551468098ed7b4c1c21d9d0d413b4230c79", "commit": "07d83e5a31a2830cc6c314377463e523a76e28fc",
"adapter": "claude-skill", "adapter": "claude-skill",
"sourcePath": "skills/ppt-master", "sourcePath": "skills/ppt-master",
"syncedAt": "2026-07-20T16:00:00Z" "syncedAt": "2026-07-21T16:00:00Z"
} }
@@ -26,7 +26,7 @@ Each subdirectory contains:
| Choice | Reason | | Choice | Reason |
|---|---| |---|---|
| rendering=`vector-illustration` | Most versatile in the catalog; ✓✓ compatible with all 14 palettes; minimal interference when used as the "origin" for palette / type comparisons | | rendering=`vector-illustration` | Most versatile in the catalog; ✓✓ compatible with all 14 palettes; minimal interference when used as the "origin" for palette / type comparisons |
| palette=`cool-corporate` | Most neutral and most common; simple color behavior (HEX 60-30-10 applied directly) so it doesn't overpower the dimension under comparison | | palette=`cool-corporate` | Most neutral and common; clear dominant/support/accent roles keep the compared dimension visible |
| composition=single-subject hero (§4.1 Primitive A) | One dominant subject (60-70% of canvas) — the most visually representative shape, so rendering / palette differences show up most clearly | | composition=single-subject hero (§4.1 Primitive A) | One dominant subject (60-70% of canvas) — the most visually representative shape, so rendering / palette differences show up most clearly |
## How the images were generated ## How the images were generated
@@ -47,5 +47,5 @@ Scan all 14 images side by side. Focus on:
- **Color temperature** — cool-professional vs warm-human vs neutral-editorial - **Color temperature** — cool-professional vs warm-human vs neutral-editorial
- **Saturation** — restrained vs vivid vs monochrome - **Saturation** — restrained vs vivid vs monochrome
- **The 60-30-10 proportional feel** — how dominant the secondary really is - **Dominant/support/accent balance** — how strongly each semantic role appears
- **Accent weight** — how much visual presence the small accent commands - **Accent weight** — how much visual presence the small accent commands
@@ -158,7 +158,7 @@ Flags:
Per-element animations are anchored on **top-level `<g id="...">` content groups** in the SVG (e.g. `<g id="cover-title">`, `<g id="card-1">`). IDs must be unique within the page. One group produces one animation-pane entrance row; whether that row needs a click depends on the selected Start mode. Nested implementation groups may remain anonymous because the sidecar does not target them. Per-element animations are anchored on **top-level `<g id="...">` content groups** in the SVG (e.g. `<g id="cover-title">`, `<g id="card-1">`). IDs must be unique within the page. One group produces one animation-pane entrance row; whether that row needs a click depends on the selected Start mode. Nested implementation groups may remain anonymous because the sidecar does not target them.
Aim for **38 content groups per slide**. This is also the granularity PowerPoint uses for group-select / group-move, so it improves editing ergonomics regardless of animation. Use one content group per logical page unit. This is also the granularity PowerPoint uses for group-select / group-move, so semantic grouping improves editing ergonomics regardless of animation; do not split or merge units to hit a target count.
**Chrome groups skip the cascade automatically.** Explicit SVG role and placeholder semantics are authoritative. A group with `data-pptx-layer` or an explicit static role/placeholder marker can never animate. For marker-free legacy SVGs only, top-level groups whose id tokens look like page chrome (background, header/footer, decorations, watermark, page number, nav, logo, dividing rule) are excluded and appear with the slide. An explicit `animations.json` group entry may override this id-name heuristic, but never an explicit structural marker. Examples that auto-skip by legacy id: `<g id="background">`, `<g id="bg-texture">`, `<g id="cover-footer">`, `<g id="p03-header">`, `<g id="bottom-decor">`, `<g id="watermark">`, `<g id="nav">`, `<g id="logo-area">`, `<g id="column-rule">`. Examples that still animate: `<g id="card-1">`, `<g id="cover-title">`, `<g id="step-discover">`, `<g id="timeline-track">`. Do not strip the `<g>` wrapper to avoid animation — keep it for PowerPoint group selection and use `effect: none` when the content should remain static. **Chrome groups skip the cascade automatically.** Explicit SVG role and placeholder semantics are authoritative. A group with `data-pptx-layer` or an explicit static role/placeholder marker can never animate. For marker-free legacy SVGs only, top-level groups whose id tokens look like page chrome (background, header/footer, decorations, watermark, page number, nav, logo, dividing rule) are excluded and appear with the slide. An explicit `animations.json` group entry may override this id-name heuristic, but never an explicit structural marker. Examples that auto-skip by legacy id: `<g id="background">`, `<g id="bg-texture">`, `<g id="cover-footer">`, `<g id="p03-header">`, `<g id="bottom-decor">`, `<g id="watermark">`, `<g id="nav">`, `<g id="logo-area">`, `<g id="column-rule">`. Examples that still animate: `<g id="card-1">`, `<g id="cover-title">`, `<g id="step-discover">`, `<g id="timeline-track">`. Do not strip the `<g>` wrapper to avoid animation — keep it for PowerPoint group selection and use `effect: none` when the content should remain static.
@@ -10,7 +10,7 @@ Global artifact ownership rules for PPT Master projects.
| Artifact | Owner | Role | Read/write contract | | Artifact | Owner | Role | Read/write contract |
|---|---|---|---| |---|---|---|---|
| `sources/` content-type files | Content contract | Main pipeline source for text, tables, chart data values, and SmartArt node wording | Strategist reads content-type files (`.md` / `.markdown` / `.txt` / `.csv` / `.tsv` / `.json` / `.jsonl` / `.yaml` / `.yml`) and judges by content; do not replace values with PPTX geometry JSON in the main pipeline | | `sources/` content-type files | Content contract | Main pipeline factual/text origin for tables, chart data values, SmartArt node wording, and presentation content | Strategist reads content-type files (`.md` / `.markdown` / `.txt` / `.csv` / `.tsv` / `.json` / `.jsonl` / `.yaml` / `.yml`), judges by content, and resolves approved on-slide wording into §IX. Executor opens source passages only for explicit verification/resolution; do not replace values with PPTX geometry JSON in the main pipeline. |
| `sources/*.facts.json` | Fact provenance contract | Stable external `fact_id` → claim/source mapping created by topic research | Strategist cites IDs in §IX; Executor resolves them for visible footnotes / natural notes attribution. Scenario data never enters this file. | | `sources/*.facts.json` | Fact provenance contract | Stable external `fact_id` → claim/source mapping created by topic research | Strategist cites IDs in §IX; Executor resolves them for visible footnotes / natural notes attribution. Scenario data never enters this file. |
| `sources/` converted-source originals | Source archive | Imported source files that have a converted content contract (`.pdf` / `.pptx` / `.docx` / `.xlsx` / `.html` / `.epub` / `.tex` / `.rst` / `.ipynb` / `.typ`, etc.) and source-adjacent extracted assets | Read via the converted `<stem>.md` in the main pipeline; direct-PPTX workflows read the `.pptx` by route | | `sources/` converted-source originals | Source archive | Imported source files that have a converted content contract (`.pdf` / `.pptx` / `.docx` / `.xlsx` / `.html` / `.epub` / `.tex` / `.rst` / `.ipynb` / `.typ`, etc.) and source-adjacent extracted assets | Read via the converted `<stem>.md` in the main pipeline; direct-PPTX workflows read the `.pptx` by route |
| `sources/*.conversion_profile.json`, `sources/*_files/image_manifest.json` | Pipeline sidecar | Conversion audit record / asset index | NOT read as slide content; open only to audit a conversion or resolve assets | | `sources/*.conversion_profile.json`, `sources/*_files/image_manifest.json` | Pipeline sidecar | Conversion audit record / asset index | NOT read as slide content; open only to audit a conversion or resolve assets |
@@ -18,9 +18,9 @@ Global artifact ownership rules for PPT Master projects.
| `analysis/<stem>.identity.json` | Native deck identity facts | Canvas, theme palette/fonts, observed usage | Read selectively when detailed identity facts are needed | | `analysis/<stem>.identity.json` | Native deck identity facts | Canvas, theme palette/fonts, observed usage | Read selectively when detailed identity facts are needed |
| `analysis/<stem>.slide_library.json` | Native PPTX structure facts | Text slots, geometry, native tables, native chart caches, SmartArt nodes/connections | Direct PPTX workflows use as native fill/structure contract | | `analysis/<stem>.slide_library.json` | Native PPTX structure facts | Text slots, geometry, native tables, native chart caches, SmartArt nodes/connections | Direct PPTX workflows use as native fill/structure contract |
| `analysis/image_analysis.csv` | Regenerated image fact view | Measured facts about the current `images/` folder | Re-run `analyze_images.py` before reading image facts after changes | | `analysis/image_analysis.csv` | Regenerated image fact view | Measured facts about the current `images/` folder | Re-run `analyze_images.py` before reading image facts after changes |
| `design_spec.md` | Strategist design authority | Human-readable design intent, outline, rationale, and resource plan authored from the final confirmation plus source analysis | Strategist writes and audits it against every confirmed field before lock projection; humans and later roles read it for intent | | `design_spec.md` | Strategist design authority | Human-readable design intent, complete page brief, rationale, resource plan, and confirmed production mechanics authored from the final confirmation plus source analysis | Strategist consumes the final confirmation once, writes and audits every confirmed field here, then later roles read this artifact instead of reopening `result.json`; after Gate 1, §IX is the Executor's page-content authority. |
| `spec_lock.md` | Execution projection | Machine-readable colors, typography, icons, images, page rhythm, charts, `template_reuse_scope`, and the route's PowerPoint structure mode; mirror/layout template routes additionally own input prototypes, the Master roster, and the complete page-to-Master/Layout mapping | Strategist projects the route-specific contract from the audited Design Spec without making another design decision; `page-context` deliberately repeats its compact global projection per page as an anti-drift guard and adds current-page routing values. Executor may add a new adaptive Layout identity only on a structured mirror/layout route while authoring the page that first needs it. | | `spec_lock.md` | Execution anchor and routing contract | Machine-readable stable color/type roles, icons, images, page rhythm, charts, `template_reuse_scope`, and the route's PowerPoint structure mode; mirror/layout template routes additionally own input prototypes, the Master roster, and the complete page-to-Master/Layout mapping | Strategist authors the route-specific anchors from the audited Design Spec plus current project/page/template context. `page-context` repeats that compact anchor set per page and adds current-page routing values. Sparse page-local color/font garnish needs no lock row; a recurring semantic role or new adaptive Layout identity requires Strategist repair before reuse. |
| `project_manager.py page-context` stdout | Derived per-page context | Read-only model-facing lock projection + current-page delta + fingerprints for large references | Generate immediately before each page without `--bundle`; never edit or persist it as a replacement source of truth. `global` is the bounded repeated lock guard. `reference_set` carries path/SHA/load policy for project/template Design Specs and selected prototype/chart SVGs, but never appends their payloads. | | `project_manager.py page-context` stdout | Derived per-page context | Read-only model-facing anchor set + current-page delta + fingerprints for large references | Generate immediately before each page without `--bundle`; never edit or persist it as a replacement source of truth. `global` is the bounded repeated anchor set, not a whitelist of every valid page-local value. `reference_set` carries path/SHA/load policy for project/template Design Specs and selected prototype/chart SVGs, but never appends their payloads. The project Design Spec alone may carry `same_context_edit_policy` for verified targeted readback/rebind after the current main agent's own repair. |
| `analysis/page-context/P<NN>.usage.json` | Derived context telemetry | Actual compact page-context size plus hashes of owning inputs/references | `page-context --record-usage` deterministically replaces that page's snapshot; `page-context-report` summarizes current snapshots and unique references. Use token data to evaluate context cost, never as content or an execution contract. | | `analysis/page-context/P<NN>.usage.json` | Derived context telemetry | Actual compact page-context size plus hashes of owning inputs/references | `page-context --record-usage` deterministically replaces that page's snapshot; `page-context-report` summarizes current snapshots and unique references. Use token data to evaluate context cost, never as content or an execution contract. |
| `images/` | Runtime image pool | User, extracted, AI, web, formula, slice, EMF/WMF assets | Step 5 writes here; `analysis/image_analysis.csv` derives from current contents | | `images/` | Runtime image pool | User, extracted, AI, web, formula, slice, EMF/WMF assets | Step 5 writes here; `analysis/image_analysis.csv` derives from current contents |
| `icons/` | Project icon inventory | Icons copied by `icon_sync.py` for this project | Executor uses locked project icons; exporter may fall back to global library only as documented | | `icons/` | Project icon inventory | Icons copied by `icon_sync.py` for this project | Executor uses locked project icons; exporter may fall back to global library only as documented |
@@ -34,7 +34,7 @@ Global artifact ownership rules for PPT Master projects.
| `<import_workspace>/authoring-svg-flat/` | Optional complete-page verification IR | Self-contained page composition view with its own summary and provenance manifest | Generate only from an explicitly requested `svg-flat/`; use to verify composition, while layered `authoring-svg/` remains the canonical editable source | | `<import_workspace>/authoring-svg-flat/` | Optional complete-page verification IR | Self-contained page composition view with its own summary and provenance manifest | Generate only from an explicitly requested `svg-flat/`; use to verify composition, while layered `authoring-svg/` remains the canonical editable source |
| `<import_workspace>/icons/imported/` | Imported vector pool | One canonical copy of every factored vector subtree | Authoring SVGs reference `data-icon="imported/<name>"`; vector inventories retain source refs so expansion re-establishes IR identity | | `<import_workspace>/icons/imported/` | Imported vector pool | One canonical copy of every factored vector subtree | Authoring SVGs reference `data-icon="imported/<name>"`; vector inventories retain source refs so expansion re-establishes IR identity |
| `confirm_ui/recommendations.json` | Confirmation proposal | Strategist-authored confirmation payload | Confirm UI reads; rewritten between Stage 1, Stage 2, and Stage 3 | | `confirm_ui/recommendations.json` | Confirmation proposal | Strategist-authored confirmation payload | Confirm UI reads; rewritten between Stage 1, Stage 2, and Stage 3 |
| `confirm_ui/result.json` | Confirmation result | User-confirmed values | Strategist treats final result as authoritative over recommendations | | `confirm_ui/result.json` | Confirmation result | Persisted user-confirmed input evidence | Generate Step 4 reads the final object once into active context; Strategist consumes it completely into `design_spec.md`. Normal downstream work does not reopen it; fresh recovery may read it once when no retained final state exists. |
| `svg_output/` | Page-design author source | Main-agent handwritten SVG pages containing the complete visible design | Quality checker and native PPTX export read this as the canonical visual/page-layout source; templates and locks do not add missing visible objects at export | | `svg_output/` | Page-design author source | Main-agent handwritten SVG pages containing the complete visible design | Quality checker and native PPTX export read this as the canonical visual/page-layout source; templates and locks do not add missing visible objects at export |
| `notes/total.md` | Speaker-note source | Complete notes before splitting | Step 6 writes; Step 7.1 splits | | `notes/total.md` | Speaker-note source | Complete notes before splitting | Step 6 writes; Step 7.1 splits |
| `notes/slide_*.md` | Split notes | Per-slide notes generated from `total.md` | Derived by `total_md_split.py` | | `notes/slide_*.md` | Split notes | Per-slide notes generated from `total.md` | Derived by `total_md_split.py` |
@@ -51,12 +51,12 @@ Global artifact ownership rules for PPT Master projects.
| Invariant | Rule | | Invariant | Rule |
|---|---| |---|---|
| Content values | Main pipeline text, tables, chart values, and SmartArt node wording come from content-type files in `sources/` (`.md` / `.markdown` / `.txt` / `.csv` / `.tsv` / `.json` / `.jsonl` / `.yaml` / `.yml`), not from `slide_library.json`. | | Content authority | Content-type files in `sources/` (`.md` / `.markdown` / `.txt` / `.csv` / `.tsv` / `.json` / `.jsonl` / `.yaml` / `.yml`) own the factual/text origin for main-pipeline content, tables, chart values, and SmartArt node wording; Strategist resolves approved on-slide wording into §IX. Executor renders §IX and opens sources only for explicit verification/resolution, never to draft a second outline. `slide_library.json` does not own content values. |
| Sources read policy | In `sources/`, read content-type files (`.md` / `.markdown` / `.txt` / `.csv` / `.tsv` / `.json` / `.jsonl` / `.yaml` / `.yml`) and judge by content — a `.json` / `.csv` may be core content or just data. Exclude known sidecars: `*.conversion_profile.json` and `*_files/image_manifest.json`. `analysis/` facts (`source_profile.json`, `<stem>.slide_library.json`) are read per Step 4 / direct-PPTX workflow, not in the `sources/` content scan. | | Sources read policy | In `sources/`, read content-type files (`.md` / `.markdown` / `.txt` / `.csv` / `.tsv` / `.json` / `.jsonl` / `.yaml` / `.yml`) and judge by content — a `.json` / `.csv` may be core content or just data. Exclude known sidecars: `*.conversion_profile.json` and `*_files/image_manifest.json`. `analysis/` facts (`source_profile.json`, `<stem>.slide_library.json`) are read per Step 4 / direct-PPTX workflow, not in the `sources/` content scan. |
| PPTX structure | `slide_library.json` owns native geometry, slot facts, and SmartArt layout/relationships for direct PPTX workflows. | | PPTX structure | `slide_library.json` owns native geometry, slot facts, and SmartArt layout/relationships for direct PPTX workflows. |
| Design contract | Final confirmation audited `design_spec.md`projected `spec_lock.md`. Executor may apply projected `Template Application` prose to visible template content, but never infer lock values from it. On divergence, repair the lock from the Design Spec unless the Design Spec itself fails confirmation fidelity. | | Design contract | Final confirmation is read once → complete audited `design_spec.md`context-authored `spec_lock.md` anchors/routing. Executor may apply projected `Template Application` prose to visible template content, but never replace confirmed identity with an independent direction. On divergence, repair the lock from the Design Spec/current context unless the Design Spec itself fails confirmation fidelity. |
| Flat packaging authority | Free-design, brand-only, and `template_reuse_scope: style` declare `pptx_structure.mode: flat` and omit `pptx_masters`, `pptx_layouts`, `page_pptx_layouts`, and `page_layouts`. `svg_output/` owns the complete Slide-local visual design without root Master/Layout identity, fixed-layer ownership, or placeholder metadata. Export materializes one clean project-owned Master plus one Blank Layout, applies the locked theme defaults, removes stock content placeholders/Layout inventory, and retains only the standard date/footer/slide-number capability hooks. | | Flat packaging authority | Free-design, brand-only, and `template_reuse_scope: style` declare `pptx_structure.mode: flat` and omit `pptx_masters`, `pptx_layouts`, `page_pptx_layouts`, and `page_layouts`. `svg_output/` owns the complete Slide-local visual design without root Master/Layout identity, fixed-layer ownership, or placeholder metadata. Export materializes one clean project-owned Master plus one Blank Layout, applies the locked theme defaults, removes stock content placeholders/Layout inventory, and retains only the standard date/footer/slide-number capability hooks. |
| Template structure authority | `template_reuse_scope: mirror|layout` uses `page_layouts` for each page's authoring-input prototype. `pptx_masters` / `pptx_layouts` own the unique reusable output definitions, while `page_pptx_layouts` owns page assignment. Strict keeps the prototype contract; adaptive may create a new Layout definition during page authoring and updates its assignment immediately. Mirror additionally preserves literal visuals/text topology; layout allows project-controlled reflow/re-skinning. Unused definitions may register without a published Slide. Templates validate provenance but never add missing visible page objects during export. | | Template structure authority | `template_reuse_scope: mirror|layout` uses `page_layouts` for each page's authoring-input prototype. `pptx_masters` / `pptx_layouts` own the unique reusable output definitions, while `page_pptx_layouts` owns page assignment. Strict keeps the prototype contract; adaptive may use a current or new Layout already declared by Strategist. A construction-discovered structural change returns upstream for definition and assignment repair before authoring resumes. Mirror additionally preserves literal visuals/text topology; layout allows project-controlled reflow/re-skinning. Unused definitions may register without a published Slide. Templates validate provenance but never add missing visible page objects during export. |
| Fact classes | External facts resolve through `sources/*.facts.json`; invented demo KPIs/targets/internal ratios are labeled `scenario` in `design_spec.md §IX` and visibly in the page. Never promote scenario data into the external fact registry. | | Fact classes | External facts resolve through `sources/*.facts.json`; invented demo KPIs/targets/internal ratios are labeled `scenario` in `design_spec.md §IX` and visibly in the page. Never promote scenario data into the external fact registry. |
| Imported-template authoring | Editable SVGs under `authoring-svg/` own create-template edits, `authoring_summary.json` owns model-facing orientation, and `authoring_manifest.json` owns tool-only source-object identity. Lossless `svg/` owns immutable native payload and fallback evidence; optional `svg-flat/` owns only complete-page verification. Materialized `templates/*.svg` own the validated deliverable contract and contain no IR-only source refs. | | Imported-template authoring | Editable SVGs under `authoring-svg/` own create-template edits, `authoring_summary.json` owns model-facing orientation, and `authoring_manifest.json` owns tool-only source-object identity. Lossless `svg/` owns immutable native payload and fallback evidence; optional `svg-flat/` owns only complete-page verification. Materialized `templates/*.svg` own the validated deliverable contract and contain no IR-only source refs. |
| Legacy template input | Old unmapped/distilled/preserve structured projects and incomplete template packages are not migrated in place. [`create-template`](../workflows/create-template.md) authors a new current workspace: original PPTX Type A may preserve existing native topology in mirror; legacy SVG-only Type B is visual reference for `standard` / `fidelity`. An intentional free-design or brand-only `flat` project is already current. The exporter does not migrate or visually cluster legacy structure. | | Legacy template input | Old unmapped/distilled/preserve structured projects and incomplete template packages are not migrated in place. [`create-template`](../workflows/create-template.md) authors a new current workspace: original PPTX Type A may preserve existing native topology in mirror; legacy SVG-only Type B is visual reference for `standard` / `fidelity`. An intentional free-design or brand-only `flat` project is already current. The exporter does not migrate or visually cluster legacy structure. |
@@ -67,7 +67,7 @@ Global artifact ownership rules for PPT Master projects.
| Post-processed SVG | `svg_final/` is disposable, must be rebuilt in Step 7.2, and serves only as a self-contained visual preview / manually insertable SVG picture. | | Post-processed SVG | `svg_final/` is disposable, must be rebuilt in Step 7.2, and serves only as a self-contained visual preview / manually insertable SVG picture. |
| Export source | The only supported generated-PPTX route reads `svg_output/` through the project SVG-to-DrawingML converter. A diagnostic `-s final` override does not change ownership or create a supported release route. | | Export source | The only supported generated-PPTX route reads `svg_output/` through the project SVG-to-DrawingML converter. A diagnostic `-s final` override does not change ownership or create a supported release route. |
| Shape-conversion boundary | PowerPoint's manual Convert-to-Shape operation on `svg_final/` is outside the project compatibility contract. | | Shape-conversion boundary | PowerPoint's manual Convert-to-Shape operation on `svg_final/` is outside the project compatibility contract. |
| Confirmation | Final `confirm_ui/result.json` or chat confirmation overrides recommendations and is the mandatory input contract for `design_spec.md`; `spec_lock.md` is derived only after that Design Spec passes confirmation-fidelity review. | | Confirmation | Final `confirm_ui/result.json` or chat confirmation overrides recommendations and is consumed once as the mandatory input contract for `design_spec.md`; `spec_lock.md` is authored only after that Design Spec passes confirmation-fidelity review. |
**Forbidden - mixed ownership**: Do not copy chart values from Markdown into `analysis/` by hand, do not edit `svg_final/` as the source of a fix, do not edit imported lossless SVGs instead of their authoring IR, and do not treat `design_spec.md` prose as a replacement for `spec_lock.md`. **Forbidden - mixed ownership**: Do not copy chart values from Markdown into `analysis/` by hand, do not edit `svg_final/` as the source of a fix, do not edit imported lossless SVGs instead of their authoring IR, and do not treat `design_spec.md` prose as a replacement for `spec_lock.md`.
@@ -9,7 +9,7 @@ Always-loaded Executor authority for flat SVG page authoring and behavior shared
| `pptx_structure.mode: structured` | [`executor-structured.md`](./executor-structured.md) | | `pptx_structure.mode: structured` | [`executor-structured.md`](./executor-structured.md) |
| Any data chart, chart catalog selection, or text-grid table | [`executor-chart.md`](./executor-chart.md) | | Any data chart, chart catalog selection, or text-grid table | [`executor-chart.md`](./executor-chart.md) |
| A page will use a preset pattern fill or evaluate native chart/table replacement | [`native-data-interface.md`](./native-data-interface.md) before deciding eligibility or emitting metadata | | A page will use a preset pattern fill or evaluate native chart/table replacement | [`native-data-interface.md`](./native-data-interface.md) before deciding eligibility or emitting metadata |
| Any image or formula resource, including template-bundled images | [`executor-image.md`](./executor-image.md) | | Any image or formula resource, including template-bundled images | [`executor-image.md`](./executor-image.md) + [`image-layout-patterns.md`](./image-layout-patterns.md) |
| Any `Status: Sourced` web image | [`executor-web-image.md`](./executor-web-image.md), after `executor-image.md` | | Any `Status: Sourced` web image | [`executor-web-image.md`](./executor-web-image.md), after `executor-image.md` |
| Speaker notes generation after all SVG pages pass | [`executor-notes.md`](./executor-notes.md) | | Speaker notes generation after all SVG pages pass | [`executor-notes.md`](./executor-notes.md) |
@@ -25,6 +25,22 @@ Always-loaded Executor authority for flat SVG page authoring and behavior shared
--- ---
## 1. Effect Capability Discovery
**Reference — not a constraint**: Scan this menu for treatments that support the locked style and hierarchy. After selecting one, load [`svg-effects.md`](./svg-effects.md) before authoring it.
| Visual need | Available construction |
|---|---|
| Color / material | alpha paint, gradients, translucent overlays |
| Elevation | shadow, glow, explicit highlights |
| Image integration | scrim, vignette, brand wash, clipping, faux glass |
| Line / type | dash/cap/join, markers, gradient stroke; tracking, outline, alpha/gradient text |
| Space / constructed style | transform/reuse, curves/arcs, hand-drawn, ink/Riso, halftone, isometric, paper cut |
**Hard rule — discovery does not expand compatibility**: Follow `svg-effects.md` syntax and fallbacks; unsupported blur, blend, mask, dense texture, or skew remains baked/alternative-only.
---
## 2. Design Parameter Confirmation (Mandatory Step) ## 2. Design Parameter Confirmation (Mandatory Step)
Before the first SVG page, output a confirmation listing: the compact communication objective, canvas dimensions, body font size, color scheme (primary/secondary/accent HEX), font plan, and the live-preview URL reported by the launcher. If the preview launch failed, state that failure before generating SVGs instead of silently proceeding. Prevents purpose/spec/execution drift. Before the first SVG page, output a confirmation listing: the compact communication objective, canvas dimensions, body font size, color scheme (primary/secondary/accent HEX), font plan, and the live-preview URL reported by the launcher. If the preview launch failed, state that failure before generating SVGs instead of silently proceeding. Prevents purpose/spec/execution drift.
@@ -39,11 +55,17 @@ Before the first SVG, retain `design_spec.md`: continuous execution reuses plann
python3 skills/ppt-master/scripts/project_manager.py page-context <project_path> P<NN> --record-usage python3 skills/ppt-master/scripts/project_manager.py page-context <project_path> P<NN> --record-usage
``` ```
`global` deliberately repeats the sub-1000-token lock projection as an anti-drift guard; `lock_source.sha256` binds its version. `page_context` is the current §IX/resource/template/chart delta. For every `reference_set` entry—project/template Design Spec or selected prototype/chart SVG—reuse an in-context path + SHA; read it once only when absent or changed. `global` deliberately repeats the sub-1000-token cross-page anchor set; `lock_source.sha256` binds its version. These anchors preserve identity and recurring semantics but do not enumerate every legal color or font. `page_context` is the current §IX/resource/template/chart delta. For every `reference_set` entry—project/template Design Spec or selected prototype/chart SVG—reuse an in-context path + SHA; read it once only when absent or changed.
Use lock values literally and optional `Template Application` from the retained Design Spec. The delta overrides neither facts nor constraints. After an approved change, rerun the command and reload only changed references. Deprecated `--bundle` is a compatibility no-op. **Hard rule — known same-context Design Spec repair**: When the current main agent returns to Strategist and authors an exact project `design_spec.md` repair from its retained state, the `design-spec` reference's `same_context_edit_policy: targeted-readback-and-rebind` avoids a full reread. This applies only to bounded repairs that keep the page roster, narrative order, confirmed identity, and communication contract unchanged. Repair the owning Design Spec headings/page blocks first, re-author only affected lock rows, read back those changed fragments, run `project_manager.py validate`, then rerun `page-context` for every affected page. If each projected brief/global view matches the intended repair, bind the retained whole-document understanding plus verified delta to the new Design Spec SHA. A fresh context, an external/unknown edit, a roster/global-contract change, an unexpected diff, failed validation, or mismatched projection requires one full Design Spec read before continuing.
**Source facts**: The page delta carries page intent and routing facts, not the complete source corpus. Read the relevant `sources/` content and resolve listed `Fact IDs` from `sources/*.facts.json` when the page needs concrete claims, quotes, names, or data. **Hard rule — exact page roster**: `design_spec.md §IX` is the ordered queue: one final slide per entry, with the same id/order. The UI range no longer applies. Never add, drop, merge, split, or reorder; repair/reconfirm the Design Spec first.
**Hard rule — selection vs realization**: use Strategist-selected content, resources/paths, chart/layout keys, core fonts, palette anchors, icons, and crop boundaries. Adapt realization, never selection, except sparse local font/color garnish allowed below. Missing or unresolved material stops execution and returns to Strategist-owned acquisition/failure recovery; never search, generate, download, sync, invent, or substitute it. Selection changes require upstream repair.
Use named lock roles literally when that role applies, and use optional `Template Application` from the retained Design Spec. Choose contextual page-local values from the Design Spec, style, content, and current composition rather than forcing every object into a lock row. The delta overrides neither facts nor constraints. After an approved change, rerun the command; reload changed references except the verified same-context Design Spec repair above. Deprecated `--bundle` is a compatibility no-op.
**Source verification**: §IX owns the complete page brief; the page delta does not carry the source corpus. Read sources only to resolve listed `Fact IDs` or verify required claims, quotes, names, or data. Do not add facts, claims, or selected content. Return underspecified blocks for Design Spec repair.
**Per-page communication trace**: Read `communication.objective`, `communication.core_message`, and the current §IX `Core message` + `Audience move` before choosing composition. The page must advance the compact objective and move the audience as authored in §IX; the global core message remains the deck-wide north star. A page that cannot state this movement is an upstream outline defect — surface `warning: P<NN> has no communication move` instead of compensating with decorative layout. Do not invent a new purpose, ask, or outcome at execution time. Structural pages may advance the contract by establishing relevance / tension / decision frame or by completing the final commitment; they are not exempt from having a reason to exist. **Per-page communication trace**: Read `communication.objective`, `communication.core_message`, and the current §IX `Core message` + `Audience move` before choosing composition. The page must advance the compact objective and move the audience as authored in §IX; the global core message remains the deck-wide north star. A page that cannot state this movement is an upstream outline defect — surface `warning: P<NN> has no communication move` instead of compensating with decorative layout. Do not invent a new purpose, ask, or outcome at execution time. Structural pages may advance the contract by establishing relevance / tension / decision frame or by completing the final commitment; they are not exempt from having a reason to exist.
@@ -55,11 +77,11 @@ Use lock values literally and optional `Template Application` from the retained
| `balanced` | Keep the primary claim and its evidence on the page; let notes add interpretation and transitions. Mix prose, structured evidence, and necessary lists according to their semantic relationship. | | `balanced` | Keep the primary claim and its evidence on the page; let notes add interpretation and transitions. Mix prose, structured evidence, and necessary lists according to their semantic relationship. |
| `presentation` | Make one claim and one dominant visual expression legible at projection distance. Keep visible copy concise; put explanation and transitions in notes instead of creating paragraph dumps or compressed bullet prose. | | `presentation` | Make one claim and one dominant visual expression legible at projection distance. Keep visible copy concise; put explanation and transitions in notes instead of creating paragraph dumps or compressed bullet prose. |
The §IX wording and sourced facts remain authoritative. Do not rewrite, drop, or invent content to force a mode at execution time. When the authored texture materially conflicts with the lock, render the least-destructive faithful composition and surface `warning: P<NN> content texture conflicts with consumption_mode <value>` as an upstream outline issue; do not encode this subjective judgment in the checker. §IX and sourced facts remain authoritative. Use its wording when it works; adapt it when presentation benefits while preserving intent, necessary content, and explicit literal requirements. Never drop or invent facts to force a mode. When the authored texture materially conflicts with the lock, render the least-destructive faithful composition and surface `warning: P<NN> content texture conflicts with consumption_mode <value>` as an upstream outline issue; do not encode this subjective judgment in the checker.
**Per-block expression**: render each `design_spec.md §IX Content` block in its written texture — a full-sentence block as wrapped prose, a fragment/label block as bullets/keywords. **Never split a full-sentence block into a bullet list** — splitting loses the information that the block was continuous reasoning, not a set of parallel points; not because a bullet lays out easier, and not because an inherited template slot is shaped as a list. If a block carries no clear texture, infer the mode from its wording and the page layout. **Per-block expression**: render each `design_spec.md §IX Content` block in its written texture — a full-sentence block as wrapped prose, a fragment/label block as bullets/keywords. **Never split a full-sentence block into a bullet list** — splitting loses the information that the block was continuous reasoning, not a set of parallel points; not because a bullet lays out easier, and not because an inherited template slot is shaped as a list. If a block carries no clear texture, infer the mode from its wording and the page layout.
- **Hard rule — one paragraph, one text frame**: use one `<text>` per prose paragraph, never one sibling `<text>` per visual line. Keep the first line as direct text; each later wrap is a direct `<tspan>` that repeats the parent `x`, keeps its effective font size, and uses one positive relative `dy`. An all-`<tspan>` form may start with `dy="0"`. Line height: 1.41.5× for dense/small body, 1.62.0× for large/breathing text. - **Hard rule — one paragraph, one text frame**: use one `<text>` per prose paragraph, never one sibling `<text>` per visual line. Keep the first line as direct text; each later wrap is a direct `<tspan>` that repeats the parent `x`, keeps its effective font size, and uses one positive relative `dy`. An all-`<tspan>` form may start with `dy="0"`. Choose consistent positive line spacing from the typeface, size, density, and reading distance; no fixed ratio overrides legibility or the selected style.
- **Template precedence**: when an inherited template slot is a bullet list but the §IX block is prose, the prose wins — widen or reflow the container to hold the paragraph, or drop that card; do not pour the sentence back into the list slot. - **Template precedence**: when an inherited template slot is a bullet list but the §IX block is prose, the prose wins — widen or reflow the container to hold the paragraph, or drop that card; do not pour the sentence back into the list slot.
- **Mode precedence**: the locked mode shapes voice / register, not §IX's authored titles or page order. When a `§IX` title is a user-authored topic label, keep it — do not upgrade it to an assertion just because the mode (e.g. `pyramid`) favors them; mode title-tendencies apply only to AI-drafted titles. - **Mode precedence**: the locked mode shapes voice / register, not §IX's authored titles or page order. When a `§IX` title is a user-authored topic label, keep it — do not upgrade it to an assertion just because the mode (e.g. `pyramid`) favors them; mode title-tendencies apply only to AI-drafted titles.
@@ -69,19 +91,19 @@ The §IX wording and sourced facts remain authoritative. Do not rewrite, drop, o
**Missing field in an existing lock**: follow [`failure-recovery.md`](../workflows/governance/failure-recovery.md) §2. **Missing field in an existing lock**: follow [`failure-recovery.md`](../workflows/governance/failure-recovery.md) §2.
**Forbidden — values outside the lock**: **Execution anchors and contextual values**:
- Colors (fill / stroke / stop-color) MUST come from `colors`
- Icons MUST come from `icons.inventory`; library MUST equal `icons.library` - Icons MUST come from `icons.inventory`; library MUST equal `icons.library`
- Font family from `typography`: use role override (`title_family` / `body_family` / `emphasis_family` / `code_family`) if declared, else fall back to `font_family` - 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.
- Font sizes follow a ramp anchored on `typography.body`. Structural roles use their locked size deck-wide; recurring feature roles such as lead, pull quote, or hero number need their own lock slot. Never resize one role page by page or inherit a template placeholder size. - 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.
- **Core message ≥ `body`**: map the page's primary claim to locked `lead` / `subtitle`, never below body. Footnotes, page numbers, and credits use locked `footnote` / `annotation`; do not invent smaller sizes. - Font sizes use the named `typography` role values as deck-wide anchors. Map every structural or feature 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.
- **Write locked px verbatim, with at most two decimals.** Do not substitute familiar pt-style numbers or emit long precision tails. - **Core message ≥ `body`**: map the page's primary claim to declared `lead` / `subtitle`, never below the current body treatment. Footnotes, page numbers, and credits use declared `footnote` / `annotation`; do not invent a smaller role.
- **Bounded body-fit last resort**: reflow geometry first; only an overflowing body block may step down by `2`px, never below `body 4`px. Other roles never shrink. At the floor, warn instead of dropping content or repaginating. Mirror pages preserve source typography. - **Write unitless px, with at most two decimals.** Use only the mapped role's anchor or a value within its `±2`px band; do not substitute familiar pt-style numbers or emit long precision tails.
- **Outside-band recovery**: reflow geometry and use the declared role band locally. If the page needs a new semantic role, a size outside anchor `±2`px, or a hierarchy change, stop and return to Strategist to repair the Design Spec and `spec_lock.md`, then regenerate page-context. Never flatten a justified distinction or add a role merely to silence the checker. Generated `svg_output/` values outside every declared band are blocking errors; mirror pages preserve exact source typography as inherited input.
- Images MUST reference files listed under `images`; no invented filenames - Images MUST reference files listed under `images`; no invented filenames
- Formula PNGs are images with `Acquire Via: formula`; place a `Rendered` file only from its listed path, use the normal placeholder for `Needs-Manual`, and never recreate the formula as text. - Formula PNGs are images with `Acquire Via: formula`; place a `Rendered` file only from its listed path, use the normal placeholder for `Needs-Manual`, and never recreate the formula as text.
If a page needs a value not in `spec_lock.md`, surface it — do not silently invent one. When an intentional deck-wide or recurring color, type role/size, icon, or image is approved, extend `spec_lock.md` **before** drawing the first affected object, regenerate that page's context bundle, and only then author the page; do not draw with a temporary hardcoded value and retroactively silence drift warnings. Return upstream before any derived/accent identity becomes recurring or structural, or before typography needs a new semantic role or an outside-band size, then regenerate context. Local garnish and same-role `±2`px adjustments need no lock row. Never expand the lock to silence a comparison. Icons, images, structural fonts, role anchors, and resources keep their inventory/role rules.
**Per-page layout rhythm — `page_rhythm` section**: **Per-page layout rhythm — `page_rhythm` section**:
@@ -105,7 +127,7 @@ Before drawing each page, look up its entry in `page_rhythm` (key format `P<NN>`
## 3. Execution Guidelines ## 3. Execution Guidelines
- **Proximity**: group related elements with tight spacing; separate unrelated groups - **Proximity**: group related elements with tight spacing; separate unrelated groups
- **Element grouping (Mandatory)**: wrap each logical Slide-local body unit in a descriptive, page-unique top-level `<g id>`. Every visible direct root `<g>` declares root-coordinate `data-pptx-bounds="x y width height"`; frame/native coordinates do not replace it, and placeholder bounds also supply the slot frame. Nested groups need no bounds and any such values are ignored. Checker compares root bounds with the `viewBox` and recursively checks only estimable text against its root module: through `1px` is ignored, through `5%` warns, above `5%` fails per side. Images, shapes, paths, `<use>`, effects, and object frames remain geometrically free. Flat pages use ordinary groups; structured slots already qualify, while titles and direct Master/Layout atoms may remain root primitives. - **Element grouping (Mandatory)**: wrap each logical Slide-local body unit in a descriptive, page-unique top-level `<g id>`. Every visible direct root `<g>` declares root-coordinate `data-pptx-bounds="x y width height"`; frame/native coordinates do not replace it, and placeholder bounds also supply the slot frame. Nested groups need no bounds and any such values are ignored. Checker compares root bounds with the `viewBox` and recursively checks only estimable text against its root module: through `1px` is ignored, through `5%` warns, above `5%` fails per side. Images, shapes, paths, `<use>`, effects, and object frames remain geometrically free. Flat pages use ordinary groups; structured slots already qualify, while titles, direct Master/Layout atoms, and canvas-level static framing may remain root primitives. On flat pages, give a root background image or full-canvas scrim/decoration rectangle a stable `id` plus `data-pptx-role="background"` / `"decoration"`; never wrap it only to silence the advisory.
- **Spec adherence**: follow color, layout, canvas format, and typography in the spec - **Spec adherence**: follow color, layout, canvas format, and typography in the spec
- **Template structure**: inherit the native visual framework only for `template_reuse_scope: mirror|layout`; `style` uses the flat route - **Template structure**: inherit the native visual framework only for `template_reuse_scope: mirror|layout`; `style` uses the flat route
- **Main-agent ownership**: SVG generation must run in the main agent (not sub-agents) — pages share upstream context for cross-page visual continuity - **Main-agent ownership**: SVG generation must run in the main agent (not sub-agents) — pages share upstream context for cross-page visual continuity
@@ -116,8 +138,8 @@ Before drawing each page, look up its entry in `page_rhythm` (key format `P<NN>`
- **Reference — prefer semantic geometry over preset stacks**: for relationships such as ascending, converging, breaking through, or stacking, consider one page-specific polygon/path that expresses the relationship before stacking generic arrows. This does not override §3.0 when one literal stock shape is the semantic object. - **Reference — prefer semantic geometry over preset stacks**: for relationships such as ascending, converging, breaking through, or stacking, consider one page-specific polygon/path that expresses the relationship before stacking generic arrows. This does not override §3.0 when one literal stock shape is the semantic object.
- **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 body containers flat. - **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 body containers flat.
- **Phased generation** (recommended): - **Phased generation** (recommended):
1. **Visual Construction Phase**: generate all SVG pages sequentially for visual consistency. Use layout judgment for chart marks during the draft. **MUST embed plot-area markers** per [`executor-chart.md`](./executor-chart.md) §2.1 on every chart page — coordinate calibration is a post-generation step (see [`verify-charts`](../workflows/stages/verify-charts.md)) that depends on these markers — and **native object metadata** per [`executor-chart.md`](./executor-chart.md) §2.2 on every eligible data-chart page. **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). **First-page gate (Mandatory)**: after completing the first page, run `python3 scripts/svg_quality_checker.py <project_path> --stage first-page` and fix every error before drawing page 2. This mode checks P01 only. After it passes, draw P02 through the last page without checker calls. 1. **Visual Construction Phase**: generate all SVG pages sequentially for visual consistency. Use layout judgment for chart marks during the draft. **MUST embed plot-area markers** per [`executor-chart.md`](./executor-chart.md) §2.1 on every §IX-planned data-chart page — coordinate calibration is a post-generation step (see [`verify-charts`](../workflows/stages/verify-charts.md)) that depends on these markers — and **native object metadata** per [`executor-chart.md`](./executor-chart.md) §2.2 on every planned native-ready object. **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). **First-page gate (Mandatory)**: after completing the first page, run `python3 scripts/svg_quality_checker.py <project_path> --stage first-page --json` 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.
2. **Quality Check Gate**: only after every planned SVG exists, run `python3 scripts/svg_quality_checker.py <project_path> --stage final --json` on `svg_output/`. Any `error` (banned/unsupported features, invalid values, unresolved references, viewBox mismatch, etc.) MUST be fixed on the offending page before proceeding — regenerate and re-check. Every `warning` is advisory: it never sends the page back for required modification, never authorizes automatic rewriting of compatible user syntax, and needs no acknowledgement/disposition line. Recommendation warnings describe the generated-SVG default; fidelity/quality warnings may be surfaced when material, while the existing input remains releasable. Prototype-identical diagnostics are recorded as `inherited`, source conversion losses as `source-import`, changed/new advisories as `introduced`, and release failures as `blocking` in `validation/svg_quality_report.json`. If release truly depends on a condition, it belongs in `errors`. On success, use the exit status and terminal summary; do not open or `cat` the complete JSON into model context. Read only targeted fields for failure investigation or an explicit audit request. Do NOT defer error handling to after `finalize_svg.py` — finalize rewrites SVG and masks some violations. 2. **Quality Check Gate**: only after every planned SVG exists, run `python3 scripts/svg_quality_checker.py <project_path> --stage final --json` on `svg_output/` without `tail` / `head` / `grep` filtering. One run already reports all pages. Review its complete issue set, fix every `error` plus any selected advisory warnings in one consolidated edit pass, then perform one verification rerun. If it still fails, its complete output begins the next batch cycle; never use checker calls to discover or fix one next issue at a time. Every `warning` is advisory: it never sends the page back for required modification, never authorizes automatic rewriting of compatible user syntax, and needs no acknowledgement/disposition line. Recommendation warnings describe the generated-SVG default; fidelity/quality warnings may be surfaced when material, while the existing input remains releasable. Prototype-identical diagnostics are recorded as `inherited`, source conversion losses as `source-import`, changed/new advisories as `introduced`, and release failures as `blocking` in `validation/svg_quality_report.json`. If release truly depends on a condition, it belongs in `errors`. On success, use the exit status and terminal summary; do not open or `cat` the complete JSON into model context. If terminal output is truncated on failure, read only the relevant issue arrays from the report written by that same run. Do NOT defer error handling to after `finalize_svg.py` — finalize rewrites SVG and masks some violations.
3. **Logic Construction Phase**: after SVGs pass the quality check, batch-generate speaker notes for narrative continuity. 3. **Logic Construction Phase**: after SVGs pass the quality check, batch-generate speaker notes for narrative continuity.
### 3.0 Native Preset Shape Selection ### 3.0 Native Preset Shape Selection
@@ -176,7 +198,7 @@ Examples: `01_封面.svg` / `02_目录.svg` / `03_核心优势.svg`; `01_cover.s
Strategist chooses the library and inventory; Executor only implements. Library details and one-library rule: [`../templates/icons/README.md`](../templates/icons/README.md). This section defines placeholder syntax. Strategist chooses the library and inventory; Executor only implements. Library details and one-library rule: [`../templates/icons/README.md`](../templates/icons/README.md). This section defines placeholder syntax.
> **Resolution is project-first.** Strategist copied the chosen icons into `<project_path>/icons/<lib>/` (via `icon_sync.py`); `finalize_svg.py embed-icons` embeds from there, falling back to the global library per-icon. **Custom icons**: drop an `.svg` into `<project_path>/icons/<lib>/` (any `<lib>`, e.g. `custom/`) and reference it as `data-icon="<lib>/<name>"` — it embeds like any other. Reference only icons in the `spec_lock.md` inventory. > **Resolution is project-first.** Strategist copied the chosen icons into `<project_path>/icons/<lib>/` (via `icon_sync.py`); `finalize_svg.py embed-icons` embeds from there, falling back to the global library per-icon. Custom SVGs must already exist in the prepared project inventory under `<project_path>/icons/<lib>/`. Reference only icons in `spec_lock.md icons.inventory`.
> **Icon identifiers are case-sensitive filenames.** For bundled libraries, copy the verified lowercase basename exactly (`tabler-outline/award`, never `tabler-outline/Award`) into `spec_lock.md` and every `data-icon` value. Custom icon identifiers preserve the custom file's exact case; the pipeline never silently lowercases names. > **Icon identifiers are case-sensitive filenames.** For bundled libraries, copy the verified lowercase basename exactly (`tabler-outline/award`, never `tabler-outline/Award`) into `spec_lock.md` and every `data-icon` value. Custom icon identifiers preserve the custom file's exact case; the pipeline never silently lowercases names.
@@ -211,49 +233,20 @@ Strategist chooses the library and inventory; Executor only implements. Library
> >
> Icons are auto-embedded by `finalize_svg.py` — no need to run `embed_icons.py` manually. > Icons are auto-embedded by `finalize_svg.py` — no need to run `embed_icons.py` manually.
**Searching for icons** — use terminal, zero token cost: **Locked-id verification only**: verify the exact project-local file already named in `icons.inventory`:
```bash ```bash
ls skills/ppt-master/templates/icons/chunk-filled/ | grep home test -f "<project_path>/icons/<lib>/<name>.svg"
ls skills/ppt-master/templates/icons/tabler-filled/ | grep home
ls skills/ppt-master/templates/icons/tabler-outline/ | grep chart
ls skills/ppt-master/templates/icons/phosphor-duotone/ | grep house
ls skills/ppt-master/templates/icons/simple-icons/ | grep github
``` ```
**Abstract concept → icon name** (names for `chunk-filled`; tabler libraries use their own equivalents — verify with `ls | grep`): **Missing locked icon** → return to Strategist's inventory / `icon_sync.py` gate. Do not search the global library, select an alternative, copy a candidate, or edit the lock in Executor.
| Concept | chunk-filled | tabler-filled / tabler-outline | **Hard rule — icon inventory**: use only the Design Spec's approved inventory. Mixing stylistic libraries within one deck is FORBIDDEN.
|---------|-------|-------------------------------|
| Growth / Increase | `arrow-trend-up` | same |
| Decline / Decrease | `arrow-trend-down` | same |
| Success / Complete | `circle-checkmark` | `circle-check` |
| Warning / Risk | `triangle-exclamation` | `alert-triangle` |
| Innovation / Idea | `lightbulb` | `bulb` |
| Strategy / Goal | `target` | same |
| Efficiency / Speed | `bolt` | same |
| Collaboration / Team | `users` | same |
| Settings / Config | `cog` | `settings` |
| Security / Trust | `shield` | same |
| Money / Finance | `dollar` | `currency-dollar` |
| Time / Deadline | `clock` | same |
| Location / Region | `map-pin` | same |
| Communication | `comment` | `message` |
| Analysis / Data | `chart-bar` | same |
| Process / Flow | `arrows-rotate-clockwise` | `refresh` |
| Global / World | `globe` | `world` |
| Excellence / Award | `star` | same |
| Expand / Scale | `maximize` | same |
| Problem / Issue | `bug` | same |
> For self-evident names (home, user, file, search, arrow, etc.) — just `grep chunk-filled/` directly without consulting the table.
> ⚠️ **Icon validation**: only use icons from the Design Spec's approved inventory. Verify each via `ls | grep` before use. Mixing libraries within one deck is FORBIDDEN.
--- ---
## 5. Font Usage ## 5. Font Usage
Source of truth: `spec_lock.md typography`. Use `font_family` as default; override per role with `title_family` / `body_family` / `emphasis_family` / `code_family` if declared. LaTeX formulas that Strategist rendered are PNG images, not a `code_family` text role. Structural typography anchors come from `spec_lock.md typography`. Use an exact `<role>_family` when declared; title roles otherwise use `title_family`, and body/support roles otherwise use `body_family`. `font_family` is the legacy/default fallback, not a reason to erase role differences. Sparse accent families follow §2.1; all structural text uses selected families. LaTeX formulas rendered by Strategist are PNG images, not a `code_family` role.
**Missing required field — `typography.font_family`** → stop and return to Generate Step 4 / [`strategist.md`](strategist.md) §6.2 to repair `spec_lock.md`; do not infer a stack from `design_spec.md`. **Missing required field — `typography.font_family`** → stop and return to Generate Step 4 / [`strategist.md`](strategist.md) §6.2 to repair `spec_lock.md`; do not infer a stack from `design_spec.md`.
@@ -4,7 +4,7 @@
Conditional Executor authority for data charts, chart-catalog adaptations, chart verification markers, and eligible native chart/table replacement metadata. Conditional Executor authority for data charts, chart-catalog adaptations, chart verification markers, and eligible native chart/table replacement metadata.
**Trigger**: load when `design_spec.md §VII` contains a chart/table visualization, `spec_lock.md page_charts` contains any row, or the current page carries any data-encoded chart or text-grid table. Mini charts, sparklines, inset charts, and small multiples count even when they are absent from `page_charts` or the chart catalog. **Trigger**: load when `design_spec.md §VII` contains a selected chart/table reference, `spec_lock.md page_charts` contains any row, or the current §IX page block carries any data-encoded chart or text-grid table. Mini charts, sparklines, inset charts, and small multiples count even when they are absent from `page_charts` or the chart catalog.
## 1. Reference Loading and Per-page Selection ## 1. Reference Loading and Per-page Selection
@@ -15,18 +15,18 @@ For each selected `templates/charts/<key>.svg`, use its Skill-relative `referenc
Before drawing each page, look up its entry in `page_charts` to decide which chart structure applies (the SVG itself was loaded in §1): Before drawing each page, look up its entry in `page_charts` to decide which chart structure applies (the SVG itself was loaded in §1):
- Entry present (e.g., `P09: timeline_horizontal`) → adapt the corresponding chart SVG already in context under §3; do not copy it verbatim. Use the selected §VII row and SVG; do not load the full chart catalog during execution. - Entry present (e.g., `P09: timeline_horizontal`) → adapt the corresponding chart SVG already in context under §3; do not copy it verbatim. Use the selected §VII row and SVG; do not load the full chart catalog during execution.
- No entry for this page → either no chart on this page, or a chart that didn't match any catalog template (Strategist's `no-template-match` fallback). Design the visualization from scratch using `design_spec.md §VII` for guidance. - No entry for this page → no catalog reference was selected. Follow the current §IX `Visualization` / `Layout`; design any declared custom visualization from scratch without inventing a §VII reference.
- Whole section absent → no chart pages in this deck. - Whole section absent → no catalog references were selected; §IX may still contain custom data charts or tables.
--- ---
## 2. Chart and Native-data Authoring ## 2. Chart and Native-data Authoring
### 2.1 Chart Plot-Area Marker (MANDATORY on every chart page) ### 2.1 Chart Plot-Area Marker (MANDATORY on planned data-chart pages)
> The [`verify-charts`](../workflows/stages/verify-charts.md) stage enumerates chart pages from `design_spec.md §VII`, then reads each page's plot-area marker to feed `svg_position_calculator.py`. A missing marker invokes that stage's declared fallback and adds avoidable derivation work. > The [`verify-charts`](../workflows/stages/verify-charts.md) stage enumerates data-driven chart pages from `design_spec.md §IX`, cross-checks selected §VII references when present, then reads each page's plot-area marker to feed `svg_position_calculator.py`. A missing marker invokes that stage's declared fallback and adds avoidable derivation work.
**Hard rule**: every SVG page that contains a data visualization chart includes a plot-area marker inside `<g id="chartArea">`, placed **after axis lines** and **before the first data element** (bar, line, area, point). **Hard rule**: every page whose §IX `Visualization` declares data-driven chart geometry includes a plot-area marker inside `<g id="chartArea">`, placed **after axis lines** and **before the first data element** (bar, line, area, point). A legacy §VII data-chart row counts when its page block lacks that declaration. An incidental microvisual not promoted in §IX needs no marker; if its geometry must enter `verify-charts`, return upstream and update the owning §IX page block first.
**Rectangular plot area** (bar / horizontal_bar / grouped_bar / stacked_bar / line / area / stacked_area / scatter / waterfall / pareto / butterfly): **Rectangular plot area** (bar / horizontal_bar / grouped_bar / stacked_bar / line / area / stacked_area / scatter / waterfall / pareto / butterfly):
@@ -53,7 +53,7 @@ Before drawing each page, look up its entry in `page_charts` to decide which cha
| `cx, cy` | Center point of pie/donut/radar (accounting for `transform="translate()"`) | | `cx, cy` | Center point of pie/donut/radar (accounting for `transform="translate()"`) |
| `r` | Outer radius of the chart | | `r` | Outer radius of the chart |
**Per-page verification** — after writing each chart SVG, confirm the marker exists: **Per-page verification** — after writing each planned data-chart SVG, confirm the marker exists:
```bash ```bash
rg -n "chart-plot-area" <project_path>/svg_output/<current_page>.svg rg -n "chart-plot-area" <project_path>/svg_output/<current_page>.svg
@@ -65,15 +65,15 @@ rg -n "chart-plot-area" <project_path>/svg_output/<current_page>.svg
> the same library do not use a plot-area marker. > the same library do not use a plot-area marker.
Technical SVG/PPT constraints remain in [`shared-standards-core.md`](./shared-standards-core.md). Technical SVG/PPT constraints remain in [`shared-standards-core.md`](./shared-standards-core.md).
### 2.2 PowerPoint-Native Chart/Table Replacement Marker (MANDATORY on eligible data-chart and text-grid table pages) ### 2.2 PowerPoint-Native Chart/Table Replacement Marker (MANDATORY on planned native-ready objects)
> `svg_to_pptx.py --native-charts-and-tables` replaces marked groups with PowerPoint-native Chart/Table objects (charts get an embedded Excel workbook). Markers stay dormant in the default export, whose SVG children become independently editable DrawingML shapes, but a deck without markers can never form data-backed native Chart/Table objects. Write the marker at draw time: the data is already in hand, and recovering it later costs a full re-read pass. > `svg_to_pptx.py --native-charts-and-tables` replaces marked groups with PowerPoint-native Chart/Table objects (charts get an embedded Excel workbook). Markers stay dormant in the default export, whose SVG children become independently editable DrawingML shapes. Prepare this optional capability for planned independent data objects, not every numeric embellishment.
**Hard rule**: before deciding whether a chart or table is eligible for native replacement, load [`native-data-interface.md`](./native-data-interface.md). Every data chart whose type appears in that authority's **Supported chart types** list gets `data-pptx-replace-with="chart"` plus one `<metadata type="application/json">` JSON child on its top-level `<g>`, transcribing the same data just plotted. Every pure text-grid data table gets `data-pptx-replace-with="table"` the same way, transcribing all visible cell text into `columns` / `rows`. The parent marker determines the JSON schema; do not duplicate a chart/table kind on the metadata child. **Hard rule**: load [`native-data-interface.md`](./native-data-interface.md) for each independent data chart or pure text-grid table whose §IX page block says `Native-ready: yes`. A supported data chart then gets `data-pptx-replace-with="chart"` plus one JSON `<metadata>` child; a pure text-grid table gets the table form, transcribing all plotted data or visible cells. `no` stays ordinary SVG even when a catalog reference contains a marker. For legacy specs only, use the matching §VII value when §IX has no field. Missing, conflicting, or invalid values return upstream; Executor never infers eligibility. The parent marker selects the schema.
**MUST — atomic authoring**: Treat the visible SVG fallback, the parent `data-pptx-replace-with` marker, and its JSON `<metadata>` child as one object. Write all three in the same SVG edit while the plotted data is in context. A supported chart or eligible table is unfinished if either the marker or metadata is missing; do not defer either one to `verify-charts`, the final quality gate, or export. **MUST — atomic authoring**: For each native-ready object, treat the visible SVG fallback, the parent `data-pptx-replace-with` marker, and its JSON `<metadata>` child as one object. Write all three in the same SVG edit while the data is in context. Do not defer the marker or metadata to `verify-charts`, the final quality gate, or export.
**Hard rule — eligibility follows data semantics**: Size, point count, visual prominence, page role, catalog match, and the current export mode do not change eligibility. A two-point mini line chart, sparkline, chart inset, KPI-card trend, or small multiple that encodes recoverable categories/values still gets its own marked top-level `<g>` and metadata payload when its chart type is supported. Decorative strokes and arrows without recoverable chart data remain ordinary SVG geometry. **Hard rule — eligibility follows the plan**: A two-point line or small multiple gets metadata only when its §IX page block explicitly plans it as an independent object with `Native-ready: yes`. A sparkline, inset, KPI-card trend, or other incidental microvisual stays ordinary SVG even when values are recoverable. Changing eligibility requires upstream repair.
Generated authoring MUST omit `data-pptx-import-source` and Generated authoring MUST omit `data-pptx-import-source` and
`data-pptx-fallback-sha256`: those attributes record imported-PPTX provenance `data-pptx-fallback-sha256`: those attributes record imported-PPTX provenance
@@ -82,9 +82,9 @@ catalog or reusable template; normal content edits would make it stale.
`data-pptx-replace-with` is a **data-backed replacement claim**, not a generic label for a group that contains numbers and not a marker for ordinary PowerPoint shapes or connectors. Add it only when the matching JSON payload can be written in the same edit; if the object is meant to remain SVG geometry, do not add the marker. `data-pptx-replace-with` is a **data-backed replacement claim**, not a generic label for a group that contains numbers and not a marker for ordinary PowerPoint shapes or connectors. Add it only when the matching JSON payload can be written in the same edit; if the object is meant to remain SVG geometry, do not add the marker.
- Chart types absent from that list and conceptual/diagrammatic graphics (process flows, cycles, quadrant cards, timelines, or a KPI card container) get **no marker**`svg_quality_checker.py` rejects unsupported marker types. A supported data chart nested visually inside one of those compositions still gets its own marker. - Chart types absent from that list and conceptual/diagrammatic graphics (process flows, cycles, quadrant cards, timelines, or a KPI card container) get **no marker**`svg_quality_checker.py` rejects unsupported marker types. A supported data chart nested inside one of those compositions gets its own marker only when its §IX page block explicitly plans that object as `Native-ready: yes`.
- Canonical rectangular merged text cells may carry a table marker by putting anchor-only `row_span` / `col_span` in metadata and leaving covered cells blank. Nonrectangular/overlapping merges, nonblank covered cells, and graphical cells (icons, harvey balls, rating dots) get **no table marker** and stay on the SVG fallback route. - Canonical rectangular merged text cells may carry a table marker by putting anchor-only `row_span` / `col_span` in metadata and leaving covered cells blank. Nonrectangular/overlapping merges, nonblank covered cells, and graphical cells (icons, harvey balls, rating dots) get **no table marker** and stay on the SVG fallback route.
- Transcribe, don't restyle: `categories` / `series[].values` are the numbers just plotted; `style.colors` carries the series HEX values already used on the page (from `spec_lock.colors`). - Transcribe, don't restyle: `categories` / `series[].values` are the numbers just plotted; `style.colors` copies the series HEX values already rendered on the page, whether they use a recurring `spec_lock.colors` anchor or a contextual page-local color.
- Data-point color: when a single column/bar series uses data-point colors in the fallback, copy those fills into `series[].point_colors` in category order. - Data-point color: when a single column/bar series uses data-point colors in the fallback, copy those fills into `series[].point_colors` in category order.
- Data labels: when visible point values are part of the fallback chart, write `data_labels` instead of companion text; use `data_labels.points` for selected labels, and use `number_format`, `font_size`, `font_family`, and per-point `colors` / `color` when the fallback labels carry suffixes or color-coded text. - Data labels: when visible point values are part of the fallback chart, write `data_labels` instead of companion text; use `data_labels.points` for selected labels, and use `number_format`, `font_size`, `font_family`, and per-point `colors` / `color` when the fallback labels carry suffixes or color-coded text.
- Line markers: when the fallback line chart draws visible point nodes, set `line_style: "lineMarker"`; leave the default `line` only for line charts without nodes. - Line markers: when the fallback line chart draws visible point nodes, set `line_style: "lineMarker"`; leave the default `line` only for line charts without nodes.
@@ -93,11 +93,11 @@ catalog or reusable template; normal content edits would make it stale.
- Value-axis labels: when the fallback keeps category labels but intentionally omits numeric value-axis tick labels, set `show_value_axis_labels: false`. - Value-axis labels: when the fallback keeps category labels but intentionally omits numeric value-axis tick labels, set `show_value_axis_labels: false`.
- Freeform chart text: transcribe center labels, source notes, and other in-chart annotations as companion `caption` / `note` / `notes` entries with explicit slide-coordinate bounds; do not rely on fallback `<text>` children to survive native export. - Freeform chart text: transcribe center labels, source notes, and other in-chart annotations as companion `caption` / `note` / `notes` entries with explicit slide-coordinate bounds; do not rely on fallback `<text>` children to survive native export.
- Native chart typography mirrors the SVG fallback. Copy the fallback's shared chart font into `style.font_family` and visible chart text sizes into the matching metadata fields (`title_font_size`, `subtitle_font_size`, `axis_font_size`, `note_font_size`, etc.) only when role sizes differ; otherwise let the exporter infer them from visible fallback text. When a visible chart title, subtitle, or axis title needs its own size/color/font, write that field as an object with `text`, `font_size`, `font_family`, and `color`. Use `axis_title_font_size`, `legend_font_size`, or companion per-entry `font_size` only when the fallback visibly uses a separate size. - Native chart typography mirrors the SVG fallback. Copy the fallback's shared chart font into `style.font_family` and visible chart text sizes into the matching metadata fields (`title_font_size`, `subtitle_font_size`, `axis_font_size`, `note_font_size`, etc.) only when role sizes differ; otherwise let the exporter infer them from visible fallback text. When a visible chart title, subtitle, or axis title needs its own size/color/font, write that field as an object with `text`, `font_size`, `font_family`, and `color`. Use `axis_title_font_size`, `legend_font_size`, or companion per-entry `font_size` only when the fallback visibly uses a separate size.
- Native table typography mirrors the SVG fallback. Write `style.font_family` and `style.font_size` from the visible table text; use `header_font_size` or per-cell `font_size` only when the fallback visibly does so. If the fallback has no explicit table font, fall back to the deck body family and locked body size from `spec_lock.md typography`. - Native table typography mirrors the SVG fallback. Write `style.font_family` and `style.font_size` from the visible table text; use `header_font_size` or per-cell `font_size` only when the fallback visibly does so. If the fallback has no explicit table font, fall back to the deck body family and declared body anchor from `spec_lock.md typography`.
- The marker group's transform stays translate/scale only (no rotate / matrix / skew). - The marker group's transform stays translate/scale only (no rotate / matrix / skew).
- Visual parity is not a goal: the SVG drawing remains the designed visual and exports as editable DrawingML shapes; the native object is the data-backed counterpart with PowerPoint's chart/table-specific model. Never simplify the SVG design to match what a native object could show. - Visual parity is not a goal: the SVG drawing remains the designed visual and exports as editable DrawingML shapes; the native object is the data-backed counterpart with PowerPoint's chart/table-specific model. Never simplify the SVG design to match what a native object could show.
**Per-page verification** — after writing each eligible data-chart or text-grid table page, enumerate the eligible objects and confirm a one-to-one match: every object has one parent marker and exactly one JSON metadata child. Finding one marker somewhere on a page is insufficient when the page contains multiple eligible objects. **Per-page verification** — after writing a page with planned native-ready objects, enumerate those objects and confirm a one-to-one match: every object has one parent marker and exactly one JSON metadata child. Finding one marker somewhere on a page is insufficient when the page contains multiple planned objects.
```bash ```bash
rg -n 'data-pptx-replace-with="(chart|table)"|<metadata type="application/json">' <project_path>/svg_output/<current_page>.svg rg -n 'data-pptx-replace-with="(chart|table)"|<metadata type="application/json">' <project_path>/svg_output/<current_page>.svg
@@ -113,13 +113,14 @@ Chart SVGs referenced in **VII. Visualization Reference List** are loaded once t
**Hard rule**: adapt the loaded chart SVG; do not improvise from memory and do not replicate verbatim. Apply the active Design Spec and `spec_lock.md`; preserve the visualization type and data semantics. **Hard rule**: adapt the loaded chart SVG; do not improvise from memory and do not replicate verbatim. Apply the active Design Spec and `spec_lock.md`; preserve the visualization type and data semantics.
**Adaptation rules**: **Adaptation rules**:
- **Preserve**: visualization type (bar/line/pie/timeline/process/framework…), information relationships, data encoding, structural grouping, and capacity - **Preserve**: visualization type (bar/line/pie/timeline/process/framework…), information relationships, data encoding, and every content obligation in the active Design Spec
- **Selected key, flexible realization**: keep the Strategist-selected chart key/type; its preview grouping, frame count, item count, capacity, and geometry are adaptable to the actual authored content
- **Carry forward**: every planned label, value, unit, status, source, and explanatory block; never shorten or drop content to imitate a lighter catalog preview - **Carry forward**: every planned label, value, unit, status, source, and explanatory block; never shorten or drop content to imitate a lighter catalog preview
- **Adapt**: project data and labels, dimensions, axes, legend, and spacing as the authored content requires - **Adapt**: project data and labels, dimensions, axes, legend, and spacing as the authored content requires
- **Project-owned**: palette, typography, container treatment, effects, background, and page chrome; catalog preview values are fallbacks, never defaults - **Project-owned**: palette, typography, container treatment, effects, background, and page chrome; catalog preview values are fallbacks, never defaults
- **Bound final body modules**: add or revise root-coordinate `data-pptx-bounds` on every visible direct root `<g>` copied into the final page; nested groups need none, chart geometry and local references are not content-boundary inputs, and catalog reference warnings never waive the final-page contract - **Bound final body modules**: add or revise root-coordinate `data-pptx-bounds` on every visible direct root `<g>` copied into the final page; nested groups need none, chart geometry and local references are not content-boundary inputs, and catalog reference warnings never waive the final-page contract
- **Adjust with fidelity**: composition, axis ranges, and grid may change within the project contract only when no content, relationship, hierarchy, or capacity is lost - **Adjust with fidelity**: composition, axis ranges, grouping, and grid may change when the actual content, relationships, hierarchy, and data encoding remain complete
- **Forbidden**: changing visualization type without spec justification; omitting data points or structural elements from the outline - **Forbidden**: changing visualization type without spec justification; omitting planned data points, labels, relationships, or explanatory content to fit the catalog preview
> Templates: `templates/charts/`. The Strategist's selected key is already locked in `page_charts`; execution opens only that key's SVG. > Templates: `templates/charts/`. The Strategist's selected key is already locked in `page_charts`; execution opens only that key's SVG.
@@ -127,6 +128,6 @@ Chart SVGs referenced in **VII. Visualization Reference List** are loaded once t
Coordinate calibration runs as a **conditional post-generation stage**, not inside the Executor authoring loop. After SVG generation completes, if the deck contains data charts, run [`verify-charts`](../workflows/stages/verify-charts.md) before post-processing. Coordinate calibration runs as a **conditional post-generation stage**, not inside the Executor authoring loop. After SVG generation completes, if the deck contains data charts, run [`verify-charts`](../workflows/stages/verify-charts.md) before post-processing.
The executor's only obligation here is upstream: embed the `<!-- chart-plot-area ... -->` marker on every chart page during initial draft (§2.1). Verify-charts enumerates chart pages from `design_spec.md §VII` (authoritative deck plan) and uses the marker to feed `svg_position_calculator.py`. The executor's only obligation here is upstream: embed the `<!-- chart-plot-area ... -->` marker on each §IX-planned data-chart page during initial draft (§2.1). Verify-charts enumerates those pages from the authoritative page roster and uses the marker to feed `svg_position_calculator.py`.
> Do NOT run `svg_position_calculator.py` during the initial draft. The calculator calibrates already-generated SVGs against their declared plot areas; running it before the SVG exists has nothing to compare against. > Do NOT run `svg_position_calculator.py` during the initial draft. The calculator calibrates already-generated SVGs against their declared plot areas; running it before the SVG exists has nothing to compare against.
@@ -21,12 +21,12 @@ Handle images by their status in the Design Spec's Image Resource List. Status e
**Reference syntax**: see [`svg-image-embedding.md`](svg-image-embedding.md). **Reference syntax**: see [`svg-image-embedding.md`](svg-image-embedding.md).
**Template-bundled images**: when a template (deck / layout / brand) is applied, its bitmaps are copied into the project's `images/` alongside every other runtime image ([`apply-template-workspace.md`](../workflows/stages/apply-template-workspace.md)). Reference them the same way — `../images/<name>` and do **not** reproduce a template SVG's bare sibling href (e.g. `href="cover_bg.png"`): the template SVG is reference material, the rendered page lives in `svg_output/` and must point at `../images/`. `template_reuse_scope: mirror` ([`executor-structured.md`](./executor-structured.md) §1.1) is the one exception — it copies hrefs verbatim, and the exporter resolves those bare hrefs against `images/`. **Template-bundled images**: [`apply-template-workspace.md`](../workflows/stages/apply-template-workspace.md) copies them into project `images/`. Outside `mirror`, reference `../images/<name>` and never copy a template SVG's bare sibling href: the rendered page lives in `svg_output/`. `mirror` ([`executor-structured.md`](./executor-structured.md) §1.1) keeps hrefs verbatim; export resolves them against `images/`.
Use each resource row's locked layout pattern; do not collapse image-led pages into a generic left/right split. **Mandatory — selected pattern, flexible realization**: Read [`image-layout-patterns.md`](./image-layout-patterns.md) once when this branch loads, then resolve every primary/modifier id in each active §VIII/lock row to its exact catalog entry. Preserve the selected semantic composition. Adapt geometry, ratio, placement, spacing, and hierarchy for the actual page; never replace the pattern, role, file/source, must-use, or crop policy downstream. If another pattern is needed, return upstream to update the Design Spec. Only explicit user/template preservation locks exact geometry. Avoid generic left/right repetition.
**Placeholder**: Dashed border `<rect stroke-dasharray="8,4" .../>` + description text **Placeholder**: Dashed border `<rect stroke-dasharray="8,4" .../>` + description text
**`no-crop` images**: when a `spec_lock.md images` entry ends with ` | no-crop`, size the container to the image's native ratio (from `analyze_images.py` or file dims) and use `preserveAspectRatio="xMidYMid meet"`. Untagged entries are croppable — default to `slice`. **Crop policy**: read the §VIII row and matching lock projection. `crop=no-crop` (or a legacy trailing `| no-crop`) requires a native-ratio container and `preserveAspectRatio="xMidYMid meet"`. `crop=adaptive` permits but never requires cropping; choose `meet` or focal-safe `slice` from purpose, ratio, focus, and container. A missing or conflicting projection returns upstream instead of being inferred during execution.
**Formula images — declared-inference fallback for a missing `no-crop` flag**: rows with `Acquire Via: formula` or `Type: Latex Formula` MUST be treated as no-crop. For a rendered file, use dimensions in this order: current `analysis/image_analysis.csv`, `design_spec.md §VIII`, then `images/formula_manifest.json`. For a `Needs-Manual` row, size the dashed placeholder from the planned dimensions in §VIII, then the manifest; the Step 7 readiness gate re-analyzes the supplied file and reconciles the container before export. Do not normalize all formulas to one height unless the spec explicitly states that layout choice. **Formula images — declared-inference fallback for a missing `no-crop` flag**: rows with `Acquire Via: formula` or `Type: Latex Formula` MUST be treated as no-crop. For a rendered file, use dimensions in this order: current `analysis/image_analysis.csv`, `design_spec.md §VIII`, then `images/formula_manifest.json`. For a `Needs-Manual` row, size the dashed placeholder from the planned dimensions in §VIII, then the manifest; the Step 7 readiness gate re-analyzes the supplied file and reconciles the container before export. Do not normalize all formulas to one height unless the spec explicitly states that layout choice.
@@ -20,7 +20,7 @@ Conditional Executor authority for `template_reuse_scope: mirror|layout` with `p
Manifest/text-slot files are derived tool metadata, not model inputs. Missing metadata neither invalidates a legacy workspace nor permits text-topology changes. Manifest/text-slot files are derived tool metadata, not model inputs. Missing metadata neither invalidates a legacy workspace nor permits text-topology changes.
**Mapping change**: update the owning plan and regenerate that page's delta; load only a new/changed prototype fingerprint. **Mapping change**: stop and return to Strategist to update the owning plan; regenerate that page's delta before resuming, then load only a new/changed prototype fingerprint.
Resolve the per-page template SVG from `page_context.template.prototype`; the owning `spec_lock.md page_layouts` row remains authoritative. There is no filename/page-type fallback. Resolve the per-page template SVG from `page_context.template.prototype`; the owning `spec_lock.md page_layouts` row remains authoritative. There is no filename/page-type fallback.
@@ -34,17 +34,17 @@ Resolve the per-page template SVG from `page_context.template.prototype`; the ow
**Default — re-skin `layout` (may override when the application plan keeps template visuals and the lock reflects them)**: inherit geometry, label/legend placement, and series encoding; otherwise repaint template gradients, shadows, fills, and strokes from the current style/lock. Template font sizes remain placeholders. `mirror` preserves visuals under §1.1. **Default — re-skin `layout` (may override when the application plan keeps template visuals and the lock reflects them)**: inherit geometry, label/legend placement, and series encoding; otherwise repaint template gradients, shadows, fills, and strokes from the current style/lock. Template font sizes remain placeholders. `mirror` preserves visuals under §1.1.
**Font size is skin, not geometry (non-mirror).** A chart / layout template's hardcoded `font-size` values (often 1116px, sized for the template's own dense placeholder text) are NOT inherited — classify each text into its `spec_lock.md` role and use that role's locked size, exactly as you re-skin color. **Structural roles (page title / body / subtitle / annotation / footnote) hold their one deck-wide size on every page** — the template's placeholder px never overrides it; same-role text drifting page to page is what makes a deck look unprofessional. **Font size is skin, not geometry (non-mirror).** A chart / layout template's hardcoded `font-size` values (often 1116px, sized for the template's own dense placeholder text) are NOT inherited. Classify each text into its `spec_lock.md` role, start from that role's anchor, and keep any contextual adjustment within anchor `±2`px. The template's placeholder px never becomes the starting point or an extra role.
**Typography execution order (mandatory):** **Typography execution order (mandatory):**
1. Build a per-page text inventory from `design_spec.md §IX` + the current `notes/<NN>_*.md`. 1. Build a per-page text inventory from `design_spec.md §IX` + the current `notes/<NN>_*.md`.
2. Classify each text item before drawing. **Structural roles** (`title`, `subtitle` / `lead`, `body`, `annotation`, `footnote` / `page_number`) must map to their declared `spec_lock.typography` slot. A **one-off feature element** (a single hero number, an isolated emphasis label) may take an in-ramp intermediate value — the ramp is anchored on `body`, not a closed menu — but a feature size that **recurs** must be promoted to a declared slot. The failure mode this guards against is structural text silently inheriting the template's compact px, not legitimate feature sizing. 2. Classify each text item before drawing. **Structural and feature roles** (`title`, `subtitle` / `lead`, `body`, `annotation`, `footnote` / `page_number`, hero or emphasis slots) map to a declared `spec_lock.typography` size role. A missing semantic role returns upstream; do not borrow an unrelated size because it is numerically close.
3. Copy the role's locked px value into `font-size` verbatim. Do this before placing the text; never start from a template `font-size` and then "adjust". 3. Choose the role anchor or one contextual value within anchor `±2`px before placing the text. Never start from a template `font-size` and then adjust it.
4. Layout from those locked sizes: compute line-height, wrapped line count, child `y` / `dy`, card padding, card height, column gaps, and available image/chart area from the chosen px values. 4. Layout from those chosen sizes: compute line-height, wrapped line count, child `y` / `dy`, card padding, card height, column gaps, and available image/chart area.
5. Only after this reflow may you inspect fit. If fit fails, move / resize containers or simplify local geometry first; do not reduce the role size merely because the inherited template slot was smaller. 5. Reflow containers and local geometry together with the bounded role treatment; an inherited template slot never justifies leaving the declared band.
**Geometry adapts to the type, never the reverse**: when the locked size is larger than the template's placeholder text, widen or heighten the card, open spacing, and recompute child `y` / `dy`; do not shrink text to inherit a smaller container. Recompute line-height and downstream coordinates, and allocate wrapped-line height plus padding. Page count and density remain the confirmed Strategist decision: do not repaginate, split, or drop content. If a fully reflowed block still fails, apply the single bounded body-fit exception in [`executor-base.md`](./executor-base.md) §2.1. Mirror instead preserves source typography under §1.1. **Geometry and bounded type co-adapt**: widen or heighten the card, open spacing, recompute child `y` / `dy`, and choose within the mapped role's anchor `±2`px instead of inheriting the template's compact size. Page count and density remain the confirmed Strategist decision: do not repaginate, split, or drop content. If the page still needs a value outside the band, return upstream under [`executor-base.md`](./executor-base.md) §2.1. Mirror instead preserves source typography under §1.1.
### 1.1 Mirror reuse — literal page replacement ### 1.1 Mirror reuse — literal page replacement
@@ -54,7 +54,7 @@ When `spec_lock.md` records the AI-derived `template_reuse_scope: mirror`, Execu
2. **Copy, don't fill** — use the retained full mirror SVG as the starting point, then edit slide-specific text in place. Preserve every non-text element and every `data-pptx-*` structure attribute verbatim. Do not reopen the same path + SHA merely because another page selects it. 2. **Copy, don't fill** — use the retained full mirror SVG as the starting point, then edit slide-specific text in place. Preserve every non-text element and every `data-pptx-*` structure attribute verbatim. Do not reopen the same path + SHA merely because another page selects it.
3. **What you may edit** — decide the semantic slot mapping and replacement text only. Change only visible string values already carried by `<text>` and `<tspan>` nodes that express slide-specific content (title, body, captions, KPI labels, dates, page numbers). Keep the number, order, nesting relationship, and **all attributes** of every `<text>` / `<tspan>` node unchanged. Never merge or split nodes, move a string between nodes, add a new tspan, or delete an empty carrier. `svg_quality_checker.py` and export validate attributes, topology, and prototype hashes against the complete prototype internally. 3. **What you may edit** — decide the semantic slot mapping and replacement text only. Change only visible string values already carried by `<text>` and `<tspan>` nodes that express slide-specific content (title, body, captions, KPI labels, dates, page numbers). Keep the number, order, nesting relationship, and **all attributes** of every `<text>` / `<tspan>` node unchanged. Never merge or split nodes, move a string between nodes, add a new tspan, or delete an empty carrier. `svg_quality_checker.py` and export validate attributes, topology, and prototype hashes against the complete prototype internally.
4. **What you must not touch** — element positions, sizes, fonts, colors, fills, strokes, gradients, **which image each `<image>` points at**, `<g>` grouping, sprite-sheet `<svg viewBox>` wrappers, decorative `<rect>` / `<path>` / `<circle>` / `<polygon>` shapes, `<use data-icon="...">` markers, embedded chart data structures. Mirror's value is preserving the source deck's visual identity — any geometric / decorative drift defeats the purpose. **The `href` path is not the image**: normalizing a bare `href="cover_bg.png"` to `href="../images/<name>"` (when Step 3 relocated the asset to `images/`) points at the *same* image and changes nothing visual — that is an allowed path fix, not a fidelity edit. Leaving the bare href as-is is also fine; the exporter and live preview resolve bare hrefs against `images/` either way. 4. **What you must not touch** — element positions, sizes, fonts, colors, fills, strokes, gradients, **which image each `<image>` points at**, `<g>` grouping, sprite-sheet `<svg viewBox>` wrappers, decorative `<rect>` / `<path>` / `<circle>` / `<polygon>` shapes, `<use data-icon="...">` markers, embedded chart data structures. Mirror's value is preserving the source deck's visual identity — any geometric / decorative drift defeats the purpose. **The `href` path is not the image**: normalizing a bare `href="cover_bg.png"` to `href="../images/<name>"` (when Step 3 relocated the asset to `images/`) points at the *same* image and changes nothing visual — that is an allowed path fix, not a fidelity edit. Leaving the bare href as-is is also fine; the exporter and live preview resolve bare hrefs against `images/` either way.
5. **Content fit** — if the replacement needs a different number of text segments/items, do not merge/split nodes, drop sourced content, or restructure the grid. Select a better mirror prototype and update the planning mappings, or report `warning: P<NN> content does not fit mirror reference <basename>; choose another prototype or change template_reuse_scope to layout/style`. 5. **Content fit** — if the replacement needs a different number of text segments/items, do not merge/split nodes, drop sourced content, or restructure the grid. Report `warning: P<NN> content does not fit mirror reference <basename>; choose another prototype or change template_reuse_scope to layout/style`, then return to Strategist to select the prototype or scope and update the planning mappings.
6. **Visible text editing** — mirror SVGs may keep literal source text rather than `{{...}}` authoring markers. Edit values in place while retaining imported semantic `data-pptx-placeholder` identity and exact text topology. 6. **Visible text editing** — mirror SVGs may keep literal source text rather than `{{...}}` authoring markers. Edit values in place while retaining imported semantic `data-pptx-placeholder` identity and exact text topology.
7. **Output filename** — follow the standard project SVG naming convention (`<NN>_<page_name>.svg` where `<NN>` matches the project page index, not the mirror source index). The mirror filename is the *reference*, not the *output*. 7. **Output filename** — follow the standard project SVG naming convention (`<NN>_<page_name>.svg` where `<NN>` matches the project page index, not the mirror source index). The mirror filename is the *reference*, not the *output*.
@@ -142,7 +142,7 @@ Do **not** invent a prototype entry, and do **not** assume a structured template
- Read the current page assignment as `P<NN>: <layout_key>`. Resolve the assigned Layout key in `pptx_layouts`, then resolve its Master key in `pptx_masters`. Missing, malformed, or partial mappings stop before drawing. - Read the current page assignment as `P<NN>: <layout_key>`. Resolve the assigned Layout key in `pptx_layouts`, then resolve its Master key in `pptx_masters`. Missing, malformed, or partial mappings stop before drawing.
- Write matching root Master/Layout key and picker names. Do not write `data-pptx-layout-kind` or `data-pptx-page-role`. - Write matching root Master/Layout key and picker names. Do not write `data-pptx-layout-kind` or `data-pptx-page-role`.
- On strict template use, the row and SVG contract match the selected prototype exactly. - On strict template use, the row and SVG contract match the selected prototype exactly.
- On adaptive template use, retain the prototype Master. If the final composition changes fixed Layout atoms or slot topology/bounds, allocate a new key/name and update this row before completing the page. - On adaptive template use, retain the prototype Master and realize the Layout key/name already declared for this page. If construction proves that fixed Layout atoms or slot topology/bounds must change, stop before completing the page and return to Strategist to declare the revised definition and assignment; regenerate the current page context before resuming.
- A Layout key may repeat across non-adjacent pages only when its fixed atoms and slot contracts are identical. - A Layout key may repeat across non-adjacent pages only when its fixed atoms and slot contracts are identical.
**Structured template-page scaffold**: **Structured template-page scaffold**:
@@ -168,7 +168,7 @@ Do **not** invent a prototype entry, and do **not** assume a structured template
data-pptx-bounds="570 120 650 500"> data-pptx-bounds="570 120 650 500">
<image id="picture-carrier" data-pptx-carrier="true" /> <image id="picture-carrier" data-pptx-carrier="true" />
</g> </g>
<g id="content-block-1" data-pptx-bounds="60 120 470 500"></g> <!-- 38 content groups --> <g id="content-block-1" data-pptx-bounds="60 120 470 500"></g> <!-- one group per logical content unit -->
<g id="content-block-2" data-pptx-bounds="570 120 650 500"></g> <g id="content-block-2" data-pptx-bounds="570 120 650 500"></g>
</svg> </svg>
``` ```
@@ -21,12 +21,9 @@ Active when at least one resource list row has `Acquire Via: ai` / `web` / `slic
Defined in `design_spec.md §VIII`. Status enum: see [`svg-image-embedding.md`](svg-image-embedding.md). Defined in `design_spec.md §VIII`. Status enum: see [`svg-image-embedding.md`](svg-image-embedding.md).
| Filename | Dimensions | Purpose | Type | Acquire Via | Status | Reference | | Filename | Dimensions | Purpose / Type | Layout pattern | Crop Policy | Acquire Via | Status | Reference |
|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|---|
| cover.png | 1280x720 | Cover background | Background | `ai` | Pending | Modern tech abstract, deep blue gradient #0A2540 | | `<planned file>` | `<planned size>` | `<planned role>` | `<Strategist selection>` | `adaptive` / `no-crop` | `ai` / `web` / `slice` | Pending | `<acquisition brief>` |
| team.jpg | 800x600 | Team photo | Photography | `web` | Pending | Diverse engineering team in modern office |
| formula_001.png | 736x168 | Block equation on P03 | Latex Formula | `formula` | Rendered | `E = mc^2` |
| spot_team.png | TBD after slicing | Team spot illustration | Illustration | `slice` | Pending | From `spot_sheet.png` cell 1,1 |
**Required per non-skipped row**: `Acquire Via` and `Status`. `Reference` is required for every `web` / `slice` row and every newly authored `ai` row. An existing `ai` row whose `Reference` is omitted or blank may continue only through the declared inference in [`image-generator.md`](./image-generator.md) §8; no other path may infer it. **Required per non-skipped row**: `Acquire Via` and `Status`. `Reference` is required for every `web` / `slice` row and every newly authored `ai` row. An existing `ai` row whose `Reference` is omitted or blank may continue only through the declared inference in [`image-generator.md`](./image-generator.md) §8; no other path may infer it.
@@ -28,7 +28,7 @@ AI images exist to serve the deck's communication goal. Pick whatever combinatio
**Hard rule — only what's actually hard**: **Hard rule — only what's actually hard**:
- Same `deck_rendering` + same locked deck color roles for every image in the deck - Same `deck_rendering` + same core deck color anchors/semantic behavior for every image in the deck
- HEX codes and color names are rendering guidance — never visible text in the image - HEX codes and color names are rendering guidance — never visible text in the image
- Long body copy / data points / bulleted lists / long quotes stay in SVG (improving them later means regenerating the image, which is expensive) - Long body copy / data points / bulleted lists / long quotes stay in SVG (improving them later means regenerating the image, which is expensive)
- **In-image text is only for words that will not need editing later** — visual keywords, decorative lettering, mood words. Editable text (titles that may be reworded, subtitles, dates, authors, captions, body) belongs in SVG. Changing one in-image word costs an image regeneration; one SVG word costs a keystroke. - **In-image text is only for words that will not need editing later** — visual keywords, decorative lettering, mood words. Editable text (titles that may be reworded, subtitles, dates, authors, captions, body) belongs in SVG. Changing one in-image word costs an image regeneration; one SVG word costs a keystroke.
@@ -40,15 +40,15 @@ Everything else is the AI's judgment per page. No mandated padding, no type-lock
## 2. Style and Composition Inputs ## 2. Style and Composition Inputs
Every AI image uses one deck-wide rendering, the deck's already-locked color roles, and a per-image type / composition. Only rendering is a separate image-direction decision. Every AI image uses one deck-wide rendering, the deck's stable color anchors/semantic behavior, and a per-image type / composition. Only rendering is a separate image-direction decision.
| Dimension | Decides | When fixed | | Dimension | Decides | When fixed |
|---|---|---| |---|---|---|
| **Rendering** | Visual style family (vector / sketch-notes / 3d-isometric / corporate-photo / …) | Once per deck — every AI image in the deck shares one rendering | | **Rendering** | Visual style family (vector / sketch-notes / 3d-isometric / corporate-photo / …) | Once per deck — every AI image in the deck shares one rendering |
| **Deck colors** | The exact background / primary / accent / secondary-accent / text roles from `spec_lock.md colors`; these are consumed directly, not reconfirmed | Already locked in Stage 2 | | **Deck colors** | Core background / primary / accent / secondary-accent / text anchors from `spec_lock.md colors`, interpreted with the Design Spec and per-image context; these are not reconfirmed | Anchored after Stage 2 |
| **Type** | What the image's internal composition skeleton looks like — geometric layout of a local infographic block (infographic / flowchart / framework / matrix / cycle / funnel / pyramid / comparison / timeline / map / scene). Only applies to `page_role: local`; for `page_role: hero_page`, describe composition with §4.1 primitives instead of picking a type. | Per image | | **Type** | What the image's internal composition skeleton looks like — geometric layout of a local infographic block (infographic / flowchart / framework / matrix / cycle / funnel / pyramid / comparison / timeline / map / scene). Only applies to `page_role: local`; for `page_role: hero_page`, describe composition with §4.1 primitives instead of picking a type. | Per image |
> Rendering decides *how the image is drawn* (line quality, texture, depth). Color instructions come from the deck roles: background / secondary background usually dominate, primary carries main forms, and accents stay scarce. Adjust those proportions to the page role, but never invent or substitute HEX. > Rendering decides *how the image is drawn* (line quality, texture, depth). Color instructions begin from the deck roles: background / secondary background usually dominate, primary carries main forms, and accents stay scarce. Adjust proportions and derive coherent lighting/material/tint transitions for the image context; do not replace the deck's identity with an unrelated image-only palette.
### 2.1 Where to find each dimension ### 2.1 Where to find each dimension
@@ -56,13 +56,13 @@ Every AI image uses one deck-wide rendering, the deck's already-locked color rol
|---|---| |---|---|
| [`image-renderings/_index.md`](./image-renderings/_index.md) — rendering catalog + auto-selection table | Always (Step 1 below) | | [`image-renderings/_index.md`](./image-renderings/_index.md) — rendering catalog + auto-selection table | Always (Step 1 below) |
| [`image-type-templates/_index.md`](./image-type-templates/_index.md) — type catalog + auto-selection table | Always (Step 1 below) | | [`image-type-templates/_index.md`](./image-type-templates/_index.md) — type catalog + auto-selection table | Always (Step 1 below) |
| `image-renderings/<chosen>.md` | After Step 2 picks the rendering — only the chosen one | | `image-renderings/<chosen>.md` | After Step 2 resolves the rendering — one preset file, or every exact reference listed for `custom` |
| `image-type-templates/<chosen>.md` | After Step 3 picks the type per image — only the types actually used | | `image-type-templates/<chosen>.md` | After Step 3 picks the type per image — only the types actually used |
**Hard rule — on-demand loading**: **Hard rule — on-demand loading**:
- Read the rendering and type `_index.md` files once at role entry. - Read the rendering and type `_index.md` files once at role entry.
- After locking inputs, read **only** the specific rendering / type files selected. - After locking inputs, read **only** the specific preset rendering, custom rendering references, and type files selected.
- **Never** glob-read an entire subdirectory (`image-renderings/*.md` is forbidden). Token cost balloons and the AI loses focus. - **Never** glob-read an entire subdirectory (`image-renderings/*.md` is forbidden). Token cost balloons and the AI loses focus.
--- ---
@@ -80,7 +80,7 @@ read_file references/image-type-templates/_index.md
### Step 2 — Resolve deck-wide rendering + deck colors ### Step 2 — Resolve deck-wide rendering + deck colors
**Primary path — Strategist already locked rendering and ordinary deck colors in `spec_lock.md colors`**: **Primary path — Strategist already recorded rendering and core deck color anchors in `spec_lock.md colors`**:
``` ```
image_rendering: vector-illustration image_rendering: vector-illustration
@@ -89,9 +89,9 @@ primary: #1E3A5F
accent: #D4AF37 accent: #D4AF37
``` ```
Use them directly. Do not create another image-color choice and do not change HEX to suit a rendering. Use them as identity anchors. Do not create another user-facing image-color choice. The rendering and image subject may derive coherent tonal transitions, material colors, lighting, and atmospheric hues when the context requires them, while the core roles keep their established meaning.
**Hard rule — `custom` escape hatch**: when `image_rendering` is `custom`, do not read a preset rendering file. Splice `image_rendering_behavior` into the prompt. The deck color-role rows remain authoritative. **Hard rule — `custom` catalog basis**: when `image_rendering` is `custom`, first inspect the optional `image_rendering_references` row. If present, read every exact `image-renderings/<id>.md` it lists and synthesize their line, texture, depth, material, and mood guidance under `image_rendering_behavior` before assembling prompts. If absent, the custom is genuinely novel: read no preset file and use `image_rendering_behavior` directly. Never infer or add adjacent references during execution. The deck color-role rows remain authoritative.
**Declared-inference fallback — when an existing `spec_lock.md` omits the `image_rendering` key** (see [`failure-recovery.md`](../workflows/governance/failure-recovery.md) §2): **Declared-inference fallback — when an existing `spec_lock.md` omits the `image_rendering` key** (see [`failure-recovery.md`](../workflows/governance/failure-recovery.md) §2):
@@ -100,7 +100,7 @@ This fallback covers a missing key only. An empty or invalid value stops for loc
| Signal | Maps to | | Signal | Maps to |
|---|---| |---|---|
| `design_spec.md d. Style` mode + descriptor | Rendering (consult renderings `_index.md` auto-selection table) | | `design_spec.md d. Style` mode + descriptor | Rendering (consult renderings `_index.md` auto-selection table) |
| Existing `spec_lock.md colors` rows | Deck color-role source; never replace them from `design_spec.md` | | Existing `spec_lock.md colors` rows | Deck color anchors; interpret them with the completed `design_spec.md`, never replace confirmed identity from a second palette |
| Existing `spec_lock.md icons.library` | Sanity check: chosen rendering should be compatible with the icon library's visual weight | | Existing `spec_lock.md icons.library` | Sanity check: chosen rendering should be compatible with the icon library's visual weight |
If rendering inference surfaces multiple candidates, pick the first; do not present another choice after confirmation. If rendering inference surfaces multiple candidates, pick the first; do not present another choice after confirmation.
@@ -114,7 +114,7 @@ Then read the **single resolved** rendering file. It gives you:
- The 80-120 word style paragraph (rendering) - The 80-120 word style paragraph (rendering)
- Two ready-to-paste rendering snippets (fewshot) - Two ready-to-paste rendering snippets (fewshot)
Derive color behavior directly from the available roles: background / secondary background carry roughly 5570% of the image field, primary carries main forms, and accent / secondary accent together usually stay below 10%. A rendering may justify a different balance, but all colors still come from the lock and decorative text colors must remain readable. Derive color behavior from the available roles and image context: background / secondary background usually carry most of the field, primary carries main forms, and accent / secondary accent remain selective. A rendering may justify a different balance and coherent derived tones; decorative text colors must remain readable. Add a new lock role only when that derived color becomes a reusable cross-image semantic token.
### Step 3 — Per-image type + assembly ### Step 3 — Per-image type + assembly
@@ -126,7 +126,7 @@ For each `Acquire Via: ai` row in `design_spec.md §VIII`:
4. `read_file references/image-type-templates/<type>.md` (only if not already read — types are commonly reused across images in one deck) 4. `read_file references/image-type-templates/<type>.md` (only if not already read — types are commonly reused across images in one deck)
5. **Assemble the prompt** by combining: 5. **Assemble the prompt** by combining:
- The rendering's style paragraph (from Step 2) - The rendering's style paragraph (from Step 2)
- Color-role instructions derived directly from the locked deck HEX values (from Step 2) - Color-role instructions anchored by the deck HEX values and refined for the image context (from Step 2)
- The type's structural layout (from Step 3) - The type's structural layout (from Step 3)
- The image's specific `Reference` intent (from `design_spec.md §VIII`) - The image's specific `Reference` intent (from `design_spec.md §VIII`)
- The container sizing guidance from the type file (so the model knows it's painting a local block, not a full canvas) - The container sizing guidance from the type file (so the model knows it's painting a local block, not a full canvas)
@@ -146,7 +146,7 @@ Every assembled prompt follows this paragraph structure. **Write prose, not tag
``` ```
[Rendering style paragraph — 80-120 words from the chosen rendering file]. [Rendering style paragraph — 80-120 words from the chosen rendering file].
[Deck color behavior — apply the locked color roles directly, e.g. "secondary background #F8F9FA provides 60% breathing space, primary #1E3A5F carries main forms, accent #D4AF37 appears in one or two emphasis points only"]. [Deck color behavior — state the core anchors and any context-justified tonal treatment, e.g. "secondary background #F8F9FA provides the breathing field, primary #1E3A5F carries main forms, accent #D4AF37 marks one emphasis; subtle lighter/darker material transitions remain in the same visual family"].
[Type-specific composition — from the chosen type file, e.g. "central hub node with four radiating satellite nodes connected by clean lines"]. [Type-specific composition — from the chosen type file, e.g. "central hub node with four radiating satellite nodes connected by clean lines"].
[Image-specific subject — translated from the row's Reference intent into concrete visual nouns]. [Image-specific subject — translated from the row's Reference intent into concrete visual nouns].
[Container note — "composed as a {W}x{H}px image for {page_role} use"; add composition cues only when the page actually needs them. SVG-overlay-reservation cues ("leave the lower band calm — SVG title overlays it", "keep the right third calmer for SVG text") are valid **only** when `page_role: hero_page` (SVG sits on top of the image). For `page_role: local`, the image sits inside a region block and the SVG layer never overlays its interior — never reserve overlay space in a local prompt]. [Container note — "composed as a {W}x{H}px image for {page_role} use"; add composition cues only when the page actually needs them. SVG-overlay-reservation cues ("leave the lower band calm — SVG title overlays it", "keep the right third calmer for SVG text") are valid **only** when `page_role: hero_page` (SVG sits on top of the image). For `page_role: local`, the image sits inside a region block and the SVG layer never overlays its interior — never reserve overlay space in a local prompt].
@@ -279,7 +279,7 @@ If one deck needs mixed shapes, create separate sheets per shape family unless o
**Resource contract — the sheet and its elements are different row kinds.** A sliced element can only be placed if it exists as a resource the Executor is allowed to reference (`spec_lock.md images`). So §VIII carries two row kinds (planning authority: [`strategist-image.md`](./strategist-image.md)): **Resource contract — the sheet and its elements are different row kinds.** A sliced element can only be placed if it exists as a resource the Executor is allowed to reference (`spec_lock.md images`). So §VIII carries two row kinds (planning authority: [`strategist-image.md`](./strategist-image.md)):
- **Sheet row**`Acquire Via: ai`, `Type: Illustration Sheet`, the intent prompt, named as the slice source with its intended cell shape and placement purpose (`Reference: landscape footer-vignette spot set`). It is generated in Step 5 but **never placed on a slide** — keep it **out of** `spec_lock.md images`. Image_Generator resolves the exact `aspect_ratio`, grid, and slice command from this intent. - **Sheet row**`Acquire Via: ai`, `Type: Illustration Sheet`, the intent prompt, named as the slice source with its intended cell shape and placement purpose (`Reference: landscape footer-vignette spot set`). It is generated in Step 5 but **never placed on a slide** — keep it **out of** `spec_lock.md images`. Image_Generator resolves the exact `aspect_ratio`, grid, and slice command from this intent.
- **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 `spec_lock.md images`, usually with ` | no-crop` (a tight-trimmed transparent spot should be fit, not cover-cropped). Their dimensions are filled in after slicing (Step 5 re-runs `analyze_images.py`). **Set each element row's Layout pattern from the decorative-cutout family, never a boxed container** — see Placement below. - **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 `spec_lock.md images`, normally with `crop=no-crop` (a tight-trimmed transparent spot should be fit, not cover-cropped). Their dimensions are filled in after slicing (Step 5 re-runs `analyze_images.py`). Each row must already carry a Strategist-selected decorative-cutout Layout pattern, never a boxed container — see Placement below.
For traceability, add optional `slice_grid` and `slice_names` fields to the sheet item in `image_prompts.json` after choosing the geometry. `image_gen.py` ignores unknown item fields but preserves them in the manifest, so these fields document the exact command that must be used for slicing. For traceability, add optional `slice_grid` and `slice_names` fields to the sheet item in `image_prompts.json` after choosing the geometry. `image_gen.py` ignores unknown item fields but preserves them in the manifest, so these fields document the exact command that must be used for slicing.
@@ -296,7 +296,7 @@ python3 scripts/slice_images.py <project>/images/illus_sheet.png --grid 2x3 \
2. **Clean grid, or it cuts ugly.** The model will not place every element perfectly; force a clear grid with gutters, and generate **a few sheets** (re-roll the same prompt) to pick the cleanest-laid-out one before slicing. State the exact row/column structure and cell shape so the model does not invent a square matrix. `--trim` absorbs the rest. 2. **Clean grid, or it cuts ugly.** The model will not place every element perfectly; force a clear grid with gutters, and generate **a few sheets** (re-roll the same prompt) to pick the cleanest-laid-out one before slicing. State the exact row/column structure and cell shape so the model does not invent a square matrix. `--trim` absorbs the rest.
3. **Generate only as large as needed.** Each cell is a fraction of the sheet. Pick the smallest sheet size that keeps each sliced cell at least **1.5-2x** the intended display size. `1K` is usually enough for small 80-160px decorative spots; use `2K` for medium 180-320px placements; reserve `4K` for large, cropped, or potentially enlarged elements. 3. **Generate only as large as needed.** Each cell is a fraction of the sheet. Pick the smallest sheet size that keeps each sliced cell at least **1.5-2x** the intended display size. `1K` is usually enough for small 80-160px decorative spots; use `2K` for medium 180-320px placements; reserve `4K` for large, cropped, or potentially enlarged elements.
**Placement — these are decorative accessories, not boxed pictures.** A transparent spot wasted in a centered rectangle looks cheaper than no spot at all. Each element row's Layout pattern comes from the decorative-cutout family in [`image-layout-patterns.md`](./image-layout-patterns.md): `#63` sticker/cutout, `#4` bleed off the canvas edge, `#58` corner fragment, `#66` fade into the background, `#69` slight editorial rotation, `#49` asymmetric cluster. Push spots to the margins, let them run off-edge or sit behind/beside text, vary size and angle across pages, and overlap the content rather than reserving a tidy tile for them. Anchor most pages on one primary element and let the rest stay small ([primary-per-page](./strategist-image.md)) — scattered same-weight tiles are exactly the generic look to avoid. **Placement — these are decorative accessories, not boxed pictures.** Strategist selects each element row's pattern from the decorative-cutout family in [`image-layout-patterns.md`](./image-layout-patterns.md): `#63` sticker/cutout, `#4` bleed off the canvas edge, `#58` corner fragment, `#66` fade into the background, `#69` slight editorial rotation, or `#49` asymmetric cluster. Executor realizes that choice through margin position, off-edge treatment, overlap, scale, and angle rather than reserving a tidy tile. Anchor most pages on one primary element and let the rest stay small ([primary-per-page](./strategist-image.md)).
**Through-line — one family, many roles.** A spot sheet pays off more when the same motif family also drives the cover and section dividers. A large cover / divider anchor is not a giant sheet cell—generate it as its own `hero_page` image sharing the sheet's `deck_rendering`, `color_scheme`, and subject world. Plan this only when the deck leans into illustration, never as a quota. **Through-line — one family, many roles.** A spot sheet pays off more when the same motif family also drives the cover and section dividers. A large cover / divider anchor is not a giant sheet cell—generate it as its own `hero_page` image sharing the sheet's `deck_rendering`, `color_scheme`, and subject world. Plan this only when the deck leans into illustration, never as a quota.
@@ -447,7 +447,7 @@ Write `project/images/image_prompts.json` with this shape:
| Field | Required | Source | Description | | Field | Required | Source | Description |
|---|---|---|---| |---|---|---|---|
| `deck_rendering` | yes | Step 2 lock | Single rendering name shared by all items in this deck | | `deck_rendering` | yes | Step 2 lock | Single rendering name shared by all items in this deck |
| `color_scheme` | yes | `spec_lock.md colors` | Exact deck color roles used by every item; no separate image palette | | `color_scheme` | yes | `spec_lock.md colors` | Core deck color anchors shared by every item; prompts may add contextual tonal behavior, but no separate image palette |
| `items[].filename` | yes | `§VIII` resource list | Output filename with extension | | `items[].filename` | yes | `§VIII` resource list | Output filename with extension |
| `items[].type` | conditional | Step 3 per-image (only when `page_role: local`) | One of 11 internal-composition types: `infographic`, `flowchart`, `framework`, `matrix`, `cycle`, `funnel`, `pyramid`, `comparison`, `timeline`, `map`, `scene`. **Omit `type` entirely when `page_role: hero_page`** — the composition comes from §4.1 primitives written directly into the prompt, not from a type file. | | `items[].type` | conditional | Step 3 per-image (only when `page_role: local`) | One of 11 internal-composition types: `infographic`, `flowchart`, `framework`, `matrix`, `cycle`, `funnel`, `pyramid`, `comparison`, `timeline`, `map`, `scene`. **Omit `type` entirely when `page_role: hero_page`** — the composition comes from §4.1 primitives written directly into the prompt, not from a type file. |
| `items[].page_role` | yes | Step 3 per-image | `local` (default — region block on SVG page) or `hero_page` (image is page's main voice; SVG overlay minimal or empty) | | `items[].page_role` | yes | Step 3 per-image | `local` (default — region block on SVG page) or `hero_page` (image is page's main voice; SVG overlay minimal or empty) |
@@ -486,11 +486,11 @@ C (AI-generated) supports three implementation modes sharing one `image_prompts.
**Selection logic — declared-procedure fallback when no path is confirmed**: the confirmed user choice wins. When neither channel confirmed a specific path — the effective choice is `auto` (explicitly confirmed or defaulted) or absent — use the automatic A → B → C chain: **Selection logic — declared-procedure fallback when no path is confirmed**: the confirmed user choice wins. When neither channel confirmed a specific path — the effective choice is `auto` (explicitly confirmed or defaulted) or absent — use the automatic A → B → C chain:
0. **Confirmed override (wins)** — honor the confirmed image source **from whichever channel the confirmation actually happened in**: when the Confirm UI was used, it records the choice to `<project>/confirm_ui/result.json` as `image_ai_path`; a chat confirmation is equally binding and leaves no `result.json` — read the choice from the conversation. From either channel, if the choice is set and not `auto`, honor it directly, **even when it contradicts `IMAGE_BACKEND`**: 0. **Confirmed override (wins)** — honor `AI Image Acquisition Path` from `design_spec.md §I`. Generate Step 4 already consumed the final confirmation into that durable artifact; do not reopen `result.json` here. If the recorded choice is set and not `auto`, honor it directly, **even when it contradicts `IMAGE_BACKEND`**:
- `api`**Path A** (`image_gen.py --manifest`). - `api`**Path A** (`image_gen.py --manifest`).
- `host-native`**Path B** (host's native image tool) — skip A and do **not** run `image_gen.py --manifest`, *even if `IMAGE_BACKEND` is configured*. - `host-native`**Path B** (host's native image tool) — skip A and do **not** run `image_gen.py --manifest`, *even if `IMAGE_BACKEND` is configured*.
- `manual`**Offline Manual** (write prompts, render the Markdown sidecar, hand off; do **not** run `image_gen.py --manifest`). - `manual`**Offline Manual** (write prompts, render the Markdown sidecar, hand off; do **not** run `image_gen.py --manifest`).
("use Codex's image tool" / "走接口生成" in chat = `host-native` / `api`.) If an explicitly chosen path is unavailable or still fails after its retry, mark the affected row `Needs-Manual`; do not switch to another automated provider. Only when no channel named a specific path (the effective value is `auto` — explicitly confirmed or defaulted — or absent) does the automatic chain decide. If an explicitly chosen path is unavailable or still fails after its retry, mark the affected row `Needs-Manual`; do not switch to another automated provider. Only when the Design Spec records `auto` or no specific path applies does the automatic chain decide. A legacy project missing this Design Spec row returns to Step 4 recovery to consume persisted confirmation once and record it; Image_Generator does not inspect the confirmation channel itself.
1. **Try Path A** — if `IMAGE_BACKEND` is configured (env or `.env`), run `image_gen.py --manifest`. If it fails twice in a row, fall to Path B. 1. **Try Path A** — if `IMAGE_BACKEND` is configured (env or `.env`), run `image_gen.py --manifest`. If it fails twice in a row, fall to Path B.
2. **Try Path B** — if `IMAGE_BACKEND` was not configured (A skipped), or A failed, and the host has a native image tool (Codex / Antigravity / Claude Code / similar), the agent invokes the host's image capability directly. 2. **Try Path B** — if `IMAGE_BACKEND` was not configured (A skipped), or A failed, and the host has a native image tool (Codex / Antigravity / Claude Code / similar), the agent invokes the host's image capability directly.
3. **Fall to C (Offline Manual)** — if B is also unavailable (no host-native tool) or fails, write prompts to `images/image_prompts.json` and hand off to the user. 3. **Fall to C (Offline Manual)** — if B is also unavailable (no host-native tool) or fails, write prompts to `images/image_prompts.json` and hand off to the user.
@@ -638,7 +638,7 @@ Diagnose the failure category, adjust the **one specific dimension** responsible
|---|---|---| |---|---|---|
| Image looks generic, model-average | Tag-soup prompt | Rewrite as one coherent paragraph per §4 | | Image looks generic, model-average | Tag-soup prompt | Rewrite as one coherent paragraph per §4 |
| Wrong style family (looks photorealistic when flat was intended) | Rendering mismatch or rendering paragraph diluted | Reaffirm chosen rendering's style paragraph at the top of the prompt | | Wrong style family (looks photorealistic when flat was intended) | Rendering mismatch or rendering paragraph diluted | Reaffirm chosen rendering's style paragraph at the top of the prompt |
| Colors don't match deck | Locked role HEX not echoed, or their role / proportion instructions were diluted | Repeat the locked HEX values 2-3 times; restate which deck role owns the field, main forms, and sparse accents | | Colors don't match deck | Core role anchors or their semantic/proportion instructions were diluted | Restate which deck roles own the field, main forms, and sparse accents; remove unrelated hues while preserving context-justified tonal transitions |
| Hex code or color name visible as text in image | Missing §5.1 closing sentence | Append the §5.1 hard rule verbatim | | Hex code or color name visible as text in image | Missing §5.1 closing sentence | Append the §5.1 hard rule verbatim |
| Garbled letters in supposedly text-free image | `text_policy: none` rule too weak | Strengthen with explicit list: "no letters, no numbers, no words, no signs, no labels, no captions, no watermarks" | | Garbled letters in supposedly text-free image | `text_policy: none` rule too weak | Strengthen with explicit list: "no letters, no numbers, no words, no signs, no labels, no captions, no watermarks" |
| SVG text overlay clashes with busy image area | Page design needs negative space the prompt didn't request | Add a composition cue like "leave the {center / left third / lower band} relatively calm for text overlay" — only when the page actually overlays text on top of the image | | SVG text overlay clashes with busy image area | Page design needs negative space the prompt didn't request | Add a composition cue like "leave the {center / left third / lower band} relatively calm for text overlay" — only when the page actually overlays text on top of the image |
@@ -657,9 +657,9 @@ Diagnose the failure category, adjust the **one specific dimension** responsible
- Generating prompts for `web` rows — those go through [`image-searcher.md`](./image-searcher.md) - Generating prompts for `web` rows — those go through [`image-searcher.md`](./image-searcher.md)
- Brand names or HEX codes inside the subject description (degrades output) - Brand names or HEX codes inside the subject description (degrades output)
- Mixing renderings or inventing image-only colors across images in the same deck - Mixing renderings or introducing an unrelated image-only palette across images in the same deck
- Tag-soup prompts (keyword lists separated by commas without a coherent visual scene) - Tag-soup prompts (keyword lists separated by commas without a coherent visual scene)
- Globbing `image-renderings/*.md` or any subdirectory — read only the chosen file - Globbing `image-renderings/*.md` or any subdirectory — read only the chosen preset or exact custom-reference files
- Placing an image without updating its `image_prompts.json` `status` and the resource list status - Placing an image without updating its `image_prompts.json` `status` and the resource list status
- Switching rendering or deck-color roles for a single image—`hero_page` is not an exception to deck-wide coherence - Switching rendering or core deck-color semantics for a single image—`hero_page` is not an exception to deck-wide coherence
- Embedding body copy, data points, bullet lists, or long quotes inside an image — those route to SVG - Embedding body copy, data points, bullet lists, or long quotes inside an image — those route to SVG
@@ -2,33 +2,33 @@
# Image Layout Specification # Image Layout Specification
Layout rules for pages where the image is placed **side-by-side with body text** as a container block. Strategist and Executor both follow these rules when the image's narrative intent is *side-by-side*. Sizing reference for side-by-side or multi-image pages. Use only after Strategist selects the composition; this file never selects layout or crop policy.
**Core principle (side-by-side)**: compute container layout from the image's original aspect ratio so the image displays completely — no excess whitespace, no cropping. **Selected pattern, flexible geometry**: Let original aspect ratio inform the container. A `no-crop` asset displays completely; an `adaptive` asset may use `meet` or a focal-safe `slice`. Rework geometry within the selected pattern when either mode produces weak hierarchy, unsafe cropping, or excessive dead space; changing the pattern requires an upstream Design Spec update.
> **Scope**: this spec applies to *side-by-side* intent only. Other intents (hero / full-bleed, atmosphere / background, accent / inline) use full-bleed placement where ratio alignment is not a constraint and cropping is expected — the ratio→split table below does NOT apply. See `references/strategist-image.md` for intent selection. > **Scope**: The ratio tables and formulas are calculation aids for a selected side-by-side or multi-image plan. Hero, background, accent, and other compositions stay outside this file. Layout never overrides the `no-crop` boundary owned by [`strategist-image.md`](./strategist-image.md) and [`executor-image.md`](./executor-image.md).
--- ---
## Layout Decision Flow ## Layout Decision Flow
``` ```
1. Decide narrative intent (hero / atmosphere / side-by-side / accent) — see strategist-image.md 1. Read the selected narrative intent, hierarchy, and primary/modifier ids from Strategist's plan.
2. If intent = side-by-side: continue below. Otherwise: compose per narrative; this spec does not apply. 2. If the selected pattern is not side-by-side or multi-image, this spec does not apply.
3. Get image original dimensions → Calculate ratio (width/height) 3. Read the asset's `no-crop` boundary and original dimensions; calculate ratio (width/height).
4. Select layout type based on ratio 4. Use the tables as candidate structures, not an automatic selector.
5. Calculate maximum display size for the image 5. Calculate the image/text rectangles, then choose `meet` or focal-safe `slice` within the crop boundary.
6. Allocate remaining space for text area 6. Revise geometry within the selected ids when the result weakens hierarchy, legibility, or required image content.
7. Fill results into the Design Specification's image resource list 7. Return upstream for a different pattern, resource, role, or crop boundary; Executor never rewrites selection.
``` ```
**When to run**: if image approach includes "B) User-provided", run the scan and populate the image resource list after the Strategist's confirmation stage and before content analysis / outlining. **When to run**: after `analyze_images.py` has produced current dimensions and a side-by-side or multi-image composition is under consideration. Skip this sizing reference for other page structures.
--- ---
## Layout Type Selection (side-by-side intent) ## Layout Starting Points (side-by-side intent)
| Image Ratio | Layout Type | Image Position | Description | | Image Ratio | Useful Starting Structure | Image Position | Description |
|-------------|-------------|----------------|-------------| |-------------|-------------|----------------|-------------|
| > 2.0 (ultra-wide) | Top-bottom split | Top full-width | Image spans canvas width, height proportional | | > 2.0 (ultra-wide) | Top-bottom split | Top full-width | Image spans canvas width, height proportional |
| 1.5-2.0 (wide) | Top-bottom split | Top | Image width = content area width, height proportional | | 1.5-2.0 (wide) | Top-bottom split | Top | Image width = content area width, height proportional |
@@ -36,7 +36,7 @@ Layout rules for pages where the image is placed **side-by-side with body text**
| 0.8-1.2 (square) | Left-right split | Left | Image takes content area height, width proportional | | 0.8-1.2 (square) | Left-right split | Left | Image takes content area height, width proportional |
| < 0.8 (portrait) | Left-right split | Left | Image height = content area height, width proportional | | < 0.8 (portrait) | Left-right split | Left | Image height = content area height, width proportional |
> Boundary ratio (e.g., 1.5): decide by text volume — more text → left-right; less text → top-bottom. > Boundary ratios are orientation cues, not thresholds. Let text volume, focal content, page hierarchy, and crop safety decide.
--- ---
@@ -62,8 +62,9 @@ Image width = W = 1160 px
Image height = W / R = 1160 / R px Image height = W / R = 1160 / R px
Text area height = H - image height - gap(20px) Text area height = H - image height - gap(20px)
Validation: Text area height >= 150px (at least 3-4 lines of text) Review: if the remaining text area cannot carry the planned copy legibly,
If not satisfied → Switch to left-right layout rebalance the rectangles within the selected pattern; otherwise return upstream
for a Design Spec pattern update.
``` ```
### Left-Right Layout Calculation ### Left-Right Layout Calculation
@@ -82,7 +83,7 @@ Image height = image width / R
Text area width = W - image width - gap(20px) Text area width = W - image width - gap(20px)
``` ```
**Validation**: Text area width >= 280px; otherwise reduce image area width. **Review**: if the remaining text area cannot carry the planned copy legibly, rebalance the image/text rectangles within the selected pattern; otherwise return upstream for a Design Spec pattern update.
--- ---
@@ -106,8 +107,8 @@ Image: 773x560 (left), Text area: 367x560 (right) → 7:3 left-right
``` ```
Original: 1820x1040, R=1.75 Original: 1820x1040, R=1.75
Try top-bottom: image height=663, text area=-43 ❌ Strategist compares top-bottom: image height=663, text area=-43 ❌
Switch to left-right: image 780x446 (left), text area 360x600 (right) → 7:3 left-right Strategist selects left-right: image 780x446 (left), text area 360x600 (right) → 7:3 left-right
``` ```
--- ---
@@ -116,7 +117,7 @@ Switch to left-right: image 780x446 (left), text area 360x600 (right) → 7:3 le
Default selection table assumes **landscape or square canvas**. For portrait canvases (height > width), left-right splits leave both columns too narrow — use the override below. Default selection table assumes **landscape or square canvas**. For portrait canvases (height > width), left-right splits leave both columns too narrow — use the override below.
| Canvas Orientation | Image Ratio | Recommended Layout | Reason | | Canvas Orientation | Image Ratio | Useful Starting Structure | Reason |
|-------------------|-------------|-------------------|--------| |-------------------|-------------|-------------------|--------|
| Portrait (Xiaohongshu, Story) | > 1.5 (wide) | Top-bottom | Same as landscape canvas | | Portrait (Xiaohongshu, Story) | > 1.5 (wide) | Top-bottom | Same as landscape canvas |
| Portrait (Xiaohongshu, Story) | 1.2-1.5 (standard) | Top-bottom | Left-right too narrow on tall canvas | | Portrait (Xiaohongshu, Story) | 1.2-1.5 (standard) | Top-bottom | Left-right too narrow on tall canvas |
@@ -165,20 +166,18 @@ Image positions:
(60, 390) 570x290 (650, 390) 570x290 (60, 390) 570x290 (650, 390) 570x290
``` ```
> Multi-image slides: use `preserveAspectRatio="xMidYMid meet"` on all images for consistent in-cell display. > Multi-image slides: decide `meet` or focal-safe `slice` per asset. Keep `no-crop` images complete; do not force every image into the same scaling mode merely for grid uniformity.
--- ---
## Prohibited Practices ## Composition Checks
| Prohibited | Correct Approach | | Check | Action |
|-----------|-----------------| |-----------|-----------------|
| Fixed 50:50 or arbitrary ratios | Dynamic calculation based on image ratio | | Proportion does not reflect information weight | Rebalance image and text rectangles |
| Forcing wide image into square container | Use top-bottom layout or increase image area width | | Container conflicts with the native ratio | Change the container, choose `meet`, or use a focal-safe crop |
| Placing portrait image in narrow horizontal strip | Use left-right layout, image on left | | Required pixels, labels, identity, or evidence would be cropped | Use `preserveAspectRatio="xMidYMid meet"` and recompose around the complete image |
| Image whitespace exceeding 10% | Recalculate layout or choose alternative approach | | Text area cannot carry the planned copy legibly | Increase its area within the selected pattern; otherwise return upstream |
| Cropping key image content | Use `preserveAspectRatio="xMidYMid meet"` |
| Text area too small to read | Ensure text area >= 150px (top-bottom) or >= 280px (left-right) |
--- ---
@@ -189,15 +188,16 @@ This spec only defines layout calculation. Write computed fields into the Image
| Field | Meaning | | Field | Meaning |
|-------|---------| |-------|---------|
| `Ratio` | Original image width / height | | `Ratio` | Original image width / height |
| `Layout plan` | Top-bottom / left-right / grid, including split ratio when relevant | | `Layout pattern` | Strategist-selected catalog pattern; semantic composition fixed, geometry flexible |
| `Image area` | Computed display rectangle size | | `Crop Policy` | `no-crop` protects complete pixels; `adaptive` lets Executor choose `meet` or focal-safe `slice` |
| `Text area` | Computed remaining text area size | | `Reference` | Optional calculated image/text rectangles, focal notes, and composition intent |
| `spec_lock.md images` value | `<path> | source=<Acquire Via> | pattern=<Layout pattern> | crop=<adaptive|no-crop>` |
For SVG `<image>` syntax, path rules, `preserveAspectRatio`, external refs, and Base64 embedding: see [`svg-image-embedding.md`](svg-image-embedding.md). For SVG `<image>` syntax, path rules, `preserveAspectRatio`, external refs, and Base64 embedding: see [`svg-image-embedding.md`](svg-image-embedding.md).
### SVG Image Embedding Examples ### SVG Image Embedding Examples
Complete display (data charts, side-by-side — must not crop): Complete display (`no-crop` assets such as data charts):
```xml ```xml
<image href="../images/xxx.png" <image href="../images/xxx.png"
@@ -205,7 +205,7 @@ Complete display (data charts, side-by-side — must not crop):
preserveAspectRatio="xMidYMid meet"/> preserveAspectRatio="xMidYMid meet"/>
``` ```
Crop-to-fill (backgrounds and hero images only): Crop-to-fill (an `adaptive` asset with a verified focal-safe crop):
```xml ```xml
<image href="../images/bg.png" <image href="../images/bg.png"
@@ -223,7 +223,7 @@ python3 scripts/analyze_images.py <project_path>/images --canvas ppt43 # PPT
python3 scripts/analyze_images.py <project_path>/images --canvas xiaohongshu # Xiaohongshu python3 scripts/analyze_images.py <project_path>/images --canvas xiaohongshu # Xiaohongshu
``` ```
`--canvas` selects target format (default `ppt169`). The tool computes layout type (top-bottom / left-right), image display area, and text area per the formulas above. Output is a Markdown table — paste directly into the image resource list. `--canvas` selects target format (default `ppt169`). The tool computes a top-bottom / left-right candidate, image display area, and text area from the formulas above. Treat its output as planning input; record the composition actually selected for the page.
--- ---
@@ -231,5 +231,5 @@ python3 scripts/analyze_images.py <project_path>/images --canvas xiaohongshu #
| Role | Responsibility | | Role | Responsibility |
|------|---------------| |------|---------------|
| **Strategist** | Run analyze_images.py, calculate layout per this spec, populate image resource list | | **Strategist** | Run `analyze_images.py`, select the catalog pattern/resources, and record the crop boundary |
| **Executor** | Strictly follow the layout plan and dimensions in the image resource list when generating SVGs | | **Executor** | Realize the selected pattern for the actual asset/page while preserving its ids, role, source, must-use, and `no-crop` constraints |
@@ -8,9 +8,9 @@ Compatibility tombstone for retired `image_palette` fields and historical palett
| Input | Current behavior | | Input | Current behavior |
|---|---| |---|---|
| `spec_lock.md colors` | Sole source of generated-image color roles and exact HEX values | | `spec_lock.md colors` | Core recurring deck-color anchors; interpret them with the completed Design Spec and image context, and allow coherent tonal/material/lighting derivatives without creating another palette |
| `image_rendering` | Controls rendering treatment only; it does not create a second color decision | | `image_rendering` | Controls rendering treatment only; it does not create a second color decision |
| Legacy `image_palette` row | Ignore it; it cannot override the deck color lock | | Legacy `image_palette` row | Ignore it; it cannot override the confirmed deck identity or current contextual color judgment |
| No palette row | Expected; do not synthesize a preset or `custom` fallback | | No palette row | Expected; do not synthesize a preset or `custom` fallback |
**Forbidden — legacy activation**: **Forbidden — legacy activation**:
@@ -2,15 +2,15 @@
A **rendering** is a visual style family: line quality, texture, depth, material, mood. Lock one rendering per deck — every AI image in the deck shares it. A **rendering** is a visual style family: line quality, texture, depth, material, mood. Lock one rendering per deck — every AI image in the deck shares it.
> **HEX values are not in renderings.** Rendering describes how the image is drawn. The new flow reads exact deck color roles directly from `spec_lock.md colors`; it does not ask for or author a separate image palette. See [`image-generator.md`](../image-generator.md) §2. > **HEX values are not in renderings.** Rendering describes how the image is drawn. The new flow starts from the deck's core color anchors in `spec_lock.md colors` and interprets them with the Design Spec/image context; it does not ask for or author a separate image palette. See [`image-generator.md`](../image-generator.md) §2.
> **Deck HEX has hard precedence.** Any color name or sample HEX inside an individual rendering file is illustrative legacy prose and MUST be replaced by the current deck-role values when assembling a prompt. A rendering may change texture, lighting, opacity, and role proportions, but it may not tint, warm-grade, cool-grade, replace, or invent HEX. If its material language requires colors the selected deck roles cannot support, do not offer that rendering in Stage 2. > **Core deck identity has precedence.** Any sample HEX inside an individual rendering file is illustrative legacy prose. Prompt assembly replaces identity roles with the current deck anchors, then may derive coherent tints, light/shadow transitions, material colors, and atmospheric hues from the rendering and image context. Do not replace the deck identity with an unrelated palette. When one derived tone becomes a reusable semantic role across images/pages, promote it to a named lock row.
--- ---
## 1. Catalog (20 renderings) ## 1. Catalog (20 renderings)
Each rendering has its own file with: style paragraph, line / texture / depth notes, deck HEX usage, and a fewshot prompt snippet. **Read only the file for the rendering you pick** — never glob the directory. Each rendering has its own file with: style paragraph, line / texture / depth notes, deck HEX usage, and a fewshot prompt snippet. A preset lock reads that one file. A catalog-based `custom` reads every preset named in `image_rendering_references`; a novel `custom` may omit references. Never glob the directory.
### 1.1 Modern / commercial (the corporate-PPT main field) ### 1.1 Modern / commercial (the corporate-PPT main field)
@@ -62,7 +62,7 @@ Whenever proposed image usage includes `ai`, Stage 2 authors one separate, visib
|---|---| |---|---|
| Length | One paragraph, 2-5 sentences | | Length | One paragraph, 2-5 sentences |
| Axes covered | line / texture / depth / material / mood (same as preset files) | | Axes covered | line / texture / depth / material / mood (same as preset files) |
| Forbidden | Naming a competing preset ("like blueprint but warmer") | | Catalog basis | When existing renderings are combined or borrowed, name every exact id and read every named file before synthesis |
```yaml ```yaml
- image_rendering: custom - image_rendering: custom
@@ -71,6 +71,8 @@ Whenever proposed image usage includes `ai`, Stage 2 authors one separate, visib
**Hard rule**: the custom candidate is mandatory when AI images are proposed; selecting `custom` is a tail-case, not the default. See [`strategist-image.md`](../strategist-image.md) for the Stage-2 carrier and downstream lock behavior. **Hard rule**: the custom candidate is mandatory when AI images are proposed; selecting `custom` is a tail-case, not the default. See [`strategist-image.md`](../strategist-image.md) for the Stage-2 carrier and downstream lock behavior.
Write `image_rendering_references` only when the custom direction actually uses catalog material. Keep the list exact: a blend of `screen-print` and `watercolor` reads both files and lists both ids. A genuinely new rendering with no catalog source omits the field and proceeds from its standalone behavior; never invent a reference merely to legitimize `custom`.
--- ---
## 2. Auto-selection table — `design_spec` → rendering ## 2. Auto-selection table — `design_spec` → rendering
@@ -105,6 +107,6 @@ Match `design_spec.md d` (mode + `visual_style`) against this table. First match
1. From `design_spec.md` extract `d. Style` mode + descriptor. 1. From `design_spec.md` extract `d. Style` mode + descriptor.
2. Find the matching row above; pick the primary recommendation. 2. Find the matching row above; pick the primary recommendation.
3. `read_file image-renderings/<chosen>.md` and apply its style paragraph when assembling each prompt per [`image-generator.md`](../image-generator.md) §4. (For `custom`, this step is replaced by the consumption branch in [`image-generator.md`](../image-generator.md) Step 2 — no preset file to read.) 3. For a preset, read `image-renderings/<chosen>.md`. For `custom`, read every file named in `image_rendering_references`, then synthesize them under the confirmed behavior; with no references, use the novel behavior directly. Apply the result when assembling prompts per [`image-generator.md`](../image-generator.md) §4.
**Lock for the whole deck.** Don't change rendering between images in the same deck. **Lock for the whole deck.** Don't change rendering between images in the same deck.
@@ -4,7 +4,7 @@ Ghibli/Disney-inspired hand-drawn animation warmth. Soft painterly forms, gentle
## 1. Style paragraph (paste-ready, 110 words) ## 1. Style paragraph (paste-ready, 110 words)
> Hand-drawn animation style inspired by Ghibli and classic Disney storybook aesthetics. Forms are softly rendered with painterly fills and gentle outlines — never harsh. Exact colors come only from the deck's locked roles; dreamy warmth comes from soft window light, gentle glow, morning haze, brush texture, and atmospheric perspective rather than invented pastel or earth-tone HEX. Characters (when present) are stylized in a friendly cartoon manner — large expressive eyes, simplified anatomy, warm body language. Foreground forms are richer and the background softer. Overall feel is dreamy, magical, comforting, and storybook-like. > Hand-drawn animation style inspired by Ghibli and classic Disney storybook aesthetics. Forms are softly rendered with painterly fills and gentle outlines — never harsh. Core colors are anchored by the deck roles; dreamy warmth may derive through soft window light, gentle glow, morning haze, brush texture, atmospheric perspective, and coherent pastel/earth-tone transitions. Characters (when present) are stylized in a friendly cartoon manner — large expressive eyes, simplified anatomy, warm body language. Foreground forms are richer and the background softer. Overall feel is dreamy, magical, comforting, and storybook-like.
--- ---
@@ -20,13 +20,13 @@ Ghibli/Disney-inspired hand-drawn animation warmth. Soft painterly forms, gentle
## 3. Using the deck's HEX values ## 3. Using the deck's HEX values
fantasy-animation uses the exact deck roles with a storybook treatment: fantasy-animation uses the deck roles as identity anchors with a storybook treatment:
- Primary HEX: dominant environment or character color, unchanged - Primary HEX: anchors the dominant environment or character family
- Secondary HEX: atmospheric field or haze, unchanged - Secondary HEX: anchors the atmospheric field or haze family
- Accent HEX: a small magical focal point — perhaps a window, flower, or key character element, unchanged - Accent HEX: anchors a small magical focal point — perhaps a window, flower, or key character element
If those roles cannot support the intended storybook mood without recoloring, choose another rendering in Stage 2. Derive light, haze, and material tones coherently from those anchors; choose another rendering only when the required mood would replace the deck identity.
--- ---
@@ -34,4 +34,4 @@ If those roles cannot support the intended storybook mood without recoloring, ch
**Snippet A — half-page storybook scene, text_policy: none** **Snippet A — half-page storybook scene, text_policy: none**
> Hand-drawn storybook animation. A small cottage sits on a hillside with a winding path leading to the foreground. The sky and haze use the deck's locked secondary-background role, the hillside and tree use the locked primary, and one small glowing cottage window uses the locked accent. Do not tint or replace those values. Painterly brush texture, soft light, and atmospheric perspective create the warmth: distant hills are softer, while foreground forms carry more visual weight. A simplified storybook tree stands at foreground left. Composed as a 600×800 half-page block with 10% inner padding. Any figures are gentle cartoon silhouettes with no realistic faces. No text or labels. > Hand-drawn storybook animation. A small cottage sits on a hillside with a winding path leading to the foreground. The sky and haze derive from the deck's secondary-background family, the hillside and tree from primary, and one small glowing cottage window from accent. Coherent lighter/darker atmospheric transitions are allowed; do not replace these families with an unrelated storybook palette. Painterly brush texture, soft light, and atmospheric perspective create the warmth: distant hills are softer, while foreground forms carry more visual weight. A simplified storybook tree stands at foreground left. Composed as a 600×800 half-page block with 10% inner padding. Any figures are gentle cartoon silhouettes with no realistic faces. No text or labels.
@@ -4,7 +4,7 @@ Pure white paper, black ink, sparse semantic color accents — the Mike Rohde sk
## 1. Style paragraph (paste-ready, 105 words) ## 1. Style paragraph (paste-ready, 105 words)
> Professional hand-drawn visual-note style on a clean paper field. All line work uses the deck's body-text color with slight wobble — confident, intentional, with the human-hand quality of a thoughtful whiteboard session. Hand-lettered titles appear bold and slightly oversized (when text policy allows). Color is intentionally sparse: line work dominates ~85% of the visible content, while one or two semantic accents drawn only from the deck's locked accent roles cover less than 10% combined. Backgrounds and shape fills remain mostly empty. Small doodle decorations — stars, dashes, dots — are minimal. Overall feel is professional, considered, manifesto-quality. > Professional hand-drawn visual-note style on a clean paper field. All line work is anchored by the deck's body-text color with slight wobble — confident, intentional, with the human-hand quality of a thoughtful whiteboard session. Hand-lettered titles appear bold and slightly oversized (when text policy allows). Color is intentionally sparse: line work dominates ~85% of the visible content, while one or two semantic accents derive from the deck's accent families and cover less than 10% combined. Backgrounds and shape fills remain mostly empty. Small doodle decorations — stars, dashes, dots — are minimal. Overall feel is professional, considered, manifesto-quality.
--- ---
@@ -24,9 +24,9 @@ ink-notes has a near-fixed material language: **dark ink + light background + 1-
- Background: use the deck's `background` / `secondary_bg` - Background: use the deck's `background` / `secondary_bg`
- Lines and text: use the deck's `body_text` - Lines and text: use the deck's `body_text`
- Semantic accents: use the deck's `accent` / `secondary_accent` roles and their established meaning; do not add traditional ink-notes colors outside the lock - Semantic accents: derive from the deck's `accent` / `secondary_accent` roles and preserve their established meaning; contextual ink/paper tonal variants are allowed
This makes ink-notes the rendering most likely to fight a deck's HEX. Offer it only when the locked background / text / accent roles can support the treatment; never invent traditional coral / teal / lavender after confirmation. This makes ink-notes the rendering most likely to fight a deck's identity. Offer it only when the background / text / accent anchors can support the treatment; do not substitute a stock coral / teal / lavender palette merely because the rendering traditionally uses one.
--- ---
@@ -4,7 +4,7 @@ Warm cream paper with black hand-drawn lines and soft pastel color blocks. The m
## 1. Style paragraph (paste-ready, 110 words) ## 1. Style paragraph (paste-ready, 110 words)
> Warm hand-drawn sketchnote style on the deck's light background role. All lines use the locked body-text color with deliberate slight wobble, never perfectly straight, giving the human-hand quality of a thoughtful teacher's whiteboard. Soft color blocks derive only from the locked primary, secondary-accent, and accent roles and fill rounded shapes without quite reaching their outlines (a deliberate "hand-painted overshoot" feel). Simple cartoon icons and small doodle decorations (stars, sparkles, dots, underlines) appear sparingly to add warmth. Composition is airy and well organized, with generous whitespace. Overall feel is instructional, friendly, and approachable. > Warm hand-drawn sketchnote style anchored by the deck's light background and body-text roles. Lines retain the body-text character with deliberate slight wobble, never perfectly straight, giving the human-hand quality of a thoughtful teacher's whiteboard. Soft color blocks derive from the primary, secondary-accent, and accent families, allowing restrained pastel tints and paper/material transitions that stay coherent with those anchors. Simple cartoon icons and small doodle decorations (stars, sparkles, dots, underlines) appear sparingly to add warmth. Composition is airy and well organized, with generous whitespace. Overall feel is instructional, friendly, and approachable.
--- ---
@@ -20,11 +20,11 @@ Warm cream paper with black hand-drawn lines and soft pastel color blocks. The m
## 3. Using the deck's HEX values ## 3. Using the deck's HEX values
sketch-notes has a strong built-in tendency toward warm paper, dark ink, and soft color blocks. Offer it only when the locked deck roles can carry that tendency; otherwise choose another rendering in Stage 2. Once confirmed, use only the locked deck colors. sketch-notes has a strong built-in tendency toward warm paper, dark ink, and soft color blocks. Use the deck roles as identity anchors, then derive the paper warmth and pastel blocks contextually without creating an unrelated palette.
- Paper background: use the deck's `background` or `secondary_bg`; do not introduce a natural cream outside the lock - Paper background: begin with `background` or `secondary_bg`; a subtle contextual paper tint is allowed when it preserves the deck's identity and contrast
- Ink lines: use the deck's `body_text` role - Ink lines: use the deck's `body_text` role
- Color blocks: use the deck's primary / secondary-accent / accent roles with restrained coverage; do not invent pastel HEX - Color blocks: derive restrained pastel tints from the primary / secondary-accent / accent families
- Single emphasis accent: the deck's accent HEX, used in 1-2 strong sparing places (a key arrow, an emphasized doodle) - Single emphasis accent: the deck's accent HEX, used in 1-2 strong sparing places (a key arrow, an emphasized doodle)
--- ---
@@ -33,4 +33,4 @@ sketch-notes has a strong built-in tendency toward warm paper, dark ink, and sof
**Snippet A — half-page educational concept, text_policy: embedded** **Snippet A — half-page educational concept, text_policy: embedded**
> Warm hand-drawn sketchnote on the deck's locked background color. Lines use the locked body-text color with slight wobble and define three rounded rectangle info boxes arranged in a soft triangle. The top box uses the locked primary, the bottom-left uses the locked secondary accent, and the bottom-right uses the locked accent, each with restrained coverage and no invented tint. Color fills do not completely reach their outlines (slight hand-painted overshoot). Hand-drawn wavy arrows connect the boxes, each with a brief inline keyword such as "leads to", "becomes", or "supports" (≤2 words). Each box contains one simple hand-drawn cartoon icon — a lightbulb, a plant, a gear. Small doodle decorations appear sparingly. Composed as a 600×600 half-page block with 14% inner padding and generous whitespace. > Warm hand-drawn sketchnote anchored by the deck background. Lines use the body-text family with slight wobble and define three rounded rectangle info boxes arranged in a soft triangle. The top box derives from primary, the bottom-left from secondary accent, and the bottom-right from accent; each uses a restrained pastel tint coherent with its anchor. Color fills do not completely reach their outlines (slight hand-painted overshoot). Hand-drawn wavy arrows connect the boxes, each with a brief inline keyword such as "leads to", "becomes", or "supports" (≤2 words). Each box contains one simple hand-drawn cartoon icon — a lightbulb, a plant, a gear. Small doodle decorations appear sparingly. Composed as a 600×600 half-page block with 14% inner padding and generous whitespace.
@@ -4,7 +4,7 @@ Golden-hour cinematic warmth — illustrated scenes with intentional warm lighti
## 1. Style paragraph (paste-ready, 100 words) ## 1. Style paragraph (paste-ready, 100 words)
> Atmospheric scene illustration with golden-hour cinematic lighting. Forms are softly rendered — recognizable but not photorealistic, with soft edges and intentional light direction. The scene has a clear primary light source (low sun, lamplight, window light) casting long soft shadows. Exact colors come only from the deck's locked roles; emotional warmth comes from light placement, bloom, contrast, and atmosphere rather than hue substitution. Composition follows narrative principles — foreground subject, middle-ground context, atmospheric background. Overall feel is cinematic, contemplative, and emotionally warm, well suited to brand story and personal narrative content. > Atmospheric scene illustration with golden-hour cinematic lighting. Forms are softly rendered — recognizable but not photorealistic, with soft edges and intentional light direction. The scene has a clear primary light source (low sun, lamplight, window light) casting long soft shadows. Core colors are anchored by the deck roles; emotional warmth may derive through light placement, bloom, contrast, atmospheric haze, and coherent warm transitions without replacing the deck identity. Composition follows narrative principles — foreground subject, middle-ground context, atmospheric background. Overall feel is cinematic, contemplative, and emotionally warm, well suited to brand story and personal narrative content.
--- ---
@@ -20,13 +20,13 @@ Golden-hour cinematic warmth — illustrated scenes with intentional warm lighti
## 3. Using the deck's HEX values ## 3. Using the deck's HEX values
warm-scene uses the deck's exact HEX roles and creates warmth through light, not recoloring: warm-scene uses the deck's HEX roles as identity anchors and creates warmth through light and contextual tonal transitions:
- Primary HEX: dominant tone in shadows and middle ground, unchanged - Primary HEX: anchors shadows and middle-ground color family
- Secondary HEX: lit field or atmospheric separation, unchanged - Secondary HEX: anchors the lit field or atmospheric separation
- Accent HEX: a small concentrated zone — sun bloom, lamp glow, or key reflection, unchanged - Accent HEX: anchors a small concentrated zone — sun bloom, lamp glow, or key reflection
If the locked roles cannot support an emotionally warm scene without hue shifts, do not offer `warm-scene` in Stage 2. Never warm-grade a cool primary after confirmation. Derive warm light and haze without replacing those core families. Do not apply a generic orange grade that erases a deliberately cool brand identity; choose another rendering when that replacement would be necessary.
--- ---
@@ -34,4 +34,4 @@ If the locked roles cannot support an emotionally warm scene without hue shifts,
**Snippet A — half-page personal story, text_policy: none** **Snippet A — half-page personal story, text_policy: none**
> Atmospheric scene illustration with golden-hour cinematic lighting. A softly rendered figure (simplified silhouette, no detailed face) walks along a path — foreground left, a small distant cabin in the middle ground, soft hills in the atmospheric background. Strong low-angle light comes from the upper right and casts long soft shadows. The sky uses the deck's locked secondary-background role, main forms use the locked primary, and a small sun bloom uses the locked accent; none is hue-shifted. No hard outlines — forms emerge from light and shadow. Add subtle bloom and film grain at 8% opacity. Composed as a 600×800 half-page block with 10% inner padding. Simplified silhouette figures only; no realistic faces, text, or labels. > Atmospheric scene illustration with golden-hour cinematic lighting. A softly rendered figure (simplified silhouette, no detailed face) walks along a path — foreground left, a small distant cabin in the middle ground, soft hills in the atmospheric background. Strong low-angle light comes from the upper right and casts long soft shadows. The sky derives from the deck's secondary-background family, main forms from primary, and a small sun bloom from accent; coherent light/shadow transitions support the atmosphere without replacing those anchors. No hard outlines — forms emerge from light and shadow. Add subtle bloom and film grain at 8% opacity. Composed as a 600×800 half-page block with 10% inner padding. Simplified silhouette figures only; no realistic faces, text, or labels.
@@ -8,7 +8,7 @@ A **mode** is the deck's **narrative + persuasion skeleton** — how the argumen
## 1. Catalog (5 modes) ## 1. Catalog (5 modes)
Each mode has its own file with: narrative skeleton, page-structure tendencies, speaker-notes register, and a page skeleton example. **Read only the file for the mode you lock** — never glob the directory. Each mode has its own file with: narrative skeleton, page-structure tendencies, speaker-notes register, and a page skeleton example. A preset lock reads that one file. A catalog-based `custom` reads every preset named in `mode_references`; a novel `custom` may omit references. Never glob the directory.
| Mode | Narrative skeleton | Best for | | Mode | Narrative skeleton | Best for |
|---|---|---| |---|---|---|
@@ -20,7 +20,7 @@ Each mode has its own file with: narrative skeleton, page-structure tendencies,
> The five are **argument strategies, not a taxonomy of communication purposes**. A presentation may inform + align + request a decision at once; that composite intent stays as open prose in the Stage-1 communication contract. Stage 2 chooses the mode that best carries the dominant body-page spine, or one concrete `custom` act sequence when no preset can serve the stated priority / sequence. > The five are **argument strategies, not a taxonomy of communication purposes**. A presentation may inform + align + request a decision at once; that composite intent stays as open prose in the Stage-1 communication contract. Stage 2 chooses the mode that best carries the dominant body-page spine, or one concrete `custom` act sequence when no preset can serve the stated priority / sequence.
> >
> **A mode is a lens, not a mandate over the user's own structure.** When the user brings their own outline, it is authoritative: transcribe it into `design_spec.md §IX` as given — page order and titles preserved — and let the mode govern only voice / register and page-internal treatment. A mode never reorders a user's pages or rewrites their given titles (mode is Reference-strength; a user-authored outline is exactly the override). When the user gives no structure, the mode does the structural lifting. To lay an outline out with the least reshaping, `briefing` imposes the lightest skeleton. > **A mode is a lens, not a mandate over an explicitly preserved structure.** Apply the confirmed `content_divergence` to a user-supplied outline. An ordinary source outline is a Reference that the mode may regroup, reorder, or retitle while preserving its facts and intended relationships. Preserve page order, titles, or wording only when the user presents the outline as the final page plan or explicitly requests that boundary. When the user gives no structure, the mode does the structural lifting. To keep reshaping light, `briefing` imposes the least skeleton.
--- ---
@@ -54,7 +54,7 @@ Each mode has its own file with: narrative skeleton, page-structure tendencies,
1. Strategist reads this index at confirmation `d. Layer 1`. 1. Strategist reads this index at confirmation `d. Layer 1`.
2. Preselect one mode from the auto-selection table + the confirmed communication contract and source structure; separately author the visible AI custom candidate required by §4. 2. Preselect one mode from the auto-selection table + the confirmed communication contract and source structure; separately author the visible AI custom candidate required by §4.
3. Record the confirmed mode and rationale in `design_spec.md`, then project `- mode: <name>` into `spec_lock.md`. 3. Record the confirmed mode and rationale in `design_spec.md`, then project `- mode: <name>` into `spec_lock.md`.
4. Executor reads **only** `modes/<locked-mode>.md` at generation entry — never globs this directory. 4. Executor reads `modes/<locked-mode>.md` for a preset. For `custom`, it reads every file listed in `mode_references` before applying `mode_behavior`; with no references, it applies the novel behavior directly. Never glob this directory.
**Lock scope**: deck-wide (one mode per deck). The five are the catalog you select from; if the structure is genuinely mixed, pick the mode of the body pages and let pages vary within it, or recommend a `custom` blend (§4). Recommend the best fit; the user confirms. **Lock scope**: deck-wide (one mode per deck). The five are the catalog you select from; if the structure is genuinely mixed, pick the mode of the body pages and let pages vary within it, or recommend a `custom` blend (§4). Recommend the best fit; the user confirms.
@@ -64,7 +64,9 @@ Each mode has its own file with: narrative skeleton, page-structure tendencies,
`custom` holds **any bespoke narrative direction the five don't give as-is** — and what *kind* of thing it is doesn't matter. It might be a nameable cadence (dialectic 正反合, myth-vs-reality, countdown / Top-N, Socratic), a deliberate multi-act fusion of several modes, or the user's own feel for how the deck should carry (confrontational here, detached there). Don't try to taxonomize it. `custom` holds **any bespoke narrative direction the five don't give as-is** — and what *kind* of thing it is doesn't matter. It might be a nameable cadence (dialectic 正反合, myth-vs-reality, countdown / Top-N, Socratic), a deliberate multi-act fusion of several modes, or the user's own feel for how the deck should carry (confrontational here, detached there). Don't try to taxonomize it.
**Always author the candidate; select it only when warranted.** Stage 2 includes one visible, non-empty AI custom proposal beside the five presets, spelling out the cadence / fusion / posture in plain language. It is initially unselected and does not replace the best-fit preset recommendation unless the user already supplied that exact custom direction; with a template, it must fit available prototype capacity. When the user selects it, the editable prose is saved as `mode: custom` plus `mode_behavior`; otherwise it remains recommendation-only. The Strategist crystallizes a selected custom direction in the Design Spec first, then projects the same pair to `spec_lock.md`. The Executor follows that prose in place of a preset file. (This records the intent so it survives 20 pages of generation — the Executor only ever reads `spec_lock.md`, never the chat.) **Always author the candidate; select it only when warranted.** Stage 2 includes one visible, non-empty AI custom proposal beside the five presets, spelling out the cadence / fusion / posture in plain language. It is initially unselected and does not replace the best-fit preset recommendation unless the user already supplied that exact custom direction; with a template, it must fit available prototype capacity. When the user selects it, the editable prose is saved as `mode: custom` plus `mode_behavior`; otherwise it remains recommendation-only. The Strategist crystallizes a selected custom direction in the Design Spec first, then projects the behavior and any actual catalog basis to `spec_lock.md`. The Executor reads every listed basis file before following that prose, or follows it directly when the direction is novel. (This records the intent so it survives 20 pages of generation — the Executor only ever reads `spec_lock.md`, never the chat.)
**Mandatory — read every catalog source actually used**: If the proposal combines or borrows an existing mode, name its exact catalog id in the visible proposal and read that mode file before writing the synthesis. A `pyramid` + `narrative` fusion therefore reads both [`pyramid.md`](./pyramid.md) and [`narrative.md`](./narrative.md), then writes `mode_references: pyramid, narrative` beside `mode_behavior`. Do not add loosely related references after the fact. A genuinely new cadence with no catalog source writes no `mode_references` and may proceed from its standalone behavior.
> **One value per deck — fusion is *one* `custom`, not several modes.** A deck always locks a single `mode`. A multi-mode blend is expressed as **one** `mode: custom` whose `mode_behavior` paragraph describes the acts — never by locking several modes. > **One value per deck — fusion is *one* `custom`, not several modes.** A deck always locks a single `mode`. A multi-mode blend is expressed as **one** `mode: custom` whose `mode_behavior` paragraph describes the acts — never by locking several modes.
> >
@@ -38,11 +38,11 @@ itself is never used as a repeatable tile.
it errors when the pattern uses `patternTransform` or names a preset outside it errors when the pattern uses `patternTransform` or names a preset outside
this enum. this enum.
## 2. PowerPoint-Native Chart / Table Replacement Markers (Authoring Mandatory; Export Opt-in) ## 2. PowerPoint-Native Chart / Table Replacement Markers (Opt-in)
Native PowerPoint tables and Excel-backed charts activate at export time only. Metadata authoring is not opt-in: the default chart/table route still writes dormant replacement metadata while keeping hand-authored SVG geometry pixel-stable across PowerPoint / Keynote / LibreOffice / WPS. Native PowerPoint tables and Excel-backed charts activate at export time only. Generated pages prepare dormant replacement metadata for independently planned native-ready objects while keeping hand-authored SVG geometry pixel-stable across PowerPoint / Keynote / LibreOffice / WPS.
**Hard rule — authoring is mandatory**: Executor writes the marker and JSON metadata in the same edit as every supported data chart and pure text-grid data table ([`executor-chart.md`](./executor-chart.md) §2.2). Mini charts, sparklines, insets, KPI-card trends, and small multiples are included when they encode recoverable data in a supported chart type. Canonical rectangular merged text cells may use the narrow `row_span` / `col_span` contract below; graphical cells stay unmarked on the SVG fallback route. The marker group supplies both visible SVG fallback children for browser/live-preview rendering and JSON metadata for `svg_to_pptx` native export. **Hard rule — planned-object authoring**: Executor writes the marker and JSON metadata in the same edit only for a supported chart or pure text-grid table whose `design_spec.md §IX` page block says `Native-ready: yes` ([`executor-chart.md`](./executor-chart.md) §2.2). `no` and incidental microvisuals stay on the SVG fallback route. For legacy specs only, a matching §VII value may supply the decision when §IX has no field. Canonical rectangular merged text cells may use the narrow `row_span` / `col_span` contract below; graphical cells stay unmarked. The marker group supplies visible SVG fallback children for browser/live-preview rendering and JSON metadata for `svg_to_pptx` native export.
**Hard rule — activation is the opt-in, dormant unless exported with `--native-charts-and-tables`**: A marker only declares that a group is eligible for PowerPoint-native Chart/Table replacement. Normal `svg_to_pptx.py` runs keep the fallback SVG children and convert them into independently editable DrawingML shapes. Pass `--native-charts-and-tables` only when the data source and chart/table-specific object model matter more than cross-renderer layout fidelity: it emits the PowerPoint Chart/Table object and skips the fallback children to avoid duplicates. Native styling preserves the core palette, text, axis, grid, and background colors where possible, but it is still a PowerPoint Chart/Table object rather than a pixel-identical SVG drawing. **Hard rule — activation is the opt-in, dormant unless exported with `--native-charts-and-tables`**: A marker only declares that a group is eligible for PowerPoint-native Chart/Table replacement. Normal `svg_to_pptx.py` runs keep the fallback SVG children and convert them into independently editable DrawingML shapes. Pass `--native-charts-and-tables` only when the data source and chart/table-specific object model matter more than cross-renderer layout fidelity: it emits the PowerPoint Chart/Table object and skips the fallback children to avoid duplicates. Native styling preserves the core palette, text, axis, grid, and background colors where possible, but it is still a PowerPoint Chart/Table object rather than a pixel-identical SVG drawing.
@@ -137,7 +137,7 @@ materialized alternating row fills. Native table typography mirrors the
visible SVG fallback: put `style.font_family` and `style.font_size` on the visible SVG fallback: put `style.font_family` and `style.font_size` on the
marker from the table text already drawn, then use `style.header_font_size` or marker from the table text already drawn, then use `style.header_font_size` or
per-cell `font_size` only when the fallback visibly differs. If the fallback per-cell `font_size` only when the fallback visibly differs. If the fallback
has no explicit table font, use the deck body family and locked body size from has no explicit table font, use the deck body family and declared body anchor from
`spec_lock.md`. `spec_lock.md`.
**Hard rule — table metadata is the native source of truth**: Every row, **Hard rule — table metadata is the native source of truth**: Every row,
@@ -78,8 +78,9 @@ preserve/mirror round-trip contract.
## 3. Fragment Generation ## 3. Fragment Generation
Run one command for one selected object. Generated project pages take colors Run one command for one selected object. Generated project pages choose the
from the current page-context projection of `spec_lock.md`; `create-template` takes colors object's solid paint from the current page context, using `spec_lock.md` roles as
reusable anchors rather than an exhaustive palette; `create-template` takes colors
from the confirmed brief and template `design_spec.md`. Mirror/preserve input from the confirmed brief and template `design_spec.md`. Mirror/preserve input
keeps the source object's paint instead of regenerating this authored form. keeps the source object's paint instead of regenerating this authored form.
@@ -179,7 +180,7 @@ otherwise regenerate the complete compact group.
| Connector attachment | Authoring helper v1 creates an unconnected `p:cxnSp` and does not accept endpoint/site metadata. Do not hand-add it. The imported-shape contract may preserve an attachment that already exists in a source PPTX; creating a new attached connector is currently unsupported. | | Connector attachment | Authoring helper v1 creates an unconnected `p:cxnSp` and does not accept endpoint/site metadata. Do not hand-add it. The imported-shape contract may preserve an attachment that already exists in a source PPTX; creating a new attached connector is currently unsupported. |
| Action button behavior | `actionButton*` presets map visual geometry only. No action, navigation target, or hyperlink is created automatically. | | Action button behavior | `actionButton*` presets map visual geometry only. No action, navigation target, or hyperlink is created automatically. |
| Gradient/pattern paint | Authoring helper v1 accepts solid HEX paint only. Use ordinary SVG when a complex paint treatment is essential. | | Gradient/pattern paint | Authoring helper v1 accepts solid HEX paint only. Use ordinary SVG when a complex paint treatment is essential. |
| Multi-path darken/lighten | Direct visible layers use the shared normalized paint behavior from the PPTX importer. Their registry-derived HEX values are authorized derivatives of the locked base color, not spec-lock drift. | | Multi-path darken/lighten | Direct visible layers use the shared normalized paint behavior from the PPTX importer. Their registry-derived HEX values are authorized derivatives of the selected base color and need no separate lock row. |
| Expanded compatibility | Existing helper-authored carrier/preview fragments remain readable as ordinary Slide-local input and receive a non-blocking migration warning; they do not become structured fixed atoms or object-slot carriers. Imported expanded fragments remain the lossless mirror/preserve form. | | Expanded compatibility | Existing helper-authored carrier/preview fragments remain readable as ordinary Slide-local input and receive a non-blocking migration warning; they do not become structured fixed atoms or object-slot carriers. Imported expanded fragments remain the lossless mirror/preserve form. |
| External edits | Any registry-path, style, or semantic mismatch fails quality check and export; regenerate the fragment. | | External edits | Any registry-path, style, or semantic mismatch fails quality check and export; regenerate the fragment. |
@@ -16,7 +16,7 @@ Every new SVG project declares one deterministic route. Free-design, brand-only,
**Zero-slot Layout**: A named Layout may contain no slots and no fixed Layout atoms. This is valid for a cover, poster, full-visual page, or other fixed composition. Do not manufacture an empty `utility` kind or full-page fake `object` slot. **Zero-slot Layout**: A named Layout may contain no slots and no fixed Layout atoms. This is valid for a cover, poster, full-visual page, or other fixed composition. Do not manufacture an empty `utility` kind or full-page fake `object` slot.
**Adaptive change**: Template `strict` preserves the selected prototype contract. `adaptive` retains the prototype Master and may create a new Layout identity only when fixed Layout atoms or slot topology/bounds change. Update the page mapping immediately while authoring the first such page; never mutate a reused key silently. **Adaptive change**: Template `strict` preserves the selected prototype contract. `adaptive` retains the prototype Master and may use a current or new Layout identity only when Strategist already declared it in the complete plan and lock. If construction proves that fixed Layout atoms or slot topology/bounds must change, stop and return upstream for Strategist to add or revise the definition and page mapping before authoring resumes; Executor never mutates a reused key or the lock.
## 2. Explicit PPTX Master / Layout / Placeholder Metadata ## 2. Explicit PPTX Master / Layout / Placeholder Metadata
@@ -24,7 +24,7 @@ Every new SVG project declares one deterministic route. Free-design, brand-only,
**Project lock**: A Master row is `<master_key>: <PowerPoint picker name>`. A unique Layout row is `<layout_key>: <master_key> | <PowerPoint picker name> | <prototype source>`, where the source is a generated `P<NN>` or installed `template:<basename>`. A page assignment is `P<NN>: <layout_key>` under `page_pptx_layouts`. The SVG root values MUST match the assigned definition. A Layout key belongs to exactly one Master and must be globally unique. Reuse one key only when prototypes share identical ordered Layout atoms and slot ids/types/effective indices/default bounds/binding modes. An unused Layout uses a template SVG source and remains registered without a published carrier slide. Every structured route requires numeric `spec_lock.md` typography `title` / `body` rows. **Project lock**: A Master row is `<master_key>: <PowerPoint picker name>`. A unique Layout row is `<layout_key>: <master_key> | <PowerPoint picker name> | <prototype source>`, where the source is a generated `P<NN>` or installed `template:<basename>`. A page assignment is `P<NN>: <layout_key>` under `page_pptx_layouts`. The SVG root values MUST match the assigned definition. A Layout key belongs to exactly one Master and must be globally unique. Reuse one key only when prototypes share identical ordered Layout atoms and slot ids/types/effective indices/default bounds/binding modes. An unused Layout uses a template SVG source and remains registered without a published carrier slide. Every structured route requires numeric `spec_lock.md` typography `title` / `body` rows.
**Template behavior**: Strict preserves the selected prototype's declared Master/Layout/slot contract. Adaptive retains its Master and may allocate a new Layout key/name only when fixed Layout atoms or slot topology/bounds change; update the lock during authoring. Mirror-created prototypes preserve validated source identity, literal paint, typography, effects, atomic geometry, and referenced assets in a new workspace. `standard` / `fidelity` never make source topology authoritative; mirror does not synthesize a replacement topology or fill missing facts. **Template behavior**: Strict preserves the selected prototype's declared Master/Layout/slot contract. Adaptive retains its Master and realizes the current or new Layout key/name declared by Strategist. A construction-discovered change to fixed Layout atoms or slot topology/bounds returns upstream for plan/lock repair and regenerated page context before authoring resumes. Mirror-created prototypes preserve validated source identity, literal paint, typography, effects, atomic geometry, and referenced assets in a new workspace. `standard` / `fidelity` never make source topology authoritative; mirror does not synthesize a replacement topology or fill missing facts.
Imported inherited-shape visibility remains an immutable analysis fact until a Imported inherited-shape visibility remains an immutable analysis fact until a
structured mirror is materialized. The final mirror root carries that fact with structured mirror is materialized. The final mirror root carries that fact with
@@ -34,9 +34,9 @@ present. Authored `standard` / `fidelity` templates normally omit both and use
the default `true`. See the default `true`. See
[`conversion.md`](../scripts/docs/conversion.md#import-compatibility-and-recovery-boundary). [`conversion.md`](../scripts/docs/conversion.md#import-compatibility-and-recovery-boundary).
**Master text-style contract**: Flat and structured export map the **Master text-style contract**: Flat and structured export map the declared
locked `title` size to every `a:defRPr` in Master `p:titleStyle`. Level 1 in `title` anchor to every `a:defRPr` in Master `p:titleStyle`. Level 1 in both
both `p:bodyStyle` and `p:otherStyle` uses the locked `body` size; levels 29 `p:bodyStyle` and `p:otherStyle` uses the declared `body` anchor; levels 29
use a deterministic descending hierarchy from `15/16` through `8/16` of that use a deterministic descending hierarchy from `15/16` through `8/16` of that
size, rounded to 0.5 pt and floored at the smaller of 8 pt or the body size. size, rounded to 0.5 pt and floored at the smaller of 8 pt or the body size.
Existing per-level indentation and bullet properties remain unchanged. Existing per-level indentation and bullet properties remain unchanged.
@@ -111,6 +111,11 @@ Use `data-pptx-role` only when no specialized marker owns the behavior:
| `header`, `footer`, `logo`, `watermark`, `chrome` | Identify Slide-local static framing without claiming Master/Layout ownership. | | `header`, `footer`, `logo`, `watermark`, `chrome` | Identify Slide-local static framing without claiming Master/Layout ownership. |
| `page-number` | Identify a Slide-local number when no `slide-number` placeholder exists. | | `page-number` | Identify a Slide-local number when no `slide-number` placeholder exists. |
On flat pages, a direct root background image or full-canvas scrim/decoration
rectangle may carry the matching role and remain a primitive. Give the marked
element a stable unique `id`; do not add a `<g>` solely to avoid an
ungrouped-element advisory.
Do not add structural roles to ordinary titles, body copy, cards, KPIs, diagrams, charts, icons, or images. Do not add structural roles to ordinary titles, body copy, cards, KPIs, diagrams, charts, icons, or images.
--- ---
@@ -424,12 +424,14 @@ helper cannot write a project, select layout, or generate a page.
**Authoring paint boundary**: v1 accepts `none` or six-digit solid HEX fill and **Authoring paint boundary**: v1 accepts `none` or six-digit solid HEX fill and
stroke, optional fill/stroke opacity, stroke width, line cap, and line join. stroke, optional fill/stroke opacity, stroke width, line cap, and line join.
Generated pages take colors from `spec_lock.md`; `create-template` authored Generated pages use `spec_lock.md` for stable semantic color anchors and choose
templates take them from the confirmed brief and template `design_spec.md`. page-local paint from the retained Design Spec, style, and composition context;
`create-template` authored templates take their values from the confirmed brief
and template `design_spec.md`.
Use ordinary SVG for gradients, patterns, filters, or other treatments outside Use ordinary SVG for gradients, patterns, filters, or other treatments outside
this narrow contract. Registry-derived multi-path darken/lighten colors are this narrow contract. Registry-derived multi-path darken/lighten colors and
authorized derivatives of the locked base paint and do not count as color other contextual derivatives need no separate lock row unless they become a
drift. Mirror preserves source paint under §1.4 instead. recurring named role. Mirror preserves source paint under §1.4 instead.
**Validation**: quality check and export both rerender authored fragments from **Validation**: quality check and export both rerender authored fragments from
`preset + frame + adjustments + group paint` and compare every visible path and `preset + frame + adjustments + group paint` and compare every visible path and
@@ -575,7 +577,7 @@ These forms are needed only when the stated PPT behavior matters:
| Stable object grouping or object-level animation anchor | Wrap the intended object in `<g id="...">`. Content grouping is **mandatory** per §4.3 — a top-level `<g id>` is also the animation anchor; it is not an optional convenience. | | Stable object grouping or object-level animation anchor | Wrap the intended object in `<g id="...">`. Content grouping is **mandatory** per §4.3 — a top-level `<g id>` is also the animation anchor; it is not an optional convenience. |
| Native PowerPoint background promotion | Outside structured mode, the first eligible visual layer may be a direct full-canvas `<rect>` or one inside a simple single-child group. Its fill must have a registered native mapping (solid, linear/radial gradient, or preset pattern), and it must have no transform, filter, clip, rounding, or visible stroke. Export writes the fill as Slide `p:bg`; image elements remain pictures. Structured routes use the narrower explicit solid-background ownership contract in [`pptx-structure-interface.md`](./pptx-structure-interface.md). | | Native PowerPoint background promotion | Outside structured mode, the first eligible visual layer may be a direct full-canvas `<rect>` or one inside a simple single-child group. Its fill must have a registered native mapping (solid, linear/radial gradient, or preset pattern), and it must have no transform, filter, clip, rounding, or visible stroke. Export writes the fill as Slide `p:bg`; image elements remain pictures. Structured routes use the narrower explicit solid-background ownership contract in [`pptx-structure-interface.md`](./pptx-structure-interface.md). |
| Free-design / brand-only PowerPoint structure | Use `pptx_structure.mode: flat`. Keep every represented object Slide-local; export materializes one clean project-owned Master plus one Blank Layout from the current lock, removes stock content placeholders/Layout inventory, and retains only the standard date/footer/slide-number capability hooks. Do not author Master/Layout identities, layers, or placeholder slots. | | Free-design / brand-only PowerPoint structure | Use `pptx_structure.mode: flat`. Keep every represented object Slide-local; export materializes one clean project-owned Master plus one Blank Layout from the current lock, removes stock content placeholders/Layout inventory, and retains only the standard date/footer/slide-number capability hooks. Do not author Master/Layout identities, layers, or placeholder slots. |
| Reusable template-based PowerPoint Layout | Select one complete authoring SVG per page in `page_layouts`, declare each unique Master/Layout definition once, and assign pages through `page_pptx_layouts`. Strict preserves the prototype contract; adaptive retains its Master and may define and assign a new explicit Layout key during page authoring. Non-mirror skin follows `spec_lock`. | | Reusable template-based PowerPoint Layout | Select one complete authoring SVG per page in `page_layouts`, declare each unique Master/Layout definition once, and assign pages through `page_pptx_layouts`. Strict preserves the prototype contract; adaptive retains its Master and uses a current or new Layout key already declared and assigned by Strategist. Construction cannot extend or mutate that mapping downstream. Non-mirror skin follows `spec_lock`. |
**Hard rule — supported shape conversion**: Every PPT editability claim in this specification refers to the project converter reading `svg_output/` and emitting native DrawingML. `svg_final/` is a self-contained visual preview that may be inserted into PowerPoint as an SVG picture. PowerPoint's manual Convert-to-Shape operation is unsupported; do not narrow the authoring contract to its undocumented SVG subset. **Hard rule — supported shape conversion**: Every PPT editability claim in this specification refers to the project converter reading `svg_output/` and emitting native DrawingML. `svg_final/` is a self-contained visual preview that may be inserted into PowerPoint as an SVG picture. PowerPoint's manual Convert-to-Shape operation is unsupported; do not narrow the authoring contract to its undocumented SVG subset.
@@ -583,7 +585,7 @@ These forms are needed only when the stated PPT behavior matters:
**Hard rule — root groups protect body-text layout**: Every visible direct root `<g>` declares positive root-coordinate `data-pptx-bounds="x y width height"`. Keep it when frame/native coordinates size one PowerPoint object; placeholder bounds also supply the slot frame. Checker validates this subcanvas against the root `viewBox`, then recursively validates only estimable `<text>` descendants against it. Nested groups and all shapes, images, paths, `<use>` instances, effects, and object frames are not content-boundary inputs. Per side, Checker ignores text/bounds overflow through `1px`, warns through `5%` of the containing boundary dimension, and fails above `5%`. Bounds do not clip or reflow. **Hard rule — root groups protect body-text layout**: Every visible direct root `<g>` declares positive root-coordinate `data-pptx-bounds="x y width height"`. Keep it when frame/native coordinates size one PowerPoint object; placeholder bounds also supply the slot frame. Checker validates this subcanvas against the root `viewBox`, then recursively validates only estimable `<text>` descendants against it. Nested groups and all shapes, images, paths, `<use>` instances, effects, and object frames are not content-boundary inputs. Per side, Checker ignores text/bounds overflow through `1px`, warns through `5%` of the containing boundary dimension, and fails above `5%`. Bounds do not clip or reflow.
Wrap each logical Slide-local body unit in a descriptive top-level `<g id>`; aim for **38 ordinary groups**, each becoming one animation step. Nested implementation groups may remain anonymous and need no bounds; any nested bounds are ignored. Flat pages use ordinary groups; structured slots already qualify, while titles, direct atomic Master/Layout elements, and canvas-level decoration may remain root primitives. Wrap each logical Slide-local body unit in one descriptive top-level `<g id>`; group count follows the page's semantic units, and each group becomes one animation step when animation is enabled. Nested implementation groups may remain anonymous and need no bounds; any nested bounds are ignored. Flat pages use ordinary groups; structured slots already qualify, while titles, direct atomic Master/Layout elements, and canvas-level static framing—including background images and full-canvas scrim/decoration rectangles—may remain root primitives. On flat pages, give such static framing a stable `id` plus `data-pptx-role="background"` / `"decoration"`; never add a `<g>` solely to silence an ungrouped-element advisory.
**Structural atoms and slots are excluded automatically.** `data-pptx-layer` and `data-pptx-placeholder` semantics are read first; otherwise explicit `data-pptx-role` values (`background`, `decoration`, `header`, `footer`, `chrome`, `watermark`, `page-number`, `logo`) mark Slide-local static framing (§4.1, [`semantic-svg.md`](semantic-svg.md)). A normal slot group has exactly one direct compatible carrier; several drawing atoms require the explicit composite `object` proxy fallback. Native chart/table carrier groups retain their specialized [`native-data-interface.md`](./native-data-interface.md) contract. **Structural atoms and slots are excluded automatically.** `data-pptx-layer` and `data-pptx-placeholder` semantics are read first; otherwise explicit `data-pptx-role` values (`background`, `decoration`, `header`, `footer`, `chrome`, `watermark`, `page-number`, `logo`) mark Slide-local static framing (§4.1, [`semantic-svg.md`](semantic-svg.md)). A normal slot group has exactly one direct compatible carrier; several drawing atoms require the explicit composite `object` proxy fallback. Native chart/table carrier groups retain their specialized [`native-data-interface.md`](./native-data-interface.md) contract.
@@ -10,7 +10,7 @@ Conditional extension for formula assets, proposed / confirmed image elaboration
## 1. Proposed and Confirmed Image Plan ## 1. Proposed and Confirmed Image Plan
Before Stage 2, use proposed sources only for candidate construction. After confirmation, discard candidate-only sources, map the confirmed set through [`strategist.md`](./strategist.md) §h, and honor every explicit role or page instruction in `image_notes`; this module never adds a source. The confirmed set is a production requirement, not a fresh candidate list: represent every confirmed non-`none` source at least once. Asset inventory and later aesthetic judgment may shape the unconfirmed count, subject, placement, and composition, but must not delete, substitute, or demote a confirmed source. Before Stage 2, use proposed sources only for candidate construction. After confirmation, discard candidate-only sources, map the confirmed set through [`strategist.md`](./strategist.md) §h, and honor explicit `image_notes` roles; this module never adds a source. The confirmed non-`none` set is an allowed acquisition boundary, not coverage: use a suitable subset and leave irrelevant sources unused. Explicit must-use sources, assets, or page roles remain required. Asset inventory and judgment determine unconfirmed count, subject, placement, and composition without substituting an unconfirmed source.
For illustration, apply this precedence: confirmed `none` → explicit user intent → the locked visual style's `Illus.` propensity (`core` / `supportive` / `sparse`) → none. Propensity controls the lean, not the source or a page quota. When illustration is active, prefer one coherent motif family across hero/section anchors and local spots, but only when the confirmed assets can form that family. For illustration, apply this precedence: confirmed `none` → explicit user intent → the locked visual style's `Illus.` propensity (`core` / `supportive` / `sparse`) → none. Propensity controls the lean, not the source or a page quota. When illustration is active, prefer one coherent motif family across hero/section anchors and local spots, but only when the confirmed assets can form that family.
@@ -18,9 +18,9 @@ For ≥3 AI-generated same-family spots, plan one unplaced `ai` Illustration She
## 2. AI Image Strategy — propose before Stage 2; lock only for confirmed `ai` ## 2. AI Image Strategy — propose before Stage 2; lock only for confirmed `ai`
When proposed sources include `ai`, read every entry in [`image-renderings/_index.md`](./image-renderings/_index.md) before constructing Stage 2. Unless the user or active template already names a rendering, place at least three credible, distinct preset renderings across the coordinated safe/shifted/bold directions; a genuine compatibility shortfall may return fewer with a reason. Each preset `image_strategy` carries localized `rendering`, `visual`, and `mood` only. Mood includes a recognizable real-world analogy. Image colors always inherit that direction's deck HEX roles; never add an image palette or alter deck colors to rescue a rendering. When proposed sources include `ai`, read [`image-renderings/_index.md`](./image-renderings/_index.md) before constructing Stage 2. Unless the user or active template already names a rendering, place at least three credible, distinct preset renderings across the coordinated safe/shifted/bold directions; a genuine compatibility shortfall may return fewer with a reason. Each preset `image_strategy` carries localized `rendering`, `visual`, and `mood` only. Mood includes a recognizable real-world analogy. Image colors always inherit that direction's deck HEX roles; never add an image palette or alter deck colors to rescue a rendering.
Also write one `custom_candidates.image_strategy` under the Confirm UI contract: localized `name` / `visual` / `mood`, `rendering: custom`, and non-empty localized `behavior` satisfying the catalog grammar. Keep it unselected unless the user supplied it (`recommend.image_strategy: custom`); under a template it obeys inherited identity and application. Only a selected custom locks its edited behavior as `image_rendering_behavior`; otherwise discard it downstream. Ignore legacy `image_palette`. Also write one `custom_candidates.image_strategy` under the Confirm UI contract: localized `name` / `visual` / `mood`, `rendering: custom`, and non-empty localized `behavior` satisfying the catalog grammar. If it combines or borrows existing renderings, name every exact id in the visible proposal and read every corresponding `image-renderings/<id>.md` before writing the synthesis. If it is genuinely novel, read no preset file and name no catalog basis. Keep it unselected unless the user supplied it (`recommend.image_strategy: custom`); under a template it obeys inherited identity and application. Only a selected custom locks its edited behavior as `image_rendering_behavior`; when catalog material is actually used, also project the exact ids as `image_rendering_references`, otherwise omit that field. Discard an unselected candidate downstream. Ignore legacy `image_palette`.
For specialized or regulated paper-figure subjects, preserve the prompt depth required by [`image-generator.md`](./image-generator.md) §4.2 rather than shortening to a generic brief. Scan the outline for genuine image-led pages, list the proposed hero pages in Stage-2 `image_notes` so the user can retain, edit, or remove them in the same confirmation, then mark only the confirmed pages' AI rows `page_role: hero_page`; local is the default. `text_policy: embedded` is reserved for lettering that must be fused into the artwork; ordinary titles, data, labels, and prose remain editable SVG. Analyze confirmed provided assets before writing §VIII. For specialized or regulated paper-figure subjects, preserve the prompt depth required by [`image-generator.md`](./image-generator.md) §4.2 rather than shortening to a generic brief. Scan the outline for genuine image-led pages, list the proposed hero pages in Stage-2 `image_notes` so the user can retain, edit, or remove them in the same confirmation, then mark only the confirmed pages' AI rows `page_role: hero_page`; local is the default. `text_policy: embedded` is reserved for lettering that must be fused into the artwork; ordinary titles, data, labels, and prose remain editable SVG. Analyze confirmed provided assets before writing §VIII.
@@ -47,10 +47,10 @@ Follow `latex_render.py --help` for the manifest fields. The renderer writes dim
## 4. Image Resource List ## 4. Image Resource List
Add §VIII for every confirmed non-`none` source and selected formula; a formula-only plan contains only formula rows. Fill the scaffold's filename, dimensions/ratio, layout suggestion/pattern, purpose/type, acquisition, status, reference, and conditional AI fields. `Acquire Via` is `ai`, `web`, `user`, `formula`, `placeholder`, or `slice`; status follows [`svg-image-embedding.md`](./svg-image-embedding.md). When an asset is not yet available, retain its confirmed-source row as `Pending` or `Needs-Manual`; never remove the row or change `Acquire Via` to make the Design Spec look complete. Only after §VIII passes the final-confirmation fidelity gate, project the same planned filenames and acquisition sources into `spec_lock.md images`; do not redesign the image plan while writing the lock. References describe visual intent: AI uses subject + intent + composition without repeating rendering or HEX; web uses a concrete subject plus a few positive quality descriptors; formula preserves the source LaTeX and placement intent. Add §VIII rows for the image resources actually planned from the confirmed source boundary and for every selected formula; a formula-only plan contains only formula rows. A permitted but unused source needs no row. Author each row's filename, dimensions/ratio, layout pattern, crop policy, purpose/type, acquisition, status, reference, and conditional AI fields as part of the complete Design Spec. `Acquire Via` is `ai`, `web`, `user`, `formula`, `placeholder`, or `slice`; status follows [`svg-image-embedding.md`](./svg-image-embedding.md). When a planned or explicitly required asset is not yet available, retain its row as `Pending` or `Needs-Manual`; never remove the row or change `Acquire Via` to make the Design Spec look complete. After §VIII passes final confirmation, project every placed row into `spec_lock.md images` as `<path> | source=<Acquire Via> | pattern=<Layout pattern> | crop=<adaptive|no-crop>` and omit unplaced Illustration Sheets. References describe visual intent: AI uses subject + intent + composition without repeating rendering or HEX; web uses a concrete subject plus a few positive quality descriptors; formula preserves the source LaTeX and placement intent.
🚧 **GATE — non-formula rows**: read every entry in [`image-layout-patterns.md`](./image-layout-patterns.md). Copy one primary `#<id> <name>` plus any modifier names verbatim into each row; no empty, paraphrased, or invented ids. For decks with at least four image-bearing pages, use an Image-as-Canvas + Native Overlay pattern at least once unless every image is purely a cover, divider, or atmospheric backdrop; record the legitimate exception below the table. Reconsider a plan that collapses every row to the same left/right or top/bottom split. 🚧 **GATE — non-formula rows**: read every entry in [`image-layout-patterns.md`](./image-layout-patterns.md). Copy one primary `#<id> <name>` plus any modifier names verbatim into each row; no empty, paraphrased, or invented ids. Strategist owns that pattern selection; Executor adapts its geometry while retaining the selected primary/modifier semantics, resource role, and explicit constraints. Audit the completed column against page intent: repeated left/right or top/bottom structures are valid when the narrative calls for them, but catalog families and modifiers must remain available without a usage quota.
Choose narrative intent before dimensions: hero/full-bleed, atmosphere/background, side-by-side, or accent/inline. Only side-by-side containers follow native ratio; portrait and multi-image calculations belong to [`image-layout-spec.md`](./image-layout-spec.md). Most assets are croppable. Add `no-crop` in `spec_lock.md images` only for screenshots, charts, certificates/contracts, dense diagrams, and every formula; formula rows use `Type: Latex Formula`, `Acquire Via: formula`, and `Rendered` or `Needs-Manual`. Choose narrative intent before dimensions: hero/full-bleed, atmosphere/background, side-by-side, or accent/inline. Portrait and multi-image calculations belong to [`image-layout-spec.md`](./image-layout-spec.md). Write `Crop Policy: no-crop` whenever cropping could remove required pixels, labels, evidence, identity, or edge content; screenshots, charts, certificates/contracts, dense diagrams, logos, product markings, and formulas are common triggers rather than an exhaustive list. Otherwise write `Crop Policy: adaptive`: Executor may use complete display or a focal-safe crop, and the value never commands cropping. Formula rows use `Type: Latex Formula`, `Acquire Via: formula`, `Crop Policy: no-crop`, and `Rendered` or `Needs-Manual`.
Judge `text_policy` per AI row using [`image-generator.md`](./image-generator.md) §5.3; paper figures, academic schematics, panel comparisons, and data-axis graphics are positive triggers for reconsidering an all-`none` plan. Step 5 dispatches pending `ai` / `slice` rows to Image_Generator and pending `web` rows to Image_Searcher; formula rows bypass both. Judge `text_policy` per AI row using [`image-generator.md`](./image-generator.md) §5.3; paper figures, academic schematics, panel comparisons, and data-axis graphics are positive triggers for reconsidering an all-`none` plan. Step 5 dispatches pending `ai` / `slice` rows to Image_Generator and pending `web` rows to Image_Searcher; formula rows bypass both.
@@ -76,7 +76,7 @@ For `mirror` / `layout`, write `pptx_structure.mode: structured` plus `template_
- **Reusable Layout roster**: Write every unique Layout once as `<layout_key>: <master_key> | <PowerPoint layout name> | <prototype source>`. Copy installed `template:<basename>` sources, including currently unused Layouts. A new adaptive Layout uses its first generated `P<NN>` as source. Reuse a key only when fixed atoms and slot ids/types/indices/bounds/binding modes are identical. Name authored keys after composition, never page topic. A Layout may intentionally have zero slots; do not manufacture an empty `utility` kind or full-page fake slot. - **Reusable Layout roster**: Write every unique Layout once as `<layout_key>: <master_key> | <PowerPoint layout name> | <prototype source>`. Copy installed `template:<basename>` sources, including currently unused Layouts. A new adaptive Layout uses its first generated `P<NN>` as source. Reuse a key only when fixed atoms and slot ids/types/indices/bounds/binding modes are identical. Name authored keys after composition, never page topic. A Layout may intentionally have zero slots; do not manufacture an empty `utility` kind or full-page fake slot.
- **Page assignment**: Write exactly one `page_pptx_layouts` row per page. Each key must exist in `pptx_layouts`. Check that distinct compositions do not collapse into role-only keys and that one skeleton does not split into topic-specific keys. - **Page assignment**: Write exactly one `page_pptx_layouts` row per page. Each key must exist in `pptx_layouts`. Check that distinct compositions do not collapse into role-only keys and that one skeleton does not split into topic-specific keys.
- **Slot planning**: Each reusable slot is a direct root `<g id>` with `data-pptx-placeholder`, positive design-zone bounds, and exactly one compatible direct carrier. Bounds come from the intended safe area, column, panel inset, or media frame—not sample text ink. A genuinely composite region may use only the explicit `object` + `proxy` downgrade. - **Slot planning**: Each reusable slot is a direct root `<g id>` with `data-pptx-placeholder`, positive design-zone bounds, and exactly one compatible direct carrier. Bounds come from the intended safe area, column, panel inset, or media frame—not sample text ink. A genuinely composite region may use only the explicit `object` + `proxy` downgrade.
- **Adaptive refinement**: Initial definitions are complete. If construction changes reusable framing or slot topology/bounds, Executor creates one new definition sourced from that page and updates its assignment; it never mutates a reused contract silently. Export only compiles declared structure and never discovers or clusters Layouts. - **Adaptive refinement**: Initial definitions are complete. If construction shows that reusable framing or slot topology/bounds must change, return to Strategist to add a definition sourced from that page and update its assignment before execution resumes. Executor never mutates or extends the contract; export only compiles declared structure and never discovers or clusters Layouts.
- **Input prototypes**: Add one `page_layouts` row per page. Strict preserves that SVG's contract; adaptive keeps its Master and may declare a new output Layout; mirror also preserves literal visuals and text-node topology. - **Input prototypes**: Add one `page_layouts` row per page. Strict preserves that SVG's contract; adaptive keeps its Master and may declare a new output Layout; mirror also preserves literal visuals and text-node topology.
**Chart compatibility**: Use `page_layouts` together with `page_charts` only when the selected prototype shell is compatible. For a chart page without an exact roster match, adaptive mode starts from the closest neutral prototype and declares an output Layout; strict mode selects an existing compatible Layout or revises the outline. Never omit `page_layouts` on a structured route. **Chart compatibility**: Use `page_layouts` together with `page_charts` only when the selected prototype shell is compatible. For a chart page without an exact roster match, adaptive mode starts from the closest neutral prototype and declares an output Layout; strict mode selects an existing compatible Layout or revises the outline. Never omit `page_layouts` on a structured route.
@@ -20,7 +20,7 @@ As a top-tier AI presentation strategist, receive source documents, perform cont
## 1. Strategist Confirmation Stage ## 1. Strategist Confirmation Stage
🚧 **GATE — artifact structure**: Generate Step 4 creates both versioned scaffolds before authoring. Fill those files in place and run `project_manager.py validate`; the machine schemas, not remembered headings, own their grammar. 🚧 **GATE — whole-document authoring**: Generate Step 4 reads `templates/design_spec_reference.md`, writes the complete Design Spec from scratch, passes Gate 1, then reads `templates/spec_lock_reference.md` and writes the complete lock projection. For a new project, create each finished artifact once; do not instantiate or patch a placeholder scaffold. Run `project_manager.py validate`; the machine schemas, not remembered headings, own grammar validation.
**BLOCKING**: After the read, present professional recommendations for the confirmation fields below and wait for explicit user confirmation. **BLOCKING**: After the read, present professional recommendations for the confirmation fields below and wait for explicit user confirmation.
@@ -32,15 +32,29 @@ As a top-tier AI presentation strategist, receive source documents, perform cont
| **2 — complete deck solution** (authored once from the user's *actual* Stage 1) | reading mode (`delivery_purpose`, PPT only) · `d` mode + visual style · `b` page count · `e` color · `f` icon · `g` typography · `h` image source + generated-image rendering · conditional natural-language template application | derived from the confirmed contract; internal template exporter modes remain hidden | | **2 — complete deck solution** (authored once from the user's *actual* Stage 1) | reading mode (`delivery_purpose`, PPT only) · `d` mode + visual style · `b` page count · `e` color · `f` icon · `g` typography · `h` image source + generated-image rendering · conditional natural-language template application | derived from the confirmed contract; internal template exporter modes remain hidden |
| **3 — resources / production** (authored once from the user's *actual* Stage 1 + Stage 2) | formula policy · conditional AI-image acquisition path · generation mode · refine-spec toggle | derived from the confirmed solution | | **3 — resources / production** (authored once from the user's *actual* Stage 1 + Stage 2) | formula policy · conditional AI-image acquisition path · generation mode · refine-spec toggle | derived from the confirmed solution |
Do not force communication intent into one catalog label. A deck may report progress, expose risk, and request a decision in the same artifact; Stage 1 records that relationship in prose. Its editable prose fields are recommendation drafts, not required inputs: confirmation accepts the current text exactly, including blanks, and a cleared field must not be repopulated later. Stage 2 then confirms one complete solution: narrative spine, reading density, page budget, visual system, and image direction. When a template workspace is installed, Strategist also inspects its real prototypes and current content, presents one editable natural-language application plan, and keeps only exporter reuse/adherence values internal. Present ≥3 coordinated design directions (safe / shifted / bold) so color, type, icons, and generated-image rendering begin coherent; the user may still override each component. Generated images inherit the selected deck colors directly—there is no second image-palette confirmation. Stage 3 asks only how to produce the locked solution. **Page count is derived, not an anchor**—it follows content volume × desired audience outcome × reading mode. Author each stage once: same-stage edits update only visible browser state through documented deterministic dependencies and never trigger a new AI / backend recommendation. The launch / derive / wait mechanics live in [`generate-pptx.md`](../workflows/generate-pptx.md) Step 4; the item specs below keep their `a``h` letters. Do not force communication intent into one catalog label; Stage 1 records composite intent in prose. Editable prose fields are recommendation drafts, not required inputs: confirmation preserves current text and blanks; never repopulate a cleared field. Stage 2 confirms narrative spine, reading density, page budget, visual system, and image direction. With a template, inspect its actual prototypes/content, present one editable application plan, and keep exporter reuse/adherence internal. Present ≥3 coordinated safe / shifted / bold directions so color, type, icons, and generated-image rendering begin coherent; the user may override each component. Generated images inherit deck colors—there is no second image palette. Stage 3 covers production. Author each stage once; same-stage edits update only visible browser state through documented deterministic dependencies, without another AI/backend recommendation. Launch/derive/wait mechanics live in [`generate-pptx.md`](../workflows/generate-pptx.md) Step 4; item specs keep `a``h`.
> **Execution discipline**: This is the last BLOCKING checkpoint in the pipeline. After confirmation, complete the Design Spec and proceed to image generation / SVG / post-processing without further pauses. > **Execution discipline**: This is the last BLOCKING checkpoint in the pipeline. After confirmation, complete the Design Spec and proceed to image generation / SVG / post-processing without further pauses.
> >
> **One opt-in exception**: present the spec-refinement line alongside the split-mode note ([`generate-pptx.md`](../workflows/generate-pptx.md) Step 4). It is OFF by default — the above discipline holds unchanged. Only when the user *explicitly* asks to refine the spec do you hand off to the [refine-spec](../workflows/stages/refine-spec.md) stage, which produces the full spec first and stops for user review/revision of any part before generation. Never enter it unprompted. > **One opt-in exception**: present the spec-refinement line alongside the split-mode note ([`generate-pptx.md`](../workflows/generate-pptx.md) Step 4). It is OFF by default — the above discipline holds unchanged. Only when the user *explicitly* asks to refine the spec do you hand off to the [refine-spec](../workflows/stages/refine-spec.md) stage, which produces the full spec first and stops for user review/revision of any part before generation. Never enter it unprompted.
> **Default presentation surface — Confirm UI.** Write `<project>/confirm_ui/recommendations.json` and launch per Generate Step 4. Stage 2 carries ≥3 safe / shifted / bold `design_directions`; each bundles visual style, a six-role HEX palette, CJK + Latin typography, icons, and conditional image rendering. Also print the recommendations + URL in chat as fallback context. Skip launch only for an explicit chat-only request; a chat-question tool is not a substitute. Read the confirmed `result.json`. [`confirm_ui.md`](../scripts/docs/confirm_ui.md) owns schema and lifecycle. > **Default presentation surface — Confirm UI.** Write `<project>/confirm_ui/recommendations.json` and launch per Generate Step 4. Stage 2 carries ≥3 safe / shifted / bold `design_directions`; each bundles visual style, a six-role HEX palette, CJK + Latin heading/body typography, icons, and conditional image rendering. Also print the recommendations + URL in chat as fallback context. Skip launch only for an explicit chat-only request; a chat-question tool is not a substitute. Generate Step 4 reads the final confirmed `result.json` once and retains that object for Design Spec authoring. [`confirm_ui.md`](../scripts/docs/confirm_ui.md) owns schema and lifecycle.
> ⛔ **GATE — final confirmation is the Design Spec input contract.** Immediately before authoring `design_spec.md`, re-read the complete final `result.json` with `stage: final` and `status: confirmed`; on a chat path, use the final visible confirmation summary as the equivalent state. Every explicitly present confirmed field is mandatory. Decide only details that remain unconfirmed. Never omit, delete, replace, narrow, weaken, reinterpret, or re-recommend a confirmed value because later analysis, asset inventory, template evidence, or personal judgment suggests another choice. Consume conditional fields according to their declared semantics, and preserve an explicitly cleared prose field as empty. If a confirmed value cannot be honored, keep the requirement visible and follow [`failure-recovery.md`](../workflows/governance/failure-recovery.md) instead of silently changing it. **Confirmed-value semantics**: confirmation preserves both the value and the owning field's semantic type. Apply the type to the affected property, not automatically to the whole object:
| Type | Consumption |
|---|---|
| Literal requirement | Preserve the exact contracted value, pixels, wording, or topology. |
| Semantic requirement | Preserve facts, relationships, intent, prohibitions, and completeness; expression may change. |
| Identity anchor | Keep recurring identity stable without creating an exhaustive allowlist. |
| 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. |
**Authority chain — materials → Strategist preparation → realization.** User inputs set materials/acquisition bounds. Strategist owns sufficiency, gap-filling, and selection: roster/content, resources, chart/layout keys, fonts, palette anchors, icons, and crop bans. Fact research may precede confirmation; AI/web/slice follows final confirmation plus completed §VIII/lock; icons are synced/validated during authoring. Before Executor, each resource has a path and terminal/`Needs-Manual` state. Executor owns geometry, composition, hierarchy, spacing, treatment; 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.
> ⛔ **GATE — final confirmation is consumed once into the Design Spec.** Use the complete final object already read by Generate Step 4 (`stage: final`, `status: confirmed`); on a chat path, use the final visible confirmation summary as the equivalent retained state. Do not reopen `result.json` during normal Design Spec or lock authoring. Consume every explicitly present field according to the semantics above and its field owner. Do not omit or substitute a value, and do not silently strengthen or weaken its type. Decide only details left unconfirmed; preserve an explicitly cleared prose field as empty. If a confirmed requirement cannot be honored, keep it visible and follow [`failure-recovery.md`](../workflows/governance/failure-recovery.md) instead of silently changing it.
### a. Canvas Format Confirmation ### a. Canvas Format Confirmation
@@ -48,7 +62,7 @@ Recommend format based on scenario (see [`canvas-formats.md`](canvas-formats.md)
### b. Page Count Confirmation ### b. Page Count Confirmation
**Stage-2 (derived).** Page count is not an anchor—recommend it only after the Stage-1 communication contract is confirmed and alongside reading mode. Derive it from source volume, desired audience outcome, delivery context / artifact afterlife, and reading mode (`text` packs denser; `presentation` is one-idea-per-page and may need more). The user's confirmed count still wins. **Stage-2 planning input.** Confirm UI may hold an approximation/range; *exactly*, *1:1*, or preservation fixes it. After Stage 1, choose one exact count from source volume, audience outcome, delivery context/afterlife, and reading mode, then author the complete §IX roster. After Gate 1, that roster's ids, count, and order—not the earlier UI wording—are invariant. Executor cannot add, drop, merge, split, or reorder pages; changes first repair or reconfirm the Design Spec.
### c. Communication Contract Confirmation ### c. Communication Contract Confirmation
@@ -101,7 +115,7 @@ When authoring §IX, translate every purpose named in `communication_intent` int
Two independent layers, each locks one preset or `custom`. Output: `d. Mode: <mode> + Visual style: <visual_style>`. Two independent layers, each locks one preset or `custom`. Output: `d. Mode: <mode> + Visual style: <visual_style>`.
> **Mandatory AI custom candidates.** Every Stage-2 `recommendations.json` carries visible, non-empty `custom_candidates.mode` and `.visual_style`, initially unselected unless the user supplied that exact direction. If selected, spell the proposal out in plain language and save literal `custom` plus the edited `mode_behavior` / `visual_style_behavior`; otherwise it remains recommendation-only. Never write bespoke prose as the enum value. > **Mandatory AI custom candidates.** Every Stage-2 `recommendations.json` carries visible, non-empty `custom_candidates.mode` and `.visual_style`, initially unselected unless the user supplied that exact direction. If a proposal combines or borrows catalog entries, read every named entry file before authoring the synthesis and name those exact ids in the visible proposal; a genuinely novel proposal needs no catalog reference. If selected, spell the proposal out in plain language and save literal `custom` plus the edited `mode_behavior` / `visual_style_behavior`; otherwise it remains recommendation-only. Never write bespoke prose as the enum value.
#### Layer 1 — Communication mode #### Layer 1 — Communication mode
@@ -110,12 +124,12 @@ Two independent layers, each locks one preset or `custom`. Output: `d. Mode: <mo
The deck's **narrative + persuasion skeleton** — how the argument is organized and advanced. Lock one preset from `pyramid` / `narrative` / `instructional` / `showcase` / `briefing`, or `custom` with behavior. The deck's **narrative + persuasion skeleton** — how the argument is organized and advanced. Lock one preset from `pyramid` / `narrative` / `instructional` / `showcase` / `briefing`, or `custom` with behavior.
**Source**: **Source**:
- User supplied their own outline / structure → it is authoritative. Transcribe it into `§IX` as given (page order + titles preserved); still lock a mode, but for register / voice and page-internal treatment, **not** to reshape — never reorder the user's pages or rewrite their given titles. Note in `design_spec.md` that the structure is user-authored. `briefing` imposes the least if no particular "讲法" is intended. - User supplied their own outline / structure → preserve its facts and intended relationships, then apply the confirmed `content_divergence`. Treat an ordinary source outline as a Reference: regroup, reorder, or retitle when the communication contract benefits. Treat it as authoritative only when the user presents it as the final page plan or explicitly asks to preserve page order, titles, or wording; record that promoted boundary in `design_spec.md`. Still lock a mode for register, voice, and any permitted reshaping. `briefing` imposes the least if no particular "讲法" is intended.
- Beautify / re-layout profile ([`beautify-pptx.md`](../workflows/profiles/beautify-pptx.md)) → the extracted source content is authoritative and **verbatim**, one step stricter than the user-outline case above. Each source slide becomes exactly one `§IX` page in source order; transcribe every content block word-for-word — never reshape / re-primary / condense / merge / split / reword. Lock `mode: briefing`; color (e) and typography (g) are whatever the user confirmed in the beautify plan — the source identity (theme or observed) by default, or a content / brand-aware alternative the beautify plan offered and the user picked — locked as truth (the beautify plan already ran the recommendation through the confirm UI, so do not re-recommend here). Charts / tables / images are regenerated from their extracted data in the inherited style (route chart/table data to §VII, pictures to §VIII) — data values stay frozen, the rendering is the deck's own; never carried over verbatim. Layout, hierarchy, rhythm, and visual rendering are what gets redesigned. - Beautify / re-layout profile ([`beautify-pptx.md`](../workflows/profiles/beautify-pptx.md)) → the extracted source content is authoritative and **verbatim**, one step stricter than the user-outline case above. Each source slide becomes exactly one `§IX` page in source order; transcribe every content block word-for-word — never reshape / re-primary / condense / merge / split / reword. Lock `mode: briefing`; color (e) and typography (g) are whatever the user confirmed in the beautify plan — the source identity (theme or observed) by default, or a content / brand-aware alternative the beautify plan offered and the user picked — locked as truth (the beautify plan already ran the recommendation through the confirm UI, so do not re-recommend here). Charts / tables / images are regenerated from their extracted data in the inherited style (route chart/table data to §VII, pictures to §VIII) — data values stay frozen, the rendering is the deck's own; never carried over verbatim. Layout, hierarchy, rhythm, and visual rendering are what gets redesigned.
- A bespoke direction the five don't give — a nameable cadence (dialectic 正反合, myth-vs-reality, countdown, Socratic), a multi-act fusion of modes, or the user's own feel (confrontational here, detached there). Either the user asks, **or you recommend it** when a fusion / bespoke direction genuinely serves the deck better than a single preset (a recommendation the user confirms, like every lock). The *kind* doesn't matter → `mode: custom` + a `mode_behavior:` paragraph that **crystallizes the intent** (act sequence or posture shifts, title voice, page rhythm, register) concretely enough for the Executor to follow per page; it reads only `spec_lock.md`, never the chat. One deck locks **one** value — a fusion is one `custom` describing the acts, never several modes. Avoid only the *dodge*: don't default to `custom` when a preset genuinely fits, and prefer a dominant mode + page-level variation when one mode leads. - A bespoke direction the five don't give — a nameable cadence (dialectic 正反合, myth-vs-reality, countdown, Socratic), a multi-act fusion of modes, or the user's own feel (confrontational here, detached there). Either the user asks, **or you recommend it** when a fusion / bespoke direction genuinely serves the deck better than a single preset (a recommendation the user confirms, like every lock). The *kind* doesn't matter → `mode: custom` + a `mode_behavior:` paragraph that **crystallizes the intent** (act sequence or posture shifts, title voice, page rhythm, register) concretely enough for the Executor to follow per page; it reads only `spec_lock.md`, never the chat. If the direction uses existing modes, read every corresponding `modes/<id>.md` before synthesis and retain those exact ids as its catalog basis; if it is genuinely new, do not invent a basis. One deck locks **one** value — a fusion is one `custom` describing the acts, never several modes. Avoid only the *dodge*: don't default to `custom` when a preset genuinely fits, and prefer a dominant mode + page-level variation when one mode leads.
- No user structure or cadence → recommend from the confirmed `communication_intent`, `audience_outcome`, source texture, and delivery context using the index's auto-selection table. Composite intent does not automatically require `custom`: choose the dominant spine of the body pages when one exists; use a concrete `custom` act sequence only when no single spine can serve the stated priority / sequence. Present as a recommendation; the user may override. - No user structure or cadence → recommend from the confirmed `communication_intent`, `audience_outcome`, source texture, and delivery context using the index's auto-selection table. Composite intent does not automatically require `custom`: choose the dominant spine of the body pages when one exists; use a concrete `custom` act sequence only when no single spine can serve the stated priority / sequence. Present as a recommendation; the user may override.
Record the confirmed mode and rationale in `design_spec.md` first, then project `- mode:` to `spec_lock.md` (for `custom`, also project the sibling `- mode_behavior:` paragraph). Executor loads only that one mode file, or follows `mode_behavior` when the value is `custom`. Record the confirmed mode and rationale in `design_spec.md` first, including the exact catalog basis when a selected custom uses one. Then project `- mode:` to `spec_lock.md`; for `custom`, also project `- mode_behavior:` and, only when catalog material is actually used, `- mode_references: <id>, <id>`. Executor reads one file for a preset. For `custom`, it reads every listed reference before applying the behavior; an unreferenced novel custom follows the behavior directly.
#### Layer 2 — Visual style #### Layer 2 — Visual style
@@ -131,7 +145,7 @@ The deck's **visual aesthetic** — shape language, decoration density, whitespa
**Carries no color.** A visual style governs how the deck's HEX (locked at `e`) is *used* — never which colors, same discipline as [`image-renderings`](./image-renderings/_index.md). When the deck has AI images, prefer the style's paired rendering so layout and illustration share one aesthetic. **Carries no color.** A visual style governs how the deck's HEX (locked at `e`) is *used* — never which colors, same discipline as [`image-renderings`](./image-renderings/_index.md). When the deck has AI images, prefer the style's paired rendering so layout and illustration share one aesthetic.
Record the confirmed visual style and rationale in `design_spec.md` first, then project `- visual_style:` to `spec_lock.md`. Executor loads only that one visual-style file. Record the confirmed visual style and rationale in `design_spec.md` first, including the exact catalog basis when a selected custom uses one. Then project `- visual_style:` to `spec_lock.md`; for `custom`, also project `- visual_style_behavior:` and, only when catalog material is actually used, `- visual_style_references: <id>, <id>`. Executor reads one file for a preset. For `custom`, it reads every listed reference before applying the behavior; an unreferenced novel custom follows the behavior directly.
**Conditional template workspace**: When Generate Step 3 installed an explicit workspace path into `<project_path>/templates/`, read [`strategist-template.md`](./strategist-template.md) before completing Stage 2. It owns the editable natural-language application plan, confirmed-value consumption, AI-authored prototype selection, internal reuse/adherence derivation, inherited design precedence, and structured-lock planning. Bare names, style words, and free-design projects do not trigger it. **Conditional template workspace**: When Generate Step 3 installed an explicit workspace path into `<project_path>/templates/`, read [`strategist-template.md`](./strategist-template.md) before completing Stage 2. It owns the editable natural-language application plan, confirmed-value consumption, AI-authored prototype selection, internal reuse/adherence derivation, inherited design precedence, and structured-lock planning. Bare names, style words, and free-design projects do not trigger it.
@@ -139,22 +153,11 @@ Record the confirmed visual style and rationale in `design_spec.md` first, then
### e. Color Scheme Recommendation ### e. Color Scheme Recommendation
**Hard rule**: User-specified colors are truth. Lock supplied HEX, brand colors, or natural-language directives; templates follow inherited-design precedence. Even direct locks fill all six roles (`background`, `secondary_bg`, `primary`, `accent`, `secondary_accent`, `body_text`) in each of ≥3 directions: repeat fixed roles and vary only open ones. Never emit an empty palette. Only without user/template colors use the table below. **Hard rule**: User-specified colors are truth. Lock supplied HEX, brand colors, or natural-language directives; templates follow inherited-design precedence. Even direct locks fill all six roles (`background`, `secondary_bg`, `primary`, `accent`, `secondary_accent`, `body_text`) in each of ≥3 directions: repeat fixed roles and vary only open ones. Never emit an empty palette. Keep body-text contrast at least 4.5:1 and preserve confirmed/brand semantic roles.
Proactively provide a color scheme (HEX values) based on content characteristics and industry. **Reference — not a constraint**: Without user/template colors, propose project-specific directions from content and style. `scripts/config.py` industry colors and dominant/support/accent hierarchy are recall aids, never default locks, ratios, or color-count quotas.
**Industry color quick reference** (full 14-industry list in `scripts/config.py` under `INDUSTRY_COLORS`): **Lock recurring semantic anchors, not every possible paint.** Add the neutral roles already known to recur across the deck—such as `surface`, `grid`, `scrim`, `overlay`, or `block-shade`—when the visual style and page plan establish a stable meaning for them. Do not try to predict every page-local tint, gradient stop, shadow/glow color, transparency composite, or one-off illustration tone. Those values are chosen from page context during execution; promote one into `spec_lock.colors` only when it becomes a reusable named role.
| Industry | Primary Color | Characteristics |
|----------|--------------|-----------------|
| Finance / Business | `#003366` Navy Blue | Stable, trustworthy |
| Technology / Internet | `#1565C0` Bright Blue | Innovative, energetic |
| Healthcare / Health | `#00796B` Teal Green | Professional, reassuring |
| Government / Public Sector | `#C41E3A` Red | Authoritative, dignified |
**Color rules**: 60-30-10 rule (primary 60%, secondary 30%, accent 10%); text contrast ratio >= 4.5:1; no more than 4 colors per page.
**Lock the full neutral set the visual style implies** — not just primary / secondary / accent / border. Predict the extra neutral tiers the locked `visual_style` (§d Layer 2) needs and lock them now; `spec_lock.colors` must be complete before generation, and the Executor draws only from it (never invents a tone mid-deck).
| Style trait | Extra neutral tiers to lock | | Style trait | Extra neutral tiers to lock |
|---|---| |---|---|
@@ -177,7 +180,7 @@ See [`../templates/icons/README.md`](../templates/icons/README.md) for the curre
> **Mandatory rules when choosing C**: > **Mandatory rules when choosing C**:
> >
> **At the Strategist confirmation stage — decide the library only. Do NOT run `ls | grep` yet.** > **At the Strategist confirmation stage — decide the library and stroke only; resolve and sync filenames after approval.**
> >
> 1. **Pick exactly one stylistic library** — read the source material, then choose the library whose visual character best serves the deck: > 1. **Pick exactly one stylistic library** — read the source material, then choose the library 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
@@ -190,30 +193,32 @@ See [`../templates/icons/README.md`](../templates/icons/README.md) for the curre
> >
> **After the Strategist confirmation stage is approved — when writing `design_spec.md` §VI / `spec_lock.md`**, then materialize the icon inventory: > **After the Strategist confirmation stage is approved — when writing `design_spec.md` §VI / `spec_lock.md`**, then materialize the icon inventory:
> >
> 3. Enumerate the concepts the deck actually needs (home, chart, users, …) based on the confirmed outline. > 3. Enumerate only the concepts required by the confirmed outline.
> 4. Search for each concept's filename in the chosen library: `ls skills/ppt-master/templates/icons/<chosen-library>/ | grep <keyword>` > 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. Use the verified filename (without `.svg`) as the icon name; always include the library prefix (e.g., `chunk-filled/home`). Icon identifiers are case-sensitive: bundled-library basenames are lowercase and MUST be copied exactly (`tabler-outline/award`, never `tabler-outline/Award`). Do not rely on downstream lowercasing; custom icons preserve their file's exact case. > 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. **Copy each chosen icon into the project as you confirm it**`python3 skills/ppt-master/scripts/icon_sync.py <project_path> <lib/name> [<lib/name> …]`. This populates `<project>/icons/<lib>/` (the set the Executor embeds from) and, more importantly, **validates existence on the spot**. > 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. List the final icon inventory and chosen library in `design_spec.md` §VI; record the same in `spec_lock.md icons` (including `stroke_width` for stroke-style libraries). Executor may only use icons from this list. > 7. Record the successful inventory, library, and stroke-library `stroke_width` in `design_spec.md` §VI and `spec_lock.md icons`. Executor may use only this list.
> >
> 🚧 **GATE — missing icon = re-pick now**: if `icon_sync.py` reports any name as missing (non-zero exit), that icon is not in the library re-pick a real filename via `ls … | grep`, fix `§VI` / `spec_lock.md`, and re-run until it exits clean. Never carry a missing icon forward to generation. Over-copying candidates is harmless — finalize embeds only the icons actually referenced by `<use data-icon>`. > 🚧 **GATE — missing icon = re-pick now**: on non-zero exit, search only the missing concept in the chosen library, re-pick, and rerun the final batch until clean. Never carry a missing icon forward or switch stylistic libraries to fill the gap.
> >
> **Do NOT preload any index file** — when the inventory step arrives, use `ls | grep` to search on demand with zero token cost. > **Default — targeted lookup only**: do not load or rebuild a full index; search only unresolved concepts.
### g. Typography Plan Confirmation (Font + Size) ### g. Typography Plan Confirmation (Font + Size)
🚧 **GATE**: Read the locked visual-style file's §2 Typography character before recommending type. For a custom style, use its `visual_style_behavior`. The title carries the character; the body may remain neutral. 🚧 **GATE**: Read the locked preset visual-style file's §2 Typography character before recommending type. For a custom style, first read every file in `visual_style_references` when present, then resolve their typography character under `visual_style_behavior`; a novel custom uses the behavior directly. The title carries the character; the body may remain neutral.
**Family selection**: **Family selection**:
- User or active template typography is authoritative. Otherwise present two coherent choices: one concord (safe) and one contrast (more tension). Do not pair title/body families that are merely near-duplicates. - User or active template typography is authoritative. Otherwise present two coherent choices: one concord (safe) and one contrast (more tension). Do not pair title/body families that are merely near-duplicates.
- Every Stage-2 direction carries `heading` / `body` `cjk`, `latin`, `css`, and positive `body_size`; repeat user/template-fixed stacks. - Every Stage-2 direction carries `heading` / `body` `cjk`, `latin`, `css`, and positive `body_size`; repeat user/template-fixed stacks.
- Exported faces must resolve to fonts available in PowerPoint. Safe anchors are CJK `Microsoft YaHei` / `SimHei` / `SimSun` / `FangSong` / `KaiTi`; Latin sans `Arial` / `Calibri` / `Segoe UI`; Latin serif `Times New Roman` / `Georgia` / `Cambria`; mono `Consolas`; display `Impact` / `Arial Black`. - Exported faces must resolve to fonts available in PowerPoint. Safe anchors are CJK `Microsoft YaHei` / `SimHei` / `SimSun` / `FangSong` / `KaiTi`; Latin sans `Arial` / `Calibri` / `Segoe UI`; Latin serif `Times New Roman` / `Georgia` / `Cambria`; mono `Consolas`; display `Impact` / `Arial Black`. Executor may sparsely use another export-safe family on short non-structural display/ornament; never on title/body/data/annotation roles. Recurrence requires upstream selection.
- Keep each stack to four families or fewer. A non-installed brand or web face is legal only when the Design Spec explicitly records the install / embed requirement and a safe substitute. - Keep each stack to four families or fewer. A non-installed brand or web face is legal only when the Design Spec explicitly records the install / embed requirement and a safe substitute.
- Avoid splitting roles across near-equivalents such as YaHei↔PingFang, SimSun↔Songti, Arial↔Helvetica↔Segoe UI, or Times New Roman↔Times. A cross-platform counterpart may remain inside one fallback stack. - Avoid splitting roles across near-equivalents such as YaHei↔PingFang, SimSun↔Songti, Arial↔Helvetica↔Segoe UI, or Times New Roman↔Times. A cross-platform counterpart may remain inside one fallback stack.
- Choose by the locked style: serif for editorial / data-journalism, display weight for brutalist / poster directions, KaiTi or FangSong for ink character, mono accents for dark-tech / blueprint, and restrained sans for swiss-minimal / soft-rounded. - Choose by the locked style: serif for editorial / data-journalism, display weight for brutalist / poster directions, KaiTi or FangSong for ink character, mono accents for dark-tech / blueprint, and restrained sans for swiss-minimal / soft-rounded.
**Size lock — px only**: Every authoring layer carries bare px numbers. PowerPoint's displayed pt is an export result (`px × 0.75`), never an input or confirmation value. **Strategist-owned role extension after confirmation**: Confirm UI keeps the heading/body choice unchanged. While authoring the complete §IX roster and §IV typography plan, scan the actual content for recurring roles that materially need a different family for character or legibility—such as `annotation`, `footer`, `footnote`, `data`, `emphasis`, `quote`, or `code`. Add a lowercase snake_case role and exact stack only when it recurs; inherited roles and one-off garnish stay omitted. The extension must remain coherent with the confirmed heading/body system and locked visual style, and it does not reopen confirmation. Record one compact `Role rationale` in §IV stating the added roles and why, or that no additional family role is justified.
**Size anchors — px only**: Every authoring layer carries bare px numbers. PowerPoint's displayed pt is an export result (`px × 0.75`), never an input or confirmation value.
| Reading mode on PPT | Initial body | Information posture | | Reading mode on PPT | Initial body | Information posture |
|---|---:|---| |---|---:|---|
@@ -221,7 +226,7 @@ See [`../templates/icons/README.md`](../templates/icons/README.md) for the curre
| `balanced` | 24 | mixed reading + presentation | | `balanced` | 24 | mixed reading + presentation |
| `presentation` | 32 | projected / sparse | | `presentation` | 32 | projected / sparse |
Other canvases use the body baseline in [`canvas-formats.md`](canvas-formats.md). The confirmed visible values always win: take Confirm UI `body_size` / `sizes` verbatim; a manually edited role remains pinned, and changing canvas does not secretly rescale it. Other canvases use the body baseline in [`canvas-formats.md`](canvas-formats.md). The confirmed role-anchor values always win: take Confirm UI `body_size` / `sizes` verbatim as anchors; a manually edited anchor remains pinned, and changing canvas does not secretly rescale it.
| Recurring role | Ratio to body | | Recurring role | Ratio to body |
|---|---:| |---|---:|
@@ -234,7 +239,7 @@ Other canvases use the body baseline in [`canvas-formats.md`](canvas-formats.md)
| Annotation | 0.70.85× | | Annotation | 0.70.85× |
| Footnote / page number | 0.50.65× | | Footnote / page number | 0.50.65× |
Scan §IX before locking. Declare every recurring role, including `lead`, `footnote`, and chart annotations when used; a lead is always at least body size. One role has one deck-wide size. Snap derived values to clean even px (for body 24, a sound set is title 42, subtitle 32, lead 30, annotation 18, footnote 16). Feature elements may exceed the normal bands only through an explicit named slot. Scan §IX before locking. Declare every recurring role, including `lead`, `footnote`, and chart annotations when used; a lead is always at least body size. Give each role one deck-wide anchor and snap derived anchors to clean even px (for body 24, a sound set is title 42, subtitle 32, lead 30, annotation 18, footnote 16). Executor may vary one occurrence within that role's anchor ±2px while preserving hierarchy and readability. A new semantic role or any size outside ±2px requires an explicit named slot; feature elements that need a larger departure are planned here rather than improvised downstream.
#### Formula Planning Trigger #### Formula Planning Trigger
Formula policy and formula-asset planning are conditional. If the source contains formula-worthy expressions, or the user explicitly requests formula handling, read [`strategist-image.md`](./strategist-image.md) §3 before confirming the production policy or writing formula rows. Load it even when `image_usage` is `none`; otherwise omit formula planning from the core path. Formula policy and formula-asset planning are conditional. If the source contains formula-worthy expressions, or the user explicitly requests formula handling, read [`strategist-image.md`](./strategist-image.md) §3 before confirming the production policy or writing formula rows. Load it even when `image_usage` is `none`; otherwise omit formula planning from the core path.
@@ -282,14 +287,13 @@ python3 skills/ppt-master/scripts/chart_recall.py recall \
--limit 6 --limit 6
``` ```
The command reads the live `charts_index.json` and returns positive-scoring candidates up to the requested 38 limit, plus an explicit `no-template-match` option. It never pads the shortlist with zero-score keys; zero positive matches means use the fallback. Do not load the full catalog into the prompt. The command returns a bounded shortlist plus `no-template-match`. Read it unfiltered: `--limit` already bounds output, while `tail` / `head` / `grep` can hide higher-ranked candidates. `confidence` is diagnostic only. Semantically review the candidates; if terminology or structural ambiguity suggests a missed catalog structure, rerun with `--semantic-fallback` and compare its rules. This is optional, not a routine no-match gate. Do not open a second index.
**Selection**: **Selection**:
1. Inspect every returned `Pick for` / `Skip if` summary against the page; prefer the most specific valid structure. 1. Choose the most specific valid structure from the bounded candidates or an explicitly requested semantic fallback; keep one primary visualization per page and adapt its treatment rather than mimicking it.
2. Keep one primary visualization per page. Adapt its composition, density, colors, and decoration to the page; do not mimic blindly. 2. When no recalled reference fits, retain `no-template-match` by Strategist judgment: data content falls back to a table, permitted conceptual content to an AI image, and structural content to a custom layout. Record the chosen fallback only in the affected page's §IX `Visualization` / `Layout`; do not serialize the negative result into §VII.
3. If every candidate conflicts, choose `no-template-match`: data-driven content falls back to a table, conceptual/illustrative content to an AI image when the confirmed image source permits it, and structural content to a custom layout. 3. Validate all selected keys before writing the lock:
4. Validate all selected keys before writing the lock:
```bash ```bash
python3 skills/ppt-master/scripts/chart_recall.py validate <key> [<key> ...] python3 skills/ppt-master/scripts/chart_recall.py validate <key> [<key> ...]
@@ -297,7 +301,9 @@ python3 skills/ppt-master/scripts/chart_recall.py validate <key> [<key> ...]
A failed validation must be corrected with a recalled key. `no-template-match` is not a key and never appears in `page_charts`. A failed validation must be corrected with a recalled key. `no-template-match` is not a key and never appears in `page_charts`.
**Section VII audit**: Use one combined table. Copy the selected candidate's returned `summary` verbatim into `Summary-quote`; record its returned path and page-specific usage. List real returned runners-up with page-specific rejection reasons. If no candidate fits, record `no-template-match`, the fallback, and why. **Section VII audit**: §VII is a positive reference inventory. Include it only when at least one catalog candidate is selected. Every row copies the selected candidate's returned `summary` verbatim into `Summary-quote` and records its real path plus page-specific usage. List real returned runners-up only for pages with a selected reference. Never write an empty §VII, a `no-template-match` / `n/a` row, or prose saying no reference exists; a no-match page is described only in its §IX block.
**Native-ready boundary**: For every independent data chart or pure text-grid table, add `Native-ready: yes|no` to its §IX page block. Choose `yes` only when the confirmed requirement or artifact afterlife benefits from an editable native data object; otherwise keep the designed SVG with `no`. Conceptual rows and incidental sparklines, KPI trends, or insets omit the field; Executor never promotes them.
``` ```
| Page | Template | Path | Summary-quote (verbatim) | Usage | | Page | Template | Path | Summary-quote (verbatim) | Usage |
@@ -308,7 +314,7 @@ Runners-up considered:
- <returned_key> | rejected for P03: <page-specific reason> - <returned_key> | rejected for P03: <page-specific reason>
``` ```
**Flag native-preset candidates**: For any §VII row, including `no-template-match`, append a `Usage` note when the content calls for a literal stock PowerPoint chevron, block arrow, standard flowchart node, callout, banner, or star. Executor still decides the exact preset under its native-shape branch. **Flag native-preset candidates**: In the affected page's §IX `Layout` / `Visualization`, note when the content calls for a literal stock PowerPoint chevron, block arrow, standard flowchart node, callout, banner, or star. Executor still decides the exact preset under its native-shape branch; this note never creates a §VII row by itself.
### Speaker Notes Requirements (Default — no discussion needed) ### Speaker Notes Requirements (Default — no discussion needed)
@@ -325,15 +331,15 @@ Confirmation `d` locks two independent catalog items:
- **Mode** — narrative skeleton: [`modes/_index.md`](./modes/_index.md) → `pyramid` / `narrative` / `instructional` / `showcase` / `briefing`. - **Mode** — narrative skeleton: [`modes/_index.md`](./modes/_index.md) → `pyramid` / `narrative` / `instructional` / `showcase` / `briefing`.
- **Visual style** — aesthetic: [`visual-styles/_index.md`](./visual-styles/_index.md) → presets + `custom`. - **Visual style** — aesthetic: [`visual-styles/_index.md`](./visual-styles/_index.md) → presets + `custom`.
Read the relevant `_index.md` at confirmation `d` (Layer 1 / Layer 2) for its catalog table and auto-selection. Executor loads the locked mode + visual-style files at generation (see [`generate-pptx`](../workflows/generate-pptx.md) Step 6). Read the relevant `_index.md` at confirmation `d` (Layer 1 / Layer 2) for its catalog table and auto-selection. Executor loads one locked file per preset, or every exact custom reference before applying its behavior (see [`generate-pptx`](../workflows/generate-pptx.md) Step 6).
--- ---
## 3. Color Selection Reference ## 3. Color Selection Reference
Do not start from a universal palette. Precedence is user / brand values → active template inheritance → the industry anchors in `scripts/config.py` → a project-specific proposal that realizes the locked visual style. Keep body-text contrast at least 4.5:1 and normally use no more than four chromatic colors on one page. Do not start from a universal palette. Precedence is user / brand → active template → project-specific proposal; `scripts/config.py` industry anchors are optional recall. Keep body-text contrast at least 4.5:1; color count and distribution follow encoding, style, and natural assets, not a quota.
Lock the complete role set the style needs, including neutrals such as `surface`, `grid`, `scrim`, `overlay`, or `block-shade`; Executor must not invent a missing tone. For data semantics, use coherent positive / warning / negative ramps rather than unrelated accents. Lock the stable role set the deck needs, including recurring neutrals such as `surface`, `grid`, `scrim`, `overlay`, or `block-shade`. These are identity anchors, not an exhaustive paint list. Executor may derive tints, shades, alpha, gradients, and effects, preserve necessary natural asset colors, and add sparse page-local accents for differentiation or ornament. Such accents must not form a competing/recurring palette; Strategist owns reusable positive / warning / negative roles.
--- ---
@@ -362,7 +368,7 @@ Free-design patterns are starting points, not quotas. Adjust composition, spacin
### 6.1 Content Planning Strategy ### 6.1 Content Planning Strategy
Content-outline and speaker-notes strategy follow the deck's locked **mode** — see [`modes/_index.md`](./modes/_index.md) and the locked mode's file. The guidance below applies within any mode: Content-outline and speaker-notes strategy follow the deck's locked **mode** — see [`modes/_index.md`](./modes/_index.md), then the locked preset file or every listed custom reference plus its behavior. The guidance below applies within any mode:
**Reading mode controls information carriage, not communication intent.** `result.json delivery_purpose` is retained as the compatibility key for `text` (read-close) / `balanced` (business, default) / `presentation`, confirmed with the complete deck solution in Stage 2. It decides how meaning is divided among the page, visuals, presenter, and notes. The body baseline (§g) is one consequence, not the definition: **Reading mode controls information carriage, not communication intent.** `result.json delivery_purpose` is retained as the compatibility key for `text` (read-close) / `balanced` (business, default) / `presentation`, confirmed with the complete deck solution in Stage 2. It decides how meaning is divided among the page, visuals, presenter, and notes. The body baseline (§g) is one consequence, not the definition:
@@ -374,42 +380,44 @@ Content-outline and speaker-notes strategy follow the deck's locked **mode** —
**Recommendation signals**: derive the initial reading mode from the confirmed `audience`, `delivery_context`, and `artifact_afterlife`. Asynchronous review, reference, approval, audit, and leave-behind use lean `text`; presenter-led projection, large-room delivery, launch, or classroom explanation lean `presentation`; hybrid review / roadshow use leans `balanced`. When live projection and durable afterlife both matter, recommend `balanced` unless the contract clearly prioritizes one. If the user confirms `presentation`, support afterlife through notes, appendix pages, captions, and visible sources instead of crowding every slide. **Recommendation signals**: derive the initial reading mode from the confirmed `audience`, `delivery_context`, and `artifact_afterlife`. Asynchronous review, reference, approval, audit, and leave-behind use lean `text`; presenter-led projection, large-room delivery, launch, or classroom explanation lean `presentation`; hybrid review / roadshow use leans `balanced`. When live projection and durable afterlife both matter, recommend `balanced` unless the contract clearly prioritizes one. If the user confirms `presentation`, support afterlife through notes, appendix pages, captions, and visible sources instead of crowding every slide.
**Per-block expression**: let the semantic relationship choose the form. Causal explanation, argument, interpretation, and narrative continuity use prose. Truly parallel, ordered, or enumerable items may use bullets / numbers. Never create bullets merely because copy is long or a template exposes a list slot. In `presentation`, distill one assertion and move its explanation into notes rather than turning every sentence into a fragment. Source texture remains a secondary cue: an article / transcript / talk leans prose, while a data sheet or inventory may lean structured labels. Write the final phrasing into §IX itself; do not leave skeleton points for Executor to expand. **Per-block expression**: let the semantic relationship choose the form. Causal explanation, argument, interpretation, and narrative continuity use prose. Truly parallel, ordered, or enumerable items may use bullets / numbers. Never create bullets merely because copy is long or a template exposes a list slot. In `presentation`, distill one assertion and move its explanation into notes rather than turning every sentence into a fragment. Source texture remains a secondary cue: an article / transcript / talk leans prose, while a data sheet or inventory may lean structured labels. Write complete, usable phrasing into §IX; do not leave skeletons for Executor. It is preferred wording unless literal preservation applies.
This is what makes the axis meaningful: a `presentation` deck and a `text` deck built from the **same source and communication contract** must differ in page grammar, page count recommendation, per-page text volume, visual burden, layout density, rhythm, and notes—not only in font size. Page count stays the user's call; reading mode informs the recommendation when the user has not fixed one. Record it as **Reading Mode** in `design_spec.md §I` (compatibility key `delivery_purpose`, lock key `consumption_mode`). Separately, `communication_intent` / `audience_outcome` determine what the outline must accomplish, while `delivery_context` and `artifact_afterlife` help select the reading mode and still remain independent constraints after selection. The `page_rhythm` leans are a bias, not a quota. Preservation paths keep source wording and structure verbatim: honor reading mode only in styling and notes, never by rephrasing or re-paginating. This is what makes the axis meaningful: a `presentation` deck and a `text` deck built from the **same source and communication contract** must differ in page grammar, page count recommendation, per-page text volume, visual burden, layout density, rhythm, and notes—not only in font size. Page count stays the user's call; reading mode informs the recommendation when the user has not fixed one. Record it as **Reading Mode** in `design_spec.md §I` (compatibility key `delivery_purpose`, lock key `consumption_mode`). Separately, `communication_intent` / `audience_outcome` determine what the outline must accomplish, while `delivery_context` and `artifact_afterlife` help select the reading mode and still remain independent constraints after selection. The `page_rhythm` leans are a bias, not a quota. Preservation paths keep source wording and structure verbatim: honor reading mode only in styling and notes, never by rephrasing or re-paginating.
> Note: §IX is the content copy projected into each Executor page-context — what you write there is what survives context compression. > Note: §IX is the complete page brief projected into each Executor page-context — what you write there is what survives context compression.
### 6.2 Planning Artifact Content ### 6.2 Planning Artifact Content
Generate Step 4 owns both artifact scaffolds. `design_spec.md` is the Strategist's human-readable design decision; `spec_lock.md` is its machine-readable execution projection. Author them in that order. Never treat the two files as parallel interpretations of `result.json`, and never let lock authoring become a second design pass. Generate Step 4 owns this reference-first sequence. `design_spec.md` is the Strategist's complete human-readable design decision; `spec_lock.md` is the context-selected execution subset and routing contract. `result.json` is read once into the active final-confirmation state and consumed completely while writing the Design Spec. Never reopen it to author the lock, and never treat the two planning files as parallel interpretations of the confirmation.
1. Re-read the complete final confirmation state. 1. Use the retained complete final-confirmation state already read once by Generate Step 4, then read `templates/design_spec_reference.md`.
2. Write the full `design_spec.md` from that state plus source analysis. In §IX, each page carries layout, title, one core message, an **Audience move**, final content wording, applicable visualization/image references, `Fact IDs` for sourced claims, and `Data class: scenario` for invented demonstration data. 2. Compose the whole Design Spec in active context before touching the target path. Create `design_spec.md` once from the schema marker through §X; do not copy a scaffold into the project or patch placeholder fields. Record production mechanics in §I. In §IX, create the complete ordered roster; each entry carries layout, title, core message, **Audience move**, final wording, visualization/image references, sourced `Fact IDs`, and `Data class: scenario` for invented demo data. After Gate 1, roster ids/count/order and content are authoritative; layout, cover/closing composition, and image/chart patterns remain References unless promoted.
3. Compare `design_spec.md` against the final confirmation field by field. Repair every omission or deviation before creating `spec_lock.md`. 3. Compare `design_spec.md` against the final confirmation field by field. Repair every omission or deviation before authoring `spec_lock.md`.
4. Derive `spec_lock.md` only from the completed Design Spec. Project the exact values needed by Executor without adding a new recommendation, preference, or interpretation. 4. After Gate 1, read `templates/spec_lock_reference.md`. Compose the whole lock in active context from the completed Design Spec plus current execution context, then create `spec_lock.md` once. Retain confirmed identity anchors, select stable cross-page roles and routing values, omit page-local values that need no reusable name, and do not reopen final evidence. This is implementation judgment, not a second user-facing recommendation.
**Final confirmation → Design Spec consumption map**: **Final confirmation → Design Spec consumption map**:
| Confirmed state | Required Design Spec realization | | Confirmed state | Required Design Spec realization |
|---|---| |---|---|
| Communication contract and `content_divergence` | §I records the confirmed contract; §IX realizes every stated purpose, outcome, priority, and source-treatment constraint | | Communication contract and `content_divergence` | §I records the confirmed contract; §IX realizes every stated purpose, outcome, priority, and source-treatment constraint |
| Canvas, reading mode, and page count | §III record the confirmed values; §IX page count and page grammar obey them | | 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 exactly and use it throughout the layout and visual plan | | 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 every visible role size | §IV records the confirmed families and exact `body`, `title`, `subtitle`, and `annotation` values; never re-derive a confirmed size | | Typography, including Strategist-derived recurring family overrides and every visible role size | §IV records the confirmed heading/body stacks, any recurring support-role stacks justified by §IX, and exact `body`, `title`, `subtitle`, and `annotation` anchor values; 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 uses the confirmed library or confirmed no-icon/custom path |
| Every confirmed non-`none` image source, `image_notes`, and AI strategy | §VIII contains at least one matching resource row per source; explicit page roles and intent appear in §VIII and the affected §IX pages | | 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 |
| Formula policy, AI-image acquisition path, generation mode, refine-spec toggle | Their owning Generate stage consumes them; formula policy also shapes §VIII when formula-worthy content exists | | Formula policy, AI-image acquisition path, generation mode, refine-spec toggle | §I records them as production mechanics; their owning Generate stage consumes the Design Spec, and formula policy also shapes §VIII when formula-worthy content exists |
**GATE 1 — confirmation fidelity.** Do not create or fill `spec_lock.md` until the complete Design Spec has passed the field-by-field comparison above. A missing, changed, substituted, or weakened confirmed value blocks Step 4 even when the Design Spec schema validates. Schema validity proves structure, not fidelity to the user's decision. **GATE 1 — confirmation fidelity.** Do not create or fill `spec_lock.md` until the complete Design Spec has passed the field-by-field comparison above. A missing or substituted value, or a silently strengthened/weakened semantic type, blocks Step 4 even when the Design Spec schema validates. Adapting a Reference within its owner-defined bounds or leaving an unused Permission unmaterialized is not a fidelity failure. Schema validity proves structure, not fidelity to the user's decision.
**GATE 2 — lock projection fidelity.** After the Design Spec passes Gate 1, project its machine-relevant decisions into `spec_lock.md`. The lock may normalize syntax for its schema, but it must not change meaning or introduce an independent choice. If a projection exposes a contradiction or missing decision, return to Gate 1, repair the Design Spec from the final confirmation, and regenerate the affected lock rows. **GATE 2 — lock context fidelity.** After the Design Spec passes Gate 1, author its machine-relevant execution anchors and routing values into `spec_lock.md`. The lock may normalize syntax and add named recurring implementation roles justified by the Design Spec/page plan, but it must not change confirmed identity or introduce a competing direction. It is intentionally not a field-for-field copy and not a whitelist of every legal SVG value. If authoring exposes a contradiction or missing confirmed decision, return to Gate 1 and repair the Design Spec from the retained final-confirmation state; on a fresh recovery turn only, read the persisted final result once to restore that state.
**Execution lock content**: `spec_lock.md` is the compact machine projection of the completed Design Spec for communication execution, colors, typography, icons, images, page rhythm, chart choices, and route-specific PowerPoint structure. Project every recurring typography size into its named role; do not collapse a confirmed `subtitle` or `annotation` value back into a derived default. Project every §VIII image row with its acquisition source so downstream routing cannot infer a different one. Do not copy planning-only context or decision provenance into the lock. Free-design, brand-only, and `template_reuse_scope: style` routes write `pptx_structure.mode: flat`; the conditional template module owns every structured mapping. Executor rebuilds the lock's current-page projection before every page (see [executor-base.md](executor-base.md) §2.1). Never repair the Design Spec from the lock; repair the Design Spec from the final confirmation, then re-project the lock. **Execution lock content**: `spec_lock.md` compactly carries communication, stable color/type anchors, icons, images, page rhythm, chart choices, and route-specific PowerPoint structure. Name every recurring typography role and any planned feature role that needs to leave another role's ±2px band; never re-derive a confirmed anchor. New locks keep `font_family` as the body/default compatibility stack and also write explicit `title_family` + `body_family`; every additional recurring Design Spec role projects to `<role>_family`. Collapsing distinct Design Spec stacks into `font_family`, or dropping an extra role, fails Gate 2. Keep core fonts/palette roles stable; page authoring varies treatment and may add sparse local garnish. Project every placed §VIII image's source, pattern, and crop policy; omit unplaced sheets and planning provenance. Free-design, brand-only, and `template_reuse_scope: style` use `pptx_structure.mode: flat`; the template module owns structured mappings. Executor rebuilds page projection before every page ([executor-base.md](executor-base.md) §2.1). Repair the Design Spec only from retained final confirmation, then re-author affected lock rows.
**Contextual extension**: derived paint or sparse local font/color garnish may stay in one SVG while non-structural and non-recurring. New base/semantic colors, structural/recurring fonts, resources, or patterns require upstream repair; Executor never reverse-projects a choice as fact. Promote garnish upstream before reuse, regenerate page-context, and never add values to silence a comparison.
- **Communication trace is mandatory**: Keep the full confirmed communication contract in `design_spec.md §I`, then project only `audience`, `objective`, `core_message`, and canonical `consumption_mode` into `spec_lock.md communication`. Write `objective` as one concise execution sentence that preserves both the confirmed `communication_intent` and the success condition in `audience_outcome`; do not copy `delivery_context`, `artifact_afterlife`, dates, provenance, or conflict-resolution commentary into the lock. Before finalizing §IX, check that every named purpose has at least one outline obligation and **every Slide block**, including cover / divider / closing pages, has an `Audience move` that advances the global outcome. A page that advances no purpose or outcome should be merged, rewritten, or cut. `project_manager.py validate` and `svg_quality_checker.py` enforce the compact lock fields and per-page move presence, not their subjective quality. - **Communication trace is mandatory**: Keep the full confirmed communication contract in `design_spec.md §I`, then project only `audience`, `objective`, `core_message`, and canonical `consumption_mode` into `spec_lock.md communication`. Write `objective` as one concise execution sentence that preserves both the confirmed `communication_intent` and the success condition in `audience_outcome`; do not copy `delivery_context`, `artifact_afterlife`, dates, provenance, or conflict-resolution commentary into the lock. Before finalizing §IX, check that every named purpose has at least one outline obligation and **every Slide block**, including cover / divider / closing pages, has an `Audience move` that advances the global outcome. A page that advances no purpose or outcome should be merged, rewritten, or cut. `project_manager.py validate` and `svg_quality_checker.py` enforce the compact lock fields and per-page move presence, not their subjective quality.
- **Custom behavior is concise and executable**: For confirmed `custom` mode or visual style, project one resolved `mode_behavior` / `visual_style_behavior` sentence or short paragraph. Preserve the confirmed direction, reference locked role names such as `colors.primary` when needed, and omit selection history, contradictions, precedence explanations, or other Design Spec provenance. Page-context carries these fields directly to Executor. - **Custom behavior is concise and executable**: For confirmed `custom` mode or visual style, project one resolved `mode_behavior` / `visual_style_behavior` sentence or short paragraph. When the direction actually combines or borrows catalog entries, also project the exact, comma-separated `mode_references` / `visual_style_references`; omit the field for a genuinely novel direction and never fabricate a nearby reference. Preserve the confirmed direction, reference locked role names such as `colors.primary` when needed, and omit selection history, contradictions, precedence explanations, or other Design Spec provenance. Page-context carries these fields directly to Executor.
- **page_rhythm is mandatory**: Based on the page list in §IX Content Outline, assign each page one of `anchor` / `dense` / `breathing`. This is what breaks the uniform "every page is a card grid" feel. New locks may not omit the section; consumer omission behavior is owned by [`executor-base.md`](executor-base.md) §2.1. - **page_rhythm is mandatory**: Based on the page list in §IX Content Outline, assign each page one of `anchor` / `dense` / `breathing`. This is what breaks the uniform "every page is a card grid" feel. New locks may not omit the section; consumer omission behavior is owned by [`executor-base.md`](executor-base.md) §2.1.
- **Fact IDs and scenario labels are mandatory when applicable**: Read any `sources/*.facts.json`. For each §IX page, list the stable IDs actually used; never cite an ID whose claim is absent from the page. Mark invented KPIs/targets/internal ratios as `Data class: scenario` and state which values are scenario data. Executor carries external sources into notes/footnotes and renders a visible scenario label for scenario figures. - **Fact IDs and scenario labels are mandatory when applicable**: Read any `sources/*.facts.json`. For each §IX page, list the stable IDs actually used; never cite an ID whose claim is absent from the page. Mark invented KPIs/targets/internal ratios as `Data class: scenario` and state which values are scenario data. Executor carries external sources into notes/footnotes and renders a visible scenario label for scenario figures.
- **Rhythm follows narrative, not quota**: `breathing` pages mark natural pauses — chapter transitions, standalone emphasis (hero quote / big number), SCQA bridges. Dense decks may legitimately be all `dense`. **Do NOT invent filler pages** ("Thank you", empty dividers) to pad rhythm — every `breathing` page must say something independent. Consumption mode biases the overall lean (`presentation` toward more `anchor` / `breathing`, `text` toward `dense`; see §6.1) — a bias, never a quota. - **Rhythm follows narrative, not quota**: `breathing` pages mark natural pauses — chapter transitions, standalone emphasis (hero quote / big number), SCQA bridges. Dense decks may legitimately be all `dense`. **Do NOT invent filler pages** ("Thank you", empty dividers) to pad rhythm — every `breathing` page must say something independent. Consumption mode biases the overall lean (`presentation` toward more `anchor` / `breathing`, `text` toward `dense`; see §6.1) — a bias, never a quota.
@@ -419,13 +427,13 @@ Generate Step 4 owns both artifact scaffolds. `design_spec.md` is the Strategist
- **pptx_structure is mandatory**: Free-design, brand-only, and `template_reuse_scope: style` routes write `mode: flat`; a style-reference route may also record `template_reuse_scope: style` but omits every structure mapping and `template_adherence`. `template_reuse_scope: mirror|layout` writes `mode: structured` plus `template_adherence: strict|adaptive`. Do not write legacy `baseline`, `template`, `preserve`, `layout_strategy`, or Layout-kind rows into a new project. - **pptx_structure is mandatory**: Free-design, brand-only, and `template_reuse_scope: style` routes write `mode: flat`; a style-reference route may also record `template_reuse_scope: style` but omits every structure mapping and `template_adherence`. `template_reuse_scope: mirror|layout` writes `mode: structured` plus `template_adherence: strict|adaptive`. Do not write legacy `baseline`, `template`, `preserve`, `layout_strategy`, or Layout-kind rows into a new project.
- **Flat-route boundary**: With `mode: flat`, omit `pptx_masters`, `pptx_layouts`, `page_pptx_layouts`, and `page_layouts`. Do not plan native Master/Layout families or reusable placeholder slots. Every generated SVG object remains Slide-local: omit root Master/Layout identity, `data-pptx-layer`, and `data-pptx-placeholder*` metadata. Export materializes one clean project-owned Master plus one Blank Layout from the current color/typography lock, removes stock content placeholders/Layout inventory, and retains only the standard date/footer/slide-number capability hooks. - **Flat-route boundary**: With `mode: flat`, omit `pptx_masters`, `pptx_layouts`, `page_pptx_layouts`, and `page_layouts`. Do not plan native Master/Layout families or reusable placeholder slots. Every generated SVG object remains Slide-local: omit root Master/Layout identity, `data-pptx-layer`, and `data-pptx-placeholder*` metadata. Export materializes one clean project-owned Master plus one Blank Layout from the current color/typography lock, removes stock content placeholders/Layout inventory, and retains only the standard date/footer/slide-number capability hooks.
- **Structured template route**: When [`strategist-template.md`](./strategist-template.md) is active and reuse is `mirror|layout`, follow its complete Master/Layout/slot/prototype mapping rules. - **Structured template route**: When [`strategist-template.md`](./strategist-template.md) is active and reuse is `mirror|layout`, follow its complete Master/Layout/slot/prototype mapping rules.
- **page_charts (write only for chart pages that match a catalog template)**: For each page in `design_spec.md §VII` whose `reference template path` points to `templates/charts/<name>.svg`, add `P<NN>: <chart_name>`. Pages with `no-template-match` in §VII MUST NOT appear here (Executor would look for a non-existent reference). If the deck has no data-visualization pages, omit the section. - **page_charts (write only for pages with a selected catalog reference)**: For each page in `design_spec.md §VII` whose path points to `templates/charts/<name>.svg`, add `P<NN>: <chart_name>`. No-match pages never appear here because §VII omits them and Executor would otherwise look for a non-existent reference. If no catalog reference is selected, omit the section even when §IX contains custom data visualizations.
--- ---
## 7. Project Boundary ## 7. Project Boundary
The Generate route owns project initialization and supplies `<project_path>`. Strategist writes only the two scaffolded planning artifacts at that root plus the explicitly triggered resource manifests; it does not choose or create another project path. The Generate route owns project initialization and supplies `<project_path>`. Strategist writes only the two complete planning artifacts at that root plus the explicitly triggered resource manifests; it does not choose or create another project path.
--- ---
@@ -16,12 +16,15 @@ Use any compatible technique when it serves the locked visual style and content.
| Decision layer | Authority | | Decision layer | Authority |
|---|---| |---|---|
| Technical validity | Required / Forbidden / Conditional contracts in this file | | Technical validity | Required / Forbidden / Conditional contracts in this file |
| Project values | `<project_path>/spec_lock.md` colors, fonts, icons, and images | | Project values | `<project_path>/spec_lock.md` stable anchors plus the retained Design Spec and current page context |
| Aesthetic fit | Locked `visual_style` / `visual_style_behavior` | | Aesthetic fit | Locked `visual_style` / `visual_style_behavior` |
| Per-page choice | Content purpose, hierarchy, legibility, semantics, and rhythm | | Per-page choice | Content purpose, hierarchy, legibility, semantics, and rhythm |
**Hard rule — illustrative colors**: colors below demonstrate syntax only; **Reference — illustrative colors**: colors below demonstrate syntax only;
generated pages use matching `spec_lock.md` roles. Fidelity labels are defined generated pages choose paint from the locked identity anchors, visual style,
content semantics, and current composition. A contextual tint, gradient stop,
shadow/glow paint, or one-off display color need not already be a lock row;
promote it only when it becomes a recurring named role. Fidelity labels are defined
in [`shared-standards-core.md`](./shared-standards-core.md). Review an `Approximate` result in native PPTX in [`shared-standards-core.md`](./shared-standards-core.md). Review an `Approximate` result in native PPTX
when the effect carries material meaning. when the effect carries material meaning.
@@ -121,6 +124,9 @@ contract. See
[`conversion.md`](../scripts/docs/conversion.md#import-compatibility-and-recovery-boundary). [`conversion.md`](../scripts/docs/conversion.md#import-compatibility-and-recovery-boundary).
The quality checker and exporter preflight both validate definition location, The quality checker and exporter preflight both validate definition location,
references, gradient structure, and paint context from the same closed contract. references, gradient structure, and paint context from the same closed contract.
Gradient-stop colors are contextual paint values. Keep them coherent with the
deck anchors and page intent; they are not required to duplicate existing
`spec_lock.colors` literals.
**Hard rule — non-degenerate gradient geometry**: an `objectBoundingBox` **Hard rule — non-degenerate gradient geometry**: an `objectBoundingBox`
gradient stroke requires non-zero intrinsic width and height. SVG stroke width gradient stroke requires non-zero intrinsic width and height. SVG stroke width
@@ -11,15 +11,10 @@ Technical spec and workflow for adding images to SVG files.
Defined in the Design Specification & Content Outline; each image carries an `Acquire Via` field plus a status annotation. This file is authoritative for status names and SVG embedding behavior. If image approach includes "B) User-provided": run `analyze_images.py` right after the Strategist confirmation stage and complete the list before outputting the design spec. Defined in the Design Specification & Content Outline; each image carries an `Acquire Via` field plus a status annotation. This file is authoritative for status names and SVG embedding behavior. If image approach includes "B) User-provided": run `analyze_images.py` right after the Strategist confirmation stage and complete the list before outputting the design spec.
```markdown ```markdown
| Filename | Dimensions | Purpose | Type | Acquire Via | Status | Reference | | Filename | Dimensions | Purpose | Type | Layout pattern | Crop Policy | Acquire Via | Status | Reference |
|----------|------------|---------|------|-------------|--------|-----------| |----------|------------|---------|------|----------------|-------------|-------------|--------|-----------|
| cover_bg.png | 1280x720 | Cover background | Background | ai | Pending | Modern tech abstract, deep blue gradient | | team.jpg | 800x600 | Team photo | Photography | `#2 left-third` | adaptive | web | Pending | Diverse engineering team in modern office |
| team.jpg | 800x600 | Team photo | Photography | web | Pending | Diverse engineering team in modern office | | formula_001.png | 736x168 | Page 3 block equation | Latex Formula | formula | no-crop | formula | Rendered | `E = mc^2` |
| product.png | 600x400 | Page 3 product photo | Photography | user | Existing | - |
| formula_001.png | 736x168 | Page 3 block equation | Latex Formula | formula | Rendered | `E = mc^2` |
| chart.png | 600x400 | Page 5 placeholder | Illustration | placeholder | Placeholder | Team collaboration scene to be added later |
| spot_sheet.png | 1024x1024 | 2x2 spot illustration sheet, not placed | Illustration Sheet | ai | Pending | Four same-family spot illustrations on a clean grid |
| spot_team.png | TBD after slicing | Page 4 team spot illustration | Illustration | slice | Pending | From `spot_sheet.png` cell 1,1 |
``` ```
### Image Status Enum ### Image Status Enum
@@ -12,9 +12,9 @@ Executor finishes page → svg_quality_checker.py passes → visual_review.py re
If the static checker has not been run or has failed, the subagent must abort with status `prereq_failed` and not start the rubric. Topics already enforced by the static checker (do **not** re-check here): If the static checker has not been run or has failed, the subagent must abort with status `prereq_failed` and not start the rubric. Topics already enforced by the static checker (do **not** re-check here):
- font-size ramp drift (`RAMP_MIN_RATIO=0.5` / `MAX=5.0`) - font-size anchor drift (more than `2px` from every declared role anchor)
- id uniqueness, XML well-formed - id uniqueness, XML well-formed
- spec_lock drift (colors / fonts / canvas) - canvas/structural typography validation and informational spec-lock anchor comparison (contextual colors/fonts are allowed)
- animation_config compliance - animation_config compliance
## §0.1 Subagent inputs ## §0.1 Subagent inputs
@@ -40,7 +40,7 @@ The subagent reads inputs 24 **once** at the start of its turn, then iterates
| ~~H5~~ | Font-ramp drift | *covered by `svg_quality_checker.py` — see §0 prerequisites* | n/a (do not re-check) | | ~~H5~~ | Font-ramp drift | *covered by `svg_quality_checker.py` — see §0 prerequisites* | n/a (do not re-check) |
| H6 | Element collision | rect/circle/path bboxes overlap with z-order violating semantics | open spacing | | H6 | Element collision | rect/circle/path bboxes overlap with z-order violating semantics | open spacing |
| H7 | Anchored element displaced | page number / header / footer covered, missing, or out of canvas | restore to anchor position | | H7 | Anchored element displaced | page number / header / footer covered, missing, or out of canvas | restore to anchor position |
| H8 | Image rendering broken | `<image>` empty / broken-image / severe distortion | fix `href`, adjust `preserveAspectRatio`, add `no-crop` if face/data is cropped | | H8 | Image rendering broken | `<image>` empty / broken-image / severe distortion | fix `href`; for `adaptive`, choose `meet` or a safer crop; a new complete-display requirement returns to §VIII `Crop Policy` and lock projection |
| H9 | Missing key element | element required by `design_spec §IX` outline is absent from rendered slide | recreate from spec | | H9 | Missing key element | element required by `design_spec §IX` outline is absent from rendered slide | recreate from spec |
Detection order (run sequentially, do not parallelize within a single subagent): Detection order (run sequentially, do not parallelize within a single subagent):
@@ -85,7 +85,7 @@ Hard boundary, equal weight to §1.
- **Brand decisions** — color tokens, font families, geometry style (decided by `spec_lock.md` / brand directory) - **Brand decisions** — color tokens, font families, geometry style (decided by `spec_lock.md` / brand directory)
- **Layout restructure** — do not change column counts, replace chart types, add/remove sections - **Layout restructure** — do not change column counts, replace chart types, add/remove sections
- **Content** — do not add or remove copy; only adjust position, font-size (within ramp), spacing, letter-spacing, alignment, scrim - **Content** — do not add or remove copy; only adjust position, font-size (within the mapped role's anchor `±2px`), spacing, letter-spacing, alignment, scrim
- **Other files** — never edit `design_spec.md` / `spec_lock.md` / `animations.json` / `image_prompts.json` / `images/` / other pages' SVGs - **Other files** — never edit `design_spec.md` / `spec_lock.md` / `animations.json` / `image_prompts.json` / `images/` / other pages' SVGs
- **Atomicity** — one edit per fix, no bulk multi-element replacements - **Atomicity** — one edit per fix, no bulk multi-element replacements
@@ -2,7 +2,7 @@
A **visual style** is how the deck **looks** — shape language, decoration density, whitespace rhythm, typographic character, texture / elevation. Lock **one per deck**; it anchors the aesthetic of the SVG layout itself (cards, dividers, spacing, corner radius, shadow use). A **visual style** is how the deck **looks** — shape language, decoration density, whitespace rhythm, typographic character, texture / elevation. Lock **one per deck**; it anchors the aesthetic of the SVG layout itself (cards, dividers, spacing, corner radius, shadow use).
> **Styles carry NO HEX and lock no palette.** Color truth and role behavior live in `design_spec.colors` / `spec_lock.colors` (confirmation `e`). A visual style only describes how those existing colors are used in SVG composition—never which colors to substitute. Generated images follow the same single source of truth: their rendering comes from [`image-renderings/`](../image-renderings/), while their exact colors inherit the deck roles directly. [`image-palettes/`](../image-palettes/) is legacy compatibility material only. > **Styles carry NO fixed HEX and lock no palette.** Core color identity and recurring role behavior live in `design_spec.colors` / `spec_lock.colors` (confirmation `e`). A visual style describes how those anchors behave in SVG composition and may call for contextual tints, gradients, effects, or material transitions; it does not substitute an unrelated palette. Generated images follow the same anchor model through [`image-renderings/`](../image-renderings/). [`image-palettes/`](../image-palettes/) is legacy compatibility material only.
> >
> A visual style is *not* a mode. **Visual style = how it looks; mode = how you argue** (see [`modes/_index.md`](../modes/_index.md)). Locked independently — any style pairs with any mode. > A visual style is *not* a mode. **Visual style = how it looks; mode = how you argue** (see [`modes/_index.md`](../modes/_index.md)). Locked independently — any style pairs with any mode.
@@ -10,7 +10,7 @@ A **visual style** is how the deck **looks** — shape language, decoration dens
## 1. Catalog ## 1. Catalog
Each style has its own file with: shape & decoration, typography character, color-usage discipline (no HEX), texture / elevation, and the paired image-rendering. **Read only the file for the style you lock** — never glob the directory. The catalog mirrors [`image-renderings`](../image-renderings/_index.md): each style's "Paired rendering" names the illustration family that shares its aesthetic. Each style has its own file with: shape & decoration, typography character, color-usage discipline (no HEX), texture / elevation, and the paired image-rendering. A preset lock reads that one file. A catalog-based `custom` reads every preset named in `visual_style_references`; a novel `custom` may omit references. Never glob the directory. The catalog mirrors [`image-renderings`](../image-renderings/_index.md): each style's "Paired rendering" names the illustration family that shares its aesthetic.
> The **`visual_style` value is only ever a first-column `id`** (`swiss-minimal`, `editorial`, …). The "Paired rendering" column lists **§h image-rendering** names (`flat`, `minimalist-swiss`, `digital-dashboard`, …) — never lock one of those as the `visual_style`; they belong to confirmation h. > The **`visual_style` value is only ever a first-column `id`** (`swiss-minimal`, `editorial`, …). The "Paired rendering" column lists **§h image-rendering** names (`flat`, `minimalist-swiss`, `digital-dashboard`, …) — never lock one of those as the `visual_style`; they belong to confirmation h.
> >
@@ -94,6 +94,8 @@ Each style has its own file with: shape & decoration, typography character, colo
Stage 2 always authors one visible, non-empty AI custom proposal beside the preset spectrum. Its paragraph names shape language, composition geometry (page-scale moves), decoration density, whitespace, typographic character, and texture — **no HEX, no color names as values**. The proposal is initially unselected and remains recommendation-only unless the user chooses it; a template-backed proposal must stay inside the inherited identity and confirmed application plan. When selected, record the edited aesthetic in the Design Spec first, then project `- visual_style: custom` plus `- visual_style_behavior:` to `spec_lock.md`. The candidate is mandatory; selecting `custom` remains a tail-case, not the default. Stage 2 always authors one visible, non-empty AI custom proposal beside the preset spectrum. Its paragraph names shape language, composition geometry (page-scale moves), decoration density, whitespace, typographic character, and texture — **no HEX, no color names as values**. The proposal is initially unselected and remains recommendation-only unless the user chooses it; a template-backed proposal must stay inside the inherited identity and confirmed application plan. When selected, record the edited aesthetic in the Design Spec first, then project `- visual_style: custom` plus `- visual_style_behavior:` to `spec_lock.md`. The candidate is mandatory; selecting `custom` remains a tail-case, not the default.
**Mandatory — read every catalog source actually used**: If a custom proposal combines or borrows existing styles, name their exact ids in the visible proposal and read each corresponding file before synthesizing its shape, composition, typography, whitespace, and texture rules. Persist those ids as `visual_style_references`. Do not attach references merely because they are adjacent in the catalog. A genuinely new aesthetic with no catalog source omits `visual_style_references` and proceeds from its standalone behavior.
--- ---
## 4. How to use ## 4. How to use
@@ -101,6 +103,6 @@ Stage 2 always authors one visible, non-empty AI custom proposal beside the pres
1. Strategist reads this index at confirmation `d. Layer 2`. 1. Strategist reads this index at confirmation `d. Layer 2`.
2. Preselect one style from the auto-selection table + the deck's vibe; separately author the visible AI custom proposal from §3. 2. Preselect one style from the auto-selection table + the deck's vibe; separately author the visible AI custom proposal from §3.
3. Record the confirmed style and rationale in `design_spec.md`, then project `- visual_style: <name>` into `spec_lock.md`. 3. Record the confirmed style and rationale in `design_spec.md`, then project `- visual_style: <name>` into `spec_lock.md`.
4. Executor reads **only** `visual-styles/<locked-style>.md` at generation entry — never globs this directory. 4. Executor reads `visual-styles/<locked-style>.md` for a preset. For `custom`, it reads every file listed in `visual_style_references` before applying `visual_style_behavior`; with no references, it applies the novel behavior directly. Never glob this directory.
**Lock scope**: deck-wide (one style per deck). It anchors taste as a **reference**, not a whitelist — pages may deviate with reason. **Lock scope**: deck-wide (one style per deck). It anchors taste as a **reference**, not a whitelist — pages may deviate with reason.
@@ -21,9 +21,9 @@ Approachable and modern. Rounded cards, gentle elevation, friendly rhythm. For p
## 3. Using the deck's colors ## 3. Using the deck's colors
- Theme color used confidently on covers / chapter backgrounds; same-hue tints for card backings; accent for key figures. - Theme color used confidently on covers / chapter backgrounds; same-hue tints for card backings; accent for key figures.
- Warmer, more generous color use than swiss / editorial — still disciplined (60-30-10), never rainbow. - Warmer, more generous color use than swiss / editorial. Assign hues by semantic role and visual hierarchy; intentional multi-hue compositions remain valid.
> HEX values come from confirmation `e`; this style only governs the confident-but-disciplined (60-30-10) color use — it names no colors. > HEX values come from confirmation `e`; this style governs confident, friendly color use without naming colors or imposing a ratio.
## 4. Texture / elevation ## 4. Texture / elevation
@@ -27,7 +27,7 @@ python3 scripts/source_to_md/pdf_to_md.py <file.pdf>
python3 scripts/source_to_md/ppt_to_md.py <deck.pptx> python3 scripts/source_to_md/ppt_to_md.py <deck.pptx>
python3 scripts/source_to_md/excel_to_md.py <workbook.xlsx> python3 scripts/source_to_md/excel_to_md.py <workbook.xlsx>
python3 scripts/project_manager.py init <project_name> --format ppt169 python3 scripts/project_manager.py init <project_name> --format ppt169
python3 scripts/project_manager.py import-sources <project_path> <source_files_or_dirs...> --move python3 scripts/project_manager.py import-sources <project_path> <source_files_or_dirs...>
python3 scripts/total_md_split.py <project_path> python3 scripts/total_md_split.py <project_path>
python3 scripts/finalize_svg.py <project_path> python3 scripts/finalize_svg.py <project_path>
python3 scripts/animation_config.py scaffold <project_path> # optional object-level animation overrides python3 scripts/animation_config.py scaffold <project_path> # optional object-level animation overrides
@@ -72,16 +72,16 @@ Project setup:
```bash ```bash
python3 scripts/project_manager.py init <project_name> --format ppt169 python3 scripts/project_manager.py init <project_name> --format ppt169
python3 scripts/project_manager.py import-sources <project_path> <source_files_or_dirs...> --move python3 scripts/project_manager.py import-sources <project_path> <source_files_or_dirs...>
python3 scripts/project_manager.py scaffold-spec <project_path> python3 scripts/project_manager.py scaffold-spec <project_path> # optional manual helper
python3 scripts/project_manager.py scaffold-lock <project_path> python3 scripts/project_manager.py scaffold-lock <project_path> # optional manual helper
python3 scripts/project_manager.py validate <project_path> python3 scripts/project_manager.py validate <project_path>
python3 scripts/project_manager.py page-context <project_path> P07 --record-usage python3 scripts/project_manager.py page-context <project_path> P07 --record-usage
python3 scripts/project_manager.py page-context-report <project_path> python3 scripts/project_manager.py page-context-report <project_path>
``` ```
`page-context` prints a read-only compact current-page projection. Its global `page-context` prints a read-only compact current-page projection. Its global
lock projection repeats per page as an anti-drift guard; large Design Specs, lock projection repeats per page as a continuity anchor set, not a color/font allowlist; large Design Specs,
prototype, and `templates/charts/` references are emitted only as scoped prototype, and `templates/charts/` references are emitted only as scoped
path/SHA fingerprints and are read once per execution context. `--bundle` is a path/SHA fingerprints and are read once per execution context. `--bundle` is a
deprecated compatibility no-op. `--record-usage` writes one derived snapshot deprecated compatibility no-op. `--record-usage` writes one derived snapshot
@@ -161,7 +161,13 @@ def _score_candidate(key: str, summary: str, tags: list[str]) -> tuple[int, list
return score, matched_tags return score, matched_tags
def recall_candidates(page: str, tags: list[str], limit: int) -> dict[str, object]: def recall_candidates(
page: str,
tags: list[str],
limit: int,
*,
force_semantic_fallback: bool = False,
) -> dict[str, object]:
"""Recall a deterministic shortlist for one page.""" """Recall a deterministic shortlist for one page."""
charts = load_catalog() charts = load_catalog()
scored: list[tuple[int, str, str, list[str]]] = [] scored: list[tuple[int, str, str, list[str]]] = []
@@ -193,7 +199,7 @@ def recall_candidates(page: str, tags: list[str], limit: int) -> dict[str, objec
else: else:
confidence = "none" confidence = "none"
return { result: dict[str, object] = {
"page": page, "page": page,
"semantic_tags": tags, "semantic_tags": tags,
"confidence": confidence, "confidence": confidence,
@@ -201,9 +207,26 @@ def recall_candidates(page: str, tags: list[str], limit: int) -> dict[str, objec
"no_template_match": { "no_template_match": {
"allowed": True, "allowed": True,
"key": "no-template-match", "key": "no-template-match",
"instruction": "Use when every candidate conflicts with the page content shape or a Skip clause.", "instruction": (
"Use when none of the bounded candidates fits the page structure. If the "
"shortlist may have missed a relevant catalog rule, rerun with "
"--semantic-fallback first. Keep this result out of Design Spec Section "
"VII and describe the chosen fallback in the page's Section IX block."
),
}, },
} }
if force_semantic_fallback:
result["semantic_fallback"] = {
"reason": "requested-after-bounded-review",
"instruction": (
"Semantically compare the page tags with every returned selection rule. "
"Choose one exact catalog key or keep no-template-match; lexical overlap "
"is not required in this review."
),
"path_pattern": "templates/charts/{key}.svg",
"catalog": charts,
}
return result
def _dedupe(values: list[str]) -> list[str]: def _dedupe(values: list[str]) -> list[str]:
@@ -230,7 +253,12 @@ def _run_recall(args: argparse.Namespace) -> int:
print("Error: recall requires 3-8 distinct non-empty --tag values.", file=sys.stderr) print("Error: recall requires 3-8 distinct non-empty --tag values.", file=sys.stderr)
return 2 return 2
result = recall_candidates(page, tags, args.limit) result = recall_candidates(
page,
tags,
args.limit,
force_semantic_fallback=args.semantic_fallback,
)
print(json.dumps(result, ensure_ascii=False, indent=2, sort_keys=True)) print(json.dumps(result, ensure_ascii=False, indent=2, sort_keys=True))
return 0 return 0
@@ -247,7 +275,8 @@ def _run_validate(args: argparse.Namespace) -> int:
if invalid: if invalid:
print( print(
"Error: replace each invalid key with a key returned by the recall command, " "Error: replace each invalid key with a key returned by the recall command, "
"or record no-template-match without a page_charts entry.", "or keep no-template-match out of Section VII and page_charts while "
"recording the custom fallback in the page's Section IX block.",
file=sys.stderr, file=sys.stderr,
) )
return 1 return 1
@@ -277,6 +306,11 @@ def build_parser() -> argparse.ArgumentParser:
metavar="3..8", metavar="3..8",
help="Candidate count (default: 6).", help="Candidate count (default: 6).",
) )
recall.add_argument(
"--semantic-fallback",
action="store_true",
help="Include the full catalog when bounded recall may have missed a semantic match.",
)
recall.set_defaults(handler=_run_recall) recall.set_defaults(handler=_run_recall)
validate = subparsers.add_parser("validate", help="Validate selected catalog keys.") validate = subparsers.add_parser("validate", help="Validate selected catalog keys.")
@@ -83,7 +83,7 @@
image_strategy_visual: "Visual", image_strategy_visual: "Visual",
image_strategy_mood: "Mood", image_strategy_mood: "Mood",
image_strategy_ai_custom: "AI custom proposal", image_strategy_ai_custom: "AI custom proposal",
image_strategy_ai_custom_desc: "A complete out-of-catalog rendering proposal. Select it to edit.", image_strategy_ai_custom_desc: "A novel or multi-reference rendering proposal. Select it to edit.",
image_strategy_custom_placeholder: "Describe the exact generated-image direction, subjects, composition, style cues, or things to avoid.", image_strategy_custom_placeholder: "Describe the exact generated-image direction, subjects, composition, style cues, or things to avoid.",
image_strategy_reference_hint: "Reference images show rendering only. Final AI images inherit the deck color scheme selected above.", image_strategy_reference_hint: "Reference images show rendering only. Final AI images inherit the deck color scheme selected above.",
image_strategy_no_reference: "No reference image for this custom choice.", image_strategy_no_reference: "No reference image for this custom choice.",
@@ -221,7 +221,7 @@
image_strategy_visual: "ビジュアル", image_strategy_visual: "ビジュアル",
image_strategy_mood: "ムード", image_strategy_mood: "ムード",
image_strategy_ai_custom: "AIカスタム案", image_strategy_ai_custom: "AIカスタム案",
image_strategy_ai_custom_desc: "カタログ外の完全なレンダリング案です。選択後に編集できます。", image_strategy_ai_custom_desc: "新規または複数の既存表現を統合したレンダリング案です。選択後に編集できます。",
image_strategy_custom_placeholder: "生成画像の方向性、被写体、構図、スタイル要素、避けたい要素を具体的に入力してください。", image_strategy_custom_placeholder: "生成画像の方向性、被写体、構図、スタイル要素、避けたい要素を具体的に入力してください。",
image_strategy_reference_hint: "参照画像はレンダリングのみを示します。最終AI画像の色は上で選んだデッキ配色を継承します。", image_strategy_reference_hint: "参照画像はレンダリングのみを示します。最終AI画像の色は上で選んだデッキ配色を継承します。",
image_strategy_no_reference: "このカスタム選択には参照画像がありません。", image_strategy_no_reference: "このカスタム選択には参照画像がありません。",
@@ -359,7 +359,7 @@
image_strategy_visual: "视觉", image_strategy_visual: "视觉",
image_strategy_mood: "情绪", image_strategy_mood: "情绪",
image_strategy_ai_custom: "AI 自定义方案", image_strategy_ai_custom: "AI 自定义方案",
image_strategy_ai_custom_desc: "一套完整的目录外渲染方案;选择后可以编辑。", image_strategy_ai_custom_desc: "一套全新或综合多个已有风格的渲染方案;选择后可以编辑。",
image_strategy_custom_placeholder: "描述生成图的具体方向、主体、构图、风格关键词或需要避免的内容。", image_strategy_custom_placeholder: "描述生成图的具体方向、主体、构图、风格关键词或需要避免的内容。",
image_strategy_reference_hint: "参考图只展示渲染风格;最终 AI 图片直接继承上方已选的整套 PPT 配色。", image_strategy_reference_hint: "参考图只展示渲染风格;最终 AI 图片直接继承上方已选的整套 PPT 配色。",
image_strategy_no_reference: "自定义选择没有参考图。", image_strategy_no_reference: "自定义选择没有参考图。",
@@ -1,6 +1,6 @@
# Chart Candidate Recall # Chart Candidate Recall
`chart_recall.py` gives the Strategist a bounded, deterministic shortlist without loading the full chart catalog into the runtime prompt. It reads `templates/charts/charts_index.json` on every invocation, so the catalog remains the only template registry. `chart_recall.py` gives the Strategist a bounded deterministic shortlist. It exposes the full live catalog only when the caller explicitly requests semantic review. It reads `templates/charts/charts_index.json` on every invocation, so the catalog remains the only template registry.
## Recall candidates ## Recall candidates
@@ -15,7 +15,9 @@ python3 skills/ppt-master/scripts/chart_recall.py recall \
--limit 6 --limit 6
``` ```
`--limit` accepts 3-8 and defaults to 6. It is a maximum, not a padding target: the deterministic JSON contains only positive-scoring candidates, up to the requested limit. Zero positive matches return an empty `candidates` list plus the explicit `no-template-match` option. `--limit` accepts 3-8 and defaults to 6. The JSON is already bounded and must be read unfiltered: `tail`, `head`, `grep`, or another truncator can discard higher-ranked candidates. `confidence` reports lexical strength only; `low` / `none` never decides whether a template should be used.
Semantically review the bounded candidates. If none fits and the page clearly needs a custom composition, retain `no-template-match`. If terminology mismatch or structural ambiguity suggests that bounded recall may have missed a catalog match, rerun the same command with `--semantic-fallback`, then compare the returned rules semantically. The flag is an uncertainty fallback, not a routine no-match gate. Do not open or maintain a second keyword/category index. `no-template-match` is an internal recall result, not a Design Spec §VII row.
| Field | Contract | | Field | Contract |
|---|---| |---|---|
@@ -23,9 +25,10 @@ python3 skills/ppt-master/scripts/chart_recall.py recall \
| `semantic_tags` | Deduplicated input tags | | `semantic_tags` | Deduplicated input tags |
| `confidence` | Lexical recall strength; never a selection decision | | `confidence` | Lexical recall strength; never a selection decision |
| `candidates` | Ranked keys, SVG paths, verbatim catalog summaries, scores, and matched tags | | `candidates` | Ranked keys, SVG paths, verbatim catalog summaries, scores, and matched tags |
| `no_template_match` | Explicit fallback option when every candidate conflicts with the page | | `semantic_fallback` | Full live catalog, present only with `--semantic-fallback`; requires semantic comparison |
| `no_template_match` | Explicit fallback when the Strategist judges that no recalled reference fits |
The scorer treats the key and the summary's Pick clause as positive evidence and the Skip clause as negative evidence. A term found only in Skip cannot make a candidate eligible, and Skip matches explicitly reduce a candidate's score. Unicode input is NFKC-normalized before matching. The Strategist still applies semantic judgment: inspect every returned summary, reject candidates whose Skip clause matches, and prefer the most specific valid structure. A low score does not authorize a forced match; when no candidate has a positive final score, the result carries an empty shortlist and the explicit fallback. The scorer treats the key and the summary's Pick clause as positive evidence and the Skip clause as negative evidence. A term found only in Skip cannot make a candidate eligible, and Skip matches explicitly reduce a candidate's score. Unicode input is NFKC-normalized before matching. The Strategist still applies semantic judgment: inspect the returned candidates, reject candidates whose Skip clause matches, and prefer the most specific valid structure. An empty shortlist permits `no-template-match`; use `--semantic-fallback` only when the Strategist suspects a relevant catalog structure was missed.
## Validate selected keys ## Validate selected keys
@@ -35,11 +38,13 @@ Validate every selected template key before writing `design_spec.md §VII` or `s
python3 skills/ppt-master/scripts/chart_recall.py validate line_chart quadrant_text_bullets python3 skills/ppt-master/scripts/chart_recall.py validate line_chart quadrant_text_bullets
``` ```
The command is read-only. It exits `0` when every key exists and `1` when any key is absent. A page recorded as `no-template-match` is not a key and must not appear in `page_charts`. The command is read-only. It exits `0` when every key exists and `1` when any key is absent. A `no-template-match` page appears in neither §VII nor `page_charts`; record its chosen fallback in the page's §IX `Visualization` / `Layout` instead.
## Selection boundary ## Selection boundary
- Preserve the two-lens review: numeric/data pages and structural-information pages. - Preserve the two-lens review: numeric/data pages and structural-information pages.
- Record the selected candidate's returned `summary` verbatim as the Section VII `summary-quote`. - Record the selected candidate's returned `summary` verbatim as the Section VII `summary-quote`.
- Record real returned runners-up and page-specific rejection reasons. - Keep §VII as a positive inventory: every row has a real key/path, and the whole section is omitted when no candidate is selected.
- Never serialize `no-template-match`, an empty table, or a no-reference explanation into §VII.
- Record real returned runners-up and page-specific rejection reasons only for pages with a selected reference.
- Open only the selected `<key>.svg` before authoring that visualization; do not load unrelated catalog SVGs. - Open only the selected `<key>.svg` before authoring that visualization; do not load unrelated catalog SVGs.
@@ -52,9 +52,9 @@ pip install flask
- **Enumerable + custom** — canvas / icons retain blank manual inputs; mode / visual_style instead show a mandatory AI-authored proposal in full, initially unselected and editable after selection. Selected mode / style writes literal `custom` plus its behavior sibling. - **Enumerable + custom** — canvas / icons retain blank manual inputs; mode / visual_style instead show a mandatory AI-authored proposal in full, initially unselected and editable after selection. Selected mode / style writes literal `custom` plus its behavior sibling.
- **Visual examples for hard-to-name choices** — the full-screen confirmation page loads real SVG page samples from `static/style_previews/` for `visual_style`, and renders real sample SVGs from `templates/icons` for `icons`. These thumbnails 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 `recommendations.json`, so users compare visual treatment rather than copywriting. These previews are a confirmation aid only: they do not add fields to `recommendations.json` 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 `visual_style`, and renders real sample SVGs from `templates/icons` for `icons`. These thumbnails 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 `recommendations.json`, so users compare visual treatment rather than copywriting. These previews are a confirmation aid only: they do not add fields to `recommendations.json` 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. 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), formula policy / 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), formula policy / 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.
- **Open prose**`audience`, `communication_intent`, `audience_outcome`, `core_message`, `delivery_context`, `artifact_afterlife`, `content_divergence`, and `page_count`. `communication_intent` may carry several purposes plus priority / sequence; common paths appear only as help text. `content_divergence` remains a separate source-treatment axis. - **Open prose**`audience`, `communication_intent`, `audience_outcome`, `core_message`, `delivery_context`, `artifact_afterlife`, `content_divergence`, and `page_count`. `communication_intent` may carry several purposes plus priority / sequence; common paths appear only as help text. `content_divergence` is the source-treatment axis. `page_count` may be a range here; Strategist resolves the exact §IX roster, leaving Executor no pagination latitude.
- **Coordinated generative directions**`design_directions` carries ≥3 safe / shifted / bold candidates. Each candidate bundles visual style, color, typography, icon id, and conditional generated-image rendering. The page can still render legacy top-level `color`, `typography`, and `image_strategy` candidates, but new staged recommendations use the coordinated bundle. - **Coordinated generative directions**`design_directions` carries ≥3 safe / shifted / bold candidates. Each candidate bundles visual style, color, typography, icon id, and conditional generated-image rendering. The page can still render legacy top-level `color`, `typography`, and `image_strategy` candidates, but new staged recommendations use the coordinated bundle.
AI-authored custom proposals apply only to mode, visual style, and conditional AI-image rendering; a selected proposal cannot be blank. Color / typography keep their existing manual Custom cards. Image usage uses source ids plus `image_notes`; closed sets have no Custom path. AI-authored custom proposals apply only to mode, visual style, and conditional AI-image rendering; a selected proposal cannot be blank. Color / typography keep their existing manual Custom cards. Image usage uses source ids plus `image_notes`; closed sets have no Custom path.
@@ -202,7 +202,7 @@ After Stage 2 is confirmed, overwrite it with Stage-3 production recommendations
``` ```
- `recommend.*` names each recommended id. New mode / style values use a catalog id or literal `custom`; arbitrary prose values are legacy-only. Use `recommend.image_strategy: "custom"` only when an explicit user-supplied image direction should start selected. Missing recommendations fall back to the normal preset. Legacy aliases remain accepted; new files write canonical ids. - `recommend.*` names each recommended id. New mode / style values use a catalog id or literal `custom`; arbitrary prose values are legacy-only. Use `recommend.image_strategy: "custom"` only when an explicit user-supplied image direction should start selected. Missing recommendations fall back to the normal preset. Legacy aliases remain accepted; new files write canonical ids.
- `custom_candidates` is recommendation-only. Mode / style carry localized `name` + `behavior`; conditional image strategy also carries `rendering: "custom"`, `visual`, and `mood`. The server rejects missing required candidates; the UI shows full copy, edits it only after selection, rejects a selected blank, and omits unselected candidates from `result.json`. Template-backed proposals obey inherited identity, prototype capacity, and `template_application`. - `custom_candidates` is recommendation-only. Mode / style carry localized `name` + `behavior`; conditional image strategy also carries `rendering: "custom"`, `visual`, and `mood`. When a proposal combines or borrows existing catalog entries, the visible behavior names every exact id and Strategist reads every corresponding file before authoring it; a genuinely novel proposal names none. The server rejects missing required candidates; the UI shows full copy, edits it only after selection, rejects a selected blank, and omits unselected candidates from `result.json`. Template-backed proposals obey inherited identity, prototype capacity, and `template_application`.
- `audience`, `communication_intent`, and `audience_outcome` are load-bearing Stage-1 reasoning inputs, so seed concrete recommendations when the evidence supports them; they are not required user inputs. Every Stage-1 prose field may be blank after confirmation. The complete six-field contract stays in `result.json` and `design_spec.md`; `spec_lock.md communication` receives only the compact `audience` / `objective` / `core_message` execution projection plus the applicable reading mode. `communication_intent` may preserve several purposes plus priority / sequence; never add a `primary_job` enum. - `audience`, `communication_intent`, and `audience_outcome` are load-bearing Stage-1 reasoning inputs, so seed concrete recommendations when the evidence supports them; they are not required user inputs. Every Stage-1 prose field may be blank after confirmation. The complete six-field contract stays in `result.json` and `design_spec.md`; `spec_lock.md communication` receives only the compact `audience` / `objective` / `core_message` execution projection plus the applicable reading mode. `communication_intent` may preserve several purposes plus priority / sequence; never add a `primary_job` enum.
- Do not write `recommend.template_reuse_scope` or `recommend.template_adherence`. Strategist records those internal exporter values later in `spec_lock.md` after inspecting the actual template and current content. - Do not write `recommend.template_reuse_scope` or `recommend.template_adherence`. Strategist records those internal exporter values later in `spec_lock.md` after inspecting the actual template and current content.
- For an active template workspace, write one editable prose field as top-level `template_application.value`. It summarizes actual page/prototype use and preservation/reorganization decisions. Omit it for free design. The UI returns the current string through Stage 2, Stage 3, and final confirmation; Strategist then persists the final effective plan as `- **Template Application**: ...` in `design_spec.md §I`, and `page-context` projects it to Executor. Never replace it with internal reuse/adherence ids or a fixed option menu. - For an active template workspace, write one editable prose field as top-level `template_application.value`. It summarizes actual page/prototype use and preservation/reorganization decisions. Omit it for free design. The UI returns the current string through Stage 2, Stage 3, and final confirmation; Strategist then persists the final effective plan as `- **Template Application**: ...` in `design_spec.md §I`, and `page-context` projects it to Executor. Never replace it with internal reuse/adherence ids or a fixed option menu.
@@ -212,10 +212,10 @@ After Stage 2 is confirmed, overwrite it with Stage-3 production recommendations
- **Color candidates carry the user-facing core `palette`**: `background`, `secondary_bg`, `primary`, `accent`, `secondary_accent`, and `body_text`. The page renders every role as a labelled swatch with its HEX value visible, and offers per-role override inputs for precise single-role edits, plus a **Custom color card with a free-text box** (parallel to the custom typography box) — the user can describe the palette in words or paste HEX values instead of filling each role; this writes `color: { "name": "custom", "custom": "<text>" }` to `result.json` for the AI to interpret. Legacy `text` is accepted as an alias for `body_text`, but new files should write `body_text`. Strategist derives secondary text, borders, state colors, and visual-style neutral tiers while writing `design_spec.md`, then projects the machine values to `spec_lock.md`; those are not user-facing confirmation choices. - **Color candidates carry the user-facing core `palette`**: `background`, `secondary_bg`, `primary`, `accent`, `secondary_accent`, and `body_text`. The page renders every role as a labelled swatch with its HEX value visible, and offers per-role override inputs for precise single-role edits, plus a **Custom color card with a free-text box** (parallel to the custom typography box) — the user can describe the palette in words or paste HEX values instead of filling each role; this writes `color: { "name": "custom", "custom": "<text>" }` to `result.json` for the AI to interpret. Legacy `text` is accepted as an alias for `body_text`, but new files should write `body_text`. Strategist derives secondary text, borders, state colors, and visual-style neutral tiers while writing `design_spec.md`, then projects the machine values to `spec_lock.md`; those are not user-facing confirmation choices.
- **Candidate display text may be multilingual**: color / typography candidates can provide `name_zh` / `name_en` / `name_ja` and `note_zh` / `note_en` / `note_ja`; the page falls back to legacy `name` / `note`. Labels resolve in the page language first, then fall back across the others (a `ja` page: ja → en → zh; zh/en pages keep their zh↔en fallback and try `_ja` last), so when `lang` is `ja` always include the `_ja` variants — otherwise the candidate labels render in English. - **Candidate display text may be multilingual**: color / typography candidates can provide `name_zh` / `name_en` / `name_ja` and `note_zh` / `note_en` / `note_ja`; the page falls back to legacy `name` / `note`. Labels resolve in the page language first, then fall back across the others (a `ja` page: ja → en → zh; zh/en pages keep their zh↔en fallback and try `_ja` last), so when `lang` is `ja` always include the `_ja` variants — otherwise the candidate labels render in English.
- **Typography candidates split CJK and Latin** for both `heading` and `body`; `css` is the fallback preview stack. Each candidate includes topic-matched sample text. Stage 2 is authored once with a reading-mode baseline of `text` 20 · `balanced` 24 · `presentation` 32 px on PPT. Font cards choose family / character and preserve the current sizing state; they do not introduce a competing size recommendation. The page writes px directly. `delivery_purpose` remains the compatibility key only. - **Typography candidates split CJK and Latin** for both `heading` and `body`; `css` is the fallback preview stack. Each candidate includes topic-matched sample text. Stage 2 is authored once with a reading-mode baseline of `text` 20 · `balanced` 24 · `presentation` 32 px on PPT. Font cards choose family / character and preserve the current sizing state; they do not introduce a competing size recommendation. The page writes px directly. `delivery_purpose` remains the compatibility key only.
- **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: `reading mode → body baseline → unpinned role sizes` (role ramp: `body ×` the §g ratios). Changing reading mode updates the 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. Font / direction-card selection preserves all current sizes. This is a browser-only state update: it performs no fetch, asks the backend to author no new recommendations, and a re-render preserves exactly what the user sees. 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. Candidate `sizes` remain accepted for compatibility, but the fresh Stage-2 baseline is normalized through 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: `reading mode → body baseline → unpinned role sizes` (role ramp: `body ×` the §g ratios). Changing reading mode updates the 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. Font / direction-card selection preserves all current sizes. This is a browser-only state update: it performs no fetch, asks the backend to author no new recommendations, and a re-render preserves exactly what the user sees. 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, but the fresh Stage-2 baseline is normalized through 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 `image_usage: ai`: up to three preset cards plus one full-width AI custom proposal. Custom has no preset dropdown; selection makes it editable and submits `rendering: "custom"` + `behavior`. The live preview follows the selection. No image palette is written; deck colors remain authoritative, and legacy `image_strategy.palette` is ignored. - **Generated-image direction** appears only for `image_usage: ai`: up to three preset cards plus one full-width AI custom proposal. Custom has no preset dropdown; selection makes it editable and submits `rendering: "custom"` + `behavior`. If that behavior uses catalog renderings, it visibly names their exact ids; Strategist retains that confirmed basis as optional `image_rendering_references` in `spec_lock.md`. A genuinely novel behavior produces no reference row. The live preview follows the selection. No image palette is written; deck colors remain authoritative, and legacy `image_strategy.palette` is ignored.
- **`design_directions`** is the canonical Stage-2 spectrum: ≥3 safe / shifted / bold bundles with localized copy, style, icons, conditional image strategy, complete CJK/Latin typography, and HEX `background`, `secondary_bg`, `primary`, `accent`, `secondary_accent`, `body_text`. Selection applies the bundle; component controls override it. `result.json` stores components, not a direction id. - **`design_directions`** is the canonical Stage-2 spectrum: ≥3 safe / shifted / bold bundles with localized copy, style, icons, conditional image strategy, complete CJK/Latin typography, and HEX `background`, `secondary_bg`, `primary`, `accent`, `secondary_accent`, `body_text`. Selection applies the bundle; component controls override it. `result.json` stores components, not a direction id.
- `recommend.generation_mode` and `refine_spec` mirror the two mandatory notes in [`generate-pptx`](../../workflows/generate-pptx.md) Step 4. Confirmed `generation_mode: "split"` / `refine_spec: true` are explicit user choices, equivalent to opting in through chat. - `recommend.generation_mode` and `refine_spec` mirror the two mandatory notes in [`generate-pptx`](../../workflows/generate-pptx.md) Step 4. Confirmed `generation_mode: "split"` / `refine_spec: true` are explicit user choices, equivalent to opting in through chat.
- `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.
@@ -254,12 +254,12 @@ After Stage 2 is confirmed, overwrite it with Stage-3 production recommendations
} }
``` ```
The shape above is final. Selected custom values use `mode: custom` + `mode_behavior`, `visual_style: custom` + `visual_style_behavior`, or `image_strategy.rendering: custom` + `behavior`. Intermediate writes retain accumulated fields; legacy tier names remain read-compatible. The shape above is final. Selected custom values use `mode: custom` + `mode_behavior`, `visual_style: custom` + `visual_style_behavior`, or `image_strategy.rendering: custom` + `behavior`. During Design Spec and lock authoring, Strategist projects optional `mode_references`, `visual_style_references`, or `image_rendering_references` only when that confirmed behavior actually uses named catalog sources; genuinely novel custom behavior has no reference list. Intermediate writes retain accumulated fields; legacy tier names remain read-compatible.
**Final-result consumption contract.** A final result is the user-confirmed input contract for the Strategist's Design Spec, not another recommendation input. The Strategist re-reads the complete final object, writes and audits `design_spec.md` against every explicitly present field, and may autonomously decide only details that remain unconfirmed. Only after that audit passes does it project `spec_lock.md` from the completed Design Spec; lock authoring is not a second design pass. It must not omit, substitute, narrow, weaken, reinterpret, or re-recommend a confirmed value. If one value cannot be honored, the owning workflow reports or pauses under failure recovery; it never deletes the requirement to keep the pipeline moving. **Final-result consumption contract.** A final result is the user-confirmed input contract for the Strategist's Design Spec, not another recommendation input. After the final wait, Generate Step 4 reads the complete final object exactly once and retains it while Strategist writes and audits `design_spec.md` against every explicitly present field. Normal lock authoring and downstream execution do not reopen `result.json`; the completed Design Spec is the durable authority. Only after that audit passes does Strategist author `spec_lock.md` from the Design Spec plus current execution context, selecting stable anchors and routing rather than copying every field or enumerating every legal color/font. Every value must be consumed at the semantic type owned by [`strategist.md`](../../references/strategist.md) §1 and its field owner: do not omit or substitute it, and do not silently strengthen or weaken its type. If a confirmed requirement cannot be honored, the owning workflow reports or pauses under failure recovery; it never deletes the requirement to keep the pipeline moving.
- Bespoke mode / style prose lives only in the required behavior sibling; image custom prose lives in `image_strategy.behavior`. Canvas / icons retain free-text edge cases, color / typography retain `name: "custom"`, and image usage remains a source-id array plus `image_notes`. - Bespoke mode / style prose lives only in the required behavior sibling; image custom prose lives in `image_strategy.behavior`. Canvas / icons retain free-text edge cases, color / typography retain `name: "custom"`, and image usage remains a source-id array plus `image_notes`.
- `image_ai_path` and `image_strategy` appear only with `image_usage: ai` and remain confirmed downstream. The page is default; explicit/failure chat fallback keeps identical fields. `image_ai_path` selects the Step 5 path, and [`strategist-image.md`](../../references/strategist-image.md) §2 locks the chosen strategy verbatim. - `image_ai_path` and `image_strategy` appear only with `image_usage: ai` and remain confirmed downstream. The page is default; explicit/failure chat fallback keeps identical fields. `image_ai_path` selects the Step 5 path, and [`strategist-image.md`](../../references/strategist-image.md) §2 retains the selected rendering or custom behavior as the deck-level image identity anchor; individual prompts still adapt subject, composition, and atmosphere within it.
- After the user clicks the **final Confirm** (Stage 3, or single-pass), the page saves `result.json` and shuts the server down (auto-close). Stage-1 **Confirm contract & continue** and Stage-2 **Confirm solution & continue** keep the page open while it polls for the once-authored downstream stage. In the default flow, the first `--daemon --wait` returns on the stage-1 result, `--wait-only --wait-stage stage2` returns on the stage-2 result, and the final `--wait-only` returns on the final result; the AI reads each immediately — no extra chat confirmation is required. Chat fallback shows the same initially-unselected custom proposals. Either way, Step 4 ends with a `--shutdown` cleanup so a never-confirmed page cannot keep holding port 5050 ahead of the Step 6 live preview. - After the user clicks the **final Confirm** (Stage 3, or single-pass), the page saves `result.json` and shuts the server down (auto-close). Stage-1 **Confirm contract & continue** and Stage-2 **Confirm solution & continue** keep the page open while it polls for the once-authored downstream stage. In the default flow, the first `--daemon --wait` returns on the stage-1 result, `--wait-only --wait-stage stage2` returns on the stage-2 result, and the final `--wait-only` returns on the final result; the AI reads each immediately — no extra chat confirmation is required. Chat fallback shows the same initially-unselected custom proposals. Either way, Step 4 ends with a `--shutdown` cleanup so a never-confirmed page cannot keep holding port 5050 ahead of the Step 6 live preview.
## Scope ## Scope
@@ -1,8 +1,8 @@
# Project Tools # Project Tools
> **Import boundary**: copy out-of-repository sources by default to protect user > **Import boundary**: move only sources already under the repository's
> files; move in-repository sources by default to avoid leaving accidental > `projects/` tree. Copy every other local path, even when `--move` is supplied.
> commit artifacts. Explicit `--copy` / `--move` flags override the default. > Use `--copy` to preserve a projects-local source.
Project tools create, validate, and inspect the standard PPT Master workspace. Project tools create, validate, and inspect the standard PPT Master workspace.
@@ -13,8 +13,8 @@ Main entry point for project setup and validation.
```bash ```bash
python3 scripts/project_manager.py init <project_name> --format ppt169 python3 scripts/project_manager.py init <project_name> --format ppt169
python3 scripts/project_manager.py import-sources <project_path> <source1_or_dir> [<source2_or_dir> ...] python3 scripts/project_manager.py import-sources <project_path> <source1_or_dir> [<source2_or_dir> ...]
python3 scripts/project_manager.py scaffold-spec <project_path> python3 scripts/project_manager.py scaffold-spec <project_path> # optional manual helper
python3 scripts/project_manager.py scaffold-lock <project_path> python3 scripts/project_manager.py scaffold-lock <project_path> # optional manual helper
python3 scripts/project_manager.py validate <project_path> python3 scripts/project_manager.py validate <project_path>
python3 scripts/project_manager.py info <project_path> python3 scripts/project_manager.py info <project_path>
python3 scripts/project_manager.py page-context <project_path> P07 [--pretty] [--record-usage] python3 scripts/project_manager.py page-context <project_path> P07 [--pretty] [--record-usage]
@@ -22,40 +22,46 @@ python3 scripts/project_manager.py page-context-report <project_path>
``` ```
Notes: Notes:
- Files outside the repo are copied into `sources/` by default - Files outside `projects/` are always copied into `sources/`
- With `--move`, files outside the repo are moved into `sources/` - `--move` applies only to sources under the repository's `projects/` tree
- Directory inputs are expanded non-recursively. After Step 1 conversion, - Directory inputs are expanded non-recursively. After Step 1 conversion,
pass the source file/directory once when generated Markdown lives beside the pass the source file/directory once when generated Markdown lives beside the
original source. If Step 1 used `-o` to write Markdown elsewhere, pass both original source. If Step 1 used `-o` to write Markdown elsewhere, pass both
the original source path/directory and the Markdown output path/directory. the original source path/directory and the Markdown output path/directory.
- Under move semantics, a supplied source directory left strictly empty after - A projects-local supplied source directory left strictly empty after import
import (or empty from the start) is removed; a directory that still holds any (or empty from the start) is removed; every directory outside `projects/`
file or subdirectory is left untouched. `--copy` never removes directories. remains untouched. `--copy` never removes directories.
- Files already inside the repo are moved into `sources/` by default (with a stderr - Files already under `projects/` move into `sources/` by default. Pass `--copy`
note), to avoid leaving unintended artifacts that could be committed by mistake. to preserve them in place.
Pass `--copy` to force a copy for in-repo sources instead.
- `--move` and `--copy` are mutually exclusive. - `--move` and `--copy` are mutually exclusive.
- `scaffold-spec` creates `design_spec.md` from - Normal Generate authoring reads `templates/design_spec_reference.md`, writes
the complete `design_spec.md` from scratch, then reads
`templates/spec_lock_reference.md` and writes the complete lock projection.
It does not call either scaffold command.
- Optional `scaffold-spec` creates `design_spec.md` from
`templates/scaffolds/design_spec.md`; `scaffold-lock` creates `spec_lock.md` `templates/scaffolds/design_spec.md`; `scaffold-lock` creates `spec_lock.md`
from `templates/scaffolds/spec_lock.md`. Both substitute project/canvas from `templates/scaffolds/spec_lock.md`. Both substitute project/canvas
metadata deterministically and refuse to overwrite an existing artifact. metadata deterministically and refuse to overwrite an existing artifact.
- `validate` parses the existing Markdown artifacts against - `validate` parses the existing Markdown artifacts against
`templates/schemas/design_spec.schema.json` and `templates/schemas/design_spec.schema.json` and
`templates/schemas/spec_lock.schema.json`. It reports missing sections and `templates/schemas/spec_lock.schema.json`. It reports missing sections and
fields, illegal enums, malformed page keys, and unmet conditional sections; fields, illegal enums, malformed page keys, and unmet conditional sections.
When optional custom reference lists are present, it also requires every id
to resolve to the matching mode, visual-style, or image-rendering catalog,
rejects duplicates, and rejects reference rows on non-custom selections;
it does not rewrite either artifact or compare their values for textual it does not rewrite either artifact or compare their values for textual
equality. It also does not prove final-confirmation → Design Spec fidelity or equality. It also does not prove final-confirmation → Design Spec fidelity or
Design Spec → lock semantic projection; Generate Step 4 owns those two gates Design Spec/context → lock semantic fidelity; Generate Step 4 owns those two
before this structural validation. One slice is enforced mechanically: when gates before this structural validation. Validation reads the planning
`confirm_ui/result.json` records a final confirmed stage, every confirmed artifacts only and never reopens `confirm_ui/result.json`; the final result is
non-`none` `image_usage` source must appear in at least one `## images` row consumed once into the Design Spec before validation begins. The design schema is structural lint for
of the lock (`provided` maps to `user`; `ai` is also satisfied by `slice`).
The design schema is structural lint for
the human-readable brief; the lock schema owns machine execution values. For the human-readable brief; the lock schema owns machine execution values. For
structured template use, strict input prototypes must match their assigned structured template use, strict input prototypes must match their assigned
Master/Layout; adaptive input prototypes retain the assigned Master while a Master/Layout; adaptive input prototypes retain the assigned Master while a
new output Layout is validated only after its generated SVG exists. Versioned new output Layout already declared by Strategist is cross-validated after its
scaffolds carry the schema marker. Markerless legacy artifacts are left on generated SVG exists. Versioned
Direct-authored current artifacts and optional scaffolds carry the schema
marker. Markerless legacy artifacts are left on
their prior validation path with a warning; their prior validation path with a warning;
malformed or unsupported markers are errors. malformed or unsupported markers are errors.
- PPTX-family inputs are enriched automatically under `analysis/` with - PPTX-family inputs are enriched automatically under `analysis/` with
@@ -72,14 +78,18 @@ changes JSON formatting only. Before projection it revalidates the machine lock
and selected template-root identities; design-brief values are not treated as and selected template-root identities; design-brief values are not treated as
a second lock. Slide headings at H3H6 remain readable by the projector. a second lock. Slide headings at H3H6 remain readable by the projector.
The output deliberately repeats the bounded `global` lock projection on every The output deliberately repeats the bounded `global` anchor set on every
page as an anti-drift guard. `lock_source` binds that projection to the current page as a cross-page anchor set, not a color/font allowlist. `lock_source` binds that projection to the current
`spec_lock.md` SHA. `page_context` contains the current §IX brief, rhythm, `spec_lock.md` SHA. `page_context` contains the current §IX brief, rhythm,
resources, and conditional template/chart assignment. `reference_set` contains resources, and conditional template/chart assignment. `reference_set` contains
only `kind`, scoped path, SHA, and `once-per-execution-context` policy for the `kind`, scoped path, SHA, and `once-per-execution-context` policy for the
project/template Design Specs and selected prototype/chart SVGs. A model reads project/template Design Specs and selected prototype/chart SVGs. The project
a referenced file only when that exact path + SHA is absent from its active Design Spec additionally carries
context or has changed, then reuses the retained understanding on later pages. `same_context_edit_policy: targeted-readback-and-rebind`: when the current main
agent makes a bounded repair that preserves roster/order/identity/communication,
it reads back only the exact changed fragments,
validates, reruns `page-context`, and binds the verified delta to the new SHA.
Fresh, external, unknown, or mismatched changes still require a complete read.
The deprecated `--bundle` flag remains accepted as a compatibility no-op. It The deprecated `--bundle` flag remains accepted as a compatibility no-op. It
never appends a Design Spec, prototype SVG, chart SVG, manifest, or text-slot never appends a Design Spec, prototype SVG, chart SVG, manifest, or text-slot
@@ -122,8 +132,8 @@ Examples:
```bash ```bash
python3 scripts/project_manager.py init my_presentation --format ppt169 python3 scripts/project_manager.py init my_presentation --format ppt169
python3 scripts/project_manager.py scaffold-spec projects/my_presentation_ppt169_20251116 python3 scripts/project_manager.py scaffold-spec projects/my_presentation_ppt169_20251116 # optional
python3 scripts/project_manager.py scaffold-lock projects/my_presentation_ppt169_20251116 python3 scripts/project_manager.py scaffold-lock projects/my_presentation_ppt169_20251116 # optional
python3 scripts/project_manager.py validate projects/my_presentation_ppt169_20251116 python3 scripts/project_manager.py validate projects/my_presentation_ppt169_20251116
python3 scripts/project_manager.py info projects/my_presentation_ppt169_20251116 python3 scripts/project_manager.py info projects/my_presentation_ppt169_20251116
python3 scripts/project_manager.py page-context projects/my_presentation_ppt169_20251116 P07 --record-usage python3 scripts/project_manager.py page-context projects/my_presentation_ppt169_20251116 P07 --record-usage
@@ -336,7 +336,7 @@ Behavior:
- Missing root bounds fails on final pages/templates and under `--template-mode`; references warn until adapted. - Missing root bounds fails on final pages/templates and under `--template-mode`; references warn until adapted.
- On structured template routes, each normal slot is a direct root `<g id>` with semantic type, positive design-zone bounds, and exactly one compatible carrier. Composite `object` slots use explicit proxy binding; zero-slot Layouts are valid. Flat pages keep all SVG objects Slide-local. - On structured template routes, each normal slot is a direct root `<g id>` with semantic type, positive design-zone bounds, and exactly one compatible carrier. Composite `object` slots use explicit proxy binding; zero-slot Layouts are valid. Flat pages keep all SVG objects Slide-local.
- Flat export maps locked typography/colors into a clean project-owned theme/Master, removes stock content placeholders and unused built-in Layouts, retains only the standard date/footer/slide-number capability hooks, and keeps one Blank Layout without promoting Slide content. Structured export additionally creates one reusable Layout per declared key and reopens the package to verify the full Presentation → Master → Layout → Slide graph, fixed-object order, placeholder identities/bounds, carrier bindings, hidden proxies, and zero-slot Layouts. - Flat export maps locked typography/colors into a clean project-owned theme/Master, removes stock content placeholders and unused built-in Layouts, retains only the standard date/footer/slide-number capability hooks, and keeps one Blank Layout without promoting Slide content. Structured export additionally creates one reusable Layout per declared key and reopens the package to verify the full Presentation → Master → Layout → Slide graph, fixed-object order, placeholder identities/bounds, carrier bindings, hidden proxies, and zero-slot Layouts.
- Template `page_layouts` remains input provenance. Strict preserves the prototype contract; adaptive retains its Master and may use a new Layout identity only when fixed Layout atoms or slot topology/bounds change. - Template `page_layouts` remains input provenance. Strict preserves the prototype contract; adaptive retains its Master and may use a new Layout identity only when Strategist declared it in the plan and lock. Construction cannot allocate or mutate Layout identity downstream.
- Legacy structured/template contracts using `baseline`, `template`, `preserve`, `layout_strategy`, `data-pptx-layout-kind`, `distilled`/`utility`, direct atomic placeholders, or incomplete Master identity are rejected with a pointer to [`create-template`](../../workflows/create-template.md). Create a new workspace and generate new structured SVG pages; do not upgrade the existing project in place. Explicit flat free-design/brand-only projects intentionally omit Master identity. - Legacy structured/template contracts using `baseline`, `template`, `preserve`, `layout_strategy`, `data-pptx-layout-kind`, `distilled`/`utility`, direct atomic placeholders, or incomplete Master identity are rejected with a pointer to [`create-template`](../../workflows/create-template.md). Create a new workspace and generate new structured SVG pages; do not upgrade the existing project in place. Explicit flat free-design/brand-only projects intentionally omit Master identity.
- Native output uses content-hash media filenames, so identical images are reused and different images cannot overwrite each other by sharing a basename. - Native output uses content-hash media filenames, so identical images are reused and different images cannot overwrite each other by sharing a basename.
- `[Content_Types].xml` is generated from the actual media extensions written into the PPTX. Unknown media extensions fail unless Python's `mimetypes` can identify them. - `[Content_Types].xml` is generated from the actual media extensions written into the PPTX. Unknown media extensions fail unless Python's `mimetypes` can identify them.
@@ -358,7 +358,7 @@ Behavior:
- Long-audio import and automatic long-audio splitting are not supported; keep narration assets page-level - Long-audio import and automatic long-audio splitting are not supported; keep narration assets page-level
- Voice choices can be listed with `python3 scripts/notes_to_audio.py --list-common-voices`, `python3 scripts/notes_to_audio.py --list-voices --locale zh-CN`, or provider-specific `--provider <name> --list-voices` - Voice choices can be listed with `python3 scripts/notes_to_audio.py --list-common-voices`, `python3 scripts/notes_to_audio.py --list-voices --locale zh-CN`, or provider-specific `--provider <name> --list-voices`
- Page transitions are controlled by `-t/--transition`; per-element entrance animations are controlled by `-a/--animation` - Page transitions are controlled by `-t/--transition`; per-element entrance animations are controlled by `-a/--animation`
- Per-element animation applies to ordinary top-level SVG `<g id="...">` groups in z-order; aim for 38 Slide-local content groups per slide. Master/Layout atoms and slot groups are structural and excluded; exact id tokens remain a fallback only when explicit structural roles are absent - Per-element animation applies to ordinary top-level SVG `<g id="...">` groups in z-order; use one group per logical Slide-local content unit rather than targeting a group count. Master/Layout atoms and slot groups are structural and excluded; exact id tokens remain a fallback only when explicit structural roles are absent
- An explicit `animations.json` group entry may override the marker-free legacy chrome-name heuristic. It cannot override `data-pptx-layer` or an explicit static role/placeholder marker - An explicit `animations.json` group entry may override the marker-free legacy chrome-name heuristic. It cannot override `data-pptx-layer` or an explicit static role/placeholder marker
- Start mode is set by `--animation-trigger`, mirroring PowerPoint's Start dropdown: `after-previous` (default, cascade with `--animation-stagger` spacing on slide entry), `on-click` (presenter-paced), `with-previous` (all together on slide entry) - Start mode is set by `--animation-trigger`, mirroring PowerPoint's Start dropdown: `after-previous` (default, cascade with `--animation-stagger` spacing on slide entry), `on-click` (presenter-paced), `with-previous` (all together on slide entry)
- `on-click` is for live presentations only; recorded narration rejects it because the tool does not generate object-level click timings - `on-click` is for live presentations only; recorded narration rejects it because the tool does not generate object-level click timings
@@ -2,22 +2,19 @@
""" """
PPT Master - Icon Sync PPT Master - Icon Sync
Copy chosen library icons into a project's own `icons/` folder at the moment they Copy chosen library icons into `<project>/icons/<lib>/` when selected. Missing
are selected. Run it with the icon names you are picking; each is copied from the names exit non-zero before export. Known basenames need no separate existence
global library into `<project>/icons/<lib>/`. Any name the library does not have check; search the chosen library only for unresolved concepts.
is reported on the spot and the command exits non-zero so you re-pick a valid
icon then, not at export time. Over-copying candidates is fine: finalize only
embeds the icons actually referenced by `<use data-icon>`, the rest sit unused.
Custom icons you place in `<project>/icons/<lib>/` yourself are honored too a Project-local custom icons count as satisfied. `simple-icons` may accompany one
name already present in the project is treated as satisfied, not missing. stylistic library only for real brand marks.
Usage: Usage:
python3 scripts/icon_sync.py <project_path> <lib/name> [<lib/name> ...] python3 scripts/icon_sync.py <project_path> <lib/name> [<lib/name> ...]
Examples: Examples:
python3 scripts/icon_sync.py projects/deck chunk-filled/home tabler-outline/chart python3 scripts/icon_sync.py projects/deck tabler-outline/home tabler-outline/chart
python3 scripts/icon_sync.py projects/deck simple-icons/github python3 scripts/icon_sync.py projects/deck tabler-outline/home simple-icons/github
Dependencies: Dependencies:
None (standard library only). None (standard library only).
@@ -38,6 +35,12 @@ from console_encoding import configure_utf8_stdio
configure_utf8_stdio() configure_utf8_stdio()
_LIB_ALIASES = {"chunk": "chunk-filled"} _LIB_ALIASES = {"chunk": "chunk-filled"}
_STYLISTIC_LIBRARIES = {
"chunk-filled",
"phosphor-duotone",
"tabler-filled",
"tabler-outline",
}
_GLOBAL_ICONS_DIR = Path(__file__).resolve().parent.parent / "templates" / "icons" _GLOBAL_ICONS_DIR = Path(__file__).resolve().parent.parent / "templates" / "icons"
@@ -93,6 +96,19 @@ def main(argv: Optional[list[str]] = None) -> int:
print(f"[ERROR] project not found: {project}", file=sys.stderr) print(f"[ERROR] project not found: {project}", file=sys.stderr)
return 1 return 1
requested_libraries = {_split_name(raw)[0] for raw in args.icons}
stylistic_libraries = sorted(requested_libraries & _STYLISTIC_LIBRARIES)
if len(stylistic_libraries) > 1:
print(
f"[ERROR] mixed stylistic icon libraries: {', '.join(stylistic_libraries)}",
file=sys.stderr,
)
print(
"Choose one stylistic library per deck; simple-icons may coexist for real brand marks.",
file=sys.stderr,
)
return 1
copied, missing = sync_icons(project, args.icons) copied, missing = sync_icons(project, args.icons)
if copied: if copied:
@@ -104,7 +120,10 @@ def main(argv: Optional[list[str]] = None) -> int:
print(f"\n[MISSING] {len(missing)} icon(s) not in the library — re-pick before continuing:", file=sys.stderr) print(f"\n[MISSING] {len(missing)} icon(s) not in the library — re-pick before continuing:", file=sys.stderr)
for m in missing: for m in missing:
lib = m.split("/", 1)[0] lib = m.split("/", 1)[0]
print(f"{m} (search: ls {_GLOBAL_ICONS_DIR}/{lib}/ | grep <keyword>)", file=sys.stderr) print(
f'{m} (search: rg --files "{_GLOBAL_ICONS_DIR / lib}" -g \'*<keyword>*.svg\')',
file=sys.stderr,
)
return 1 return 1
return 0 return 0
@@ -398,15 +398,19 @@ def _reference_payload(
*, *,
scope: str, scope: str,
display_path: str, display_path: str,
same_context_edit_policy: str | None = None,
) -> dict[str, str]: ) -> dict[str, str]:
"""Describe one large reference without injecting its contents per page.""" """Describe one large reference without injecting its contents per page."""
return { payload = {
"kind": kind, "kind": kind,
"scope": scope, "scope": scope,
"path": display_path, "path": display_path,
"sha256": _file_sha256(path), "sha256": _file_sha256(path),
"load_policy": "once-per-execution-context", "load_policy": "once-per-execution-context",
} }
if same_context_edit_policy is not None:
payload["same_context_edit_policy"] = same_context_edit_policy
return payload
def _chart_reference(chart_key: str) -> tuple[dict[str, str], Path]: def _chart_reference(chart_key: str) -> tuple[dict[str, str], Path]:
@@ -517,6 +521,7 @@ def build_page_context(project: str | Path, raw_page: str) -> PageContextResult:
design_path, design_path,
scope="project", scope="project",
display_path="design_spec.md", display_path="design_spec.md",
same_context_edit_policy="targeted-readback-and-rebind",
), ),
] ]
template_design_path = project_path / "templates" / "design_spec.md" template_design_path = project_path / "templates" / "design_spec.md"
@@ -546,8 +551,8 @@ def build_page_context(project: str | Path, raw_page: str) -> PageContextResult:
reference_set.append(chart_reference) reference_set.append(chart_reference)
mode_fields = _section_fields(lock_sections, "mode") mode_fields = _section_fields(lock_sections, "mode")
visual_style_fields = _section_fields(lock_sections, "visual_style") visual_style_fields = _section_fields(lock_sections, "visual_style")
# Repeat this bounded projection per page intentionally: exact lock values # Repeat this bounded projection per page intentionally: stable lock roles
# are the anti-drift guard; large reference payloads use reference_set. # are continuity anchors, while large reference payloads use reference_set.
global_context = { global_context = {
"communication": _section_fields(lock_sections, "communication"), "communication": _section_fields(lock_sections, "communication"),
"canvas": _section_fields(lock_sections, "canvas"), "canvas": _section_fields(lock_sections, "canvas"),
@@ -586,7 +591,7 @@ def build_page_context(project: str | Path, raw_page: str) -> PageContextResult:
"lock_source": { "lock_source": {
"path": "spec_lock.md", "path": "spec_lock.md",
"sha256": _file_sha256(lock_path), "sha256": _file_sha256(lock_path),
"load_policy": "per-page-drift-guard", "load_policy": "per-page-context-anchors",
}, },
"global": global_context, "global": global_context,
"page_context": current_page, "page_context": current_page,
@@ -58,6 +58,7 @@ except ImportError:
TOOLS_DIR = Path(__file__).resolve().parent TOOLS_DIR = Path(__file__).resolve().parent
SKILL_DIR = TOOLS_DIR.parent SKILL_DIR = TOOLS_DIR.parent
REPO_ROOT = SKILL_DIR.parent.parent REPO_ROOT = SKILL_DIR.parent.parent
PROJECTS_ROOT = REPO_ROOT / "projects"
SOURCE_TO_MD_TOOLS_DIR = TOOLS_DIR / "source_to_md" SOURCE_TO_MD_TOOLS_DIR = TOOLS_DIR / "source_to_md"
if str(SOURCE_TO_MD_TOOLS_DIR) not in sys.path: if str(SOURCE_TO_MD_TOOLS_DIR) not in sys.path:
sys.path.insert(0, str(SOURCE_TO_MD_TOOLS_DIR)) sys.path.insert(0, str(SOURCE_TO_MD_TOOLS_DIR))
@@ -707,19 +708,25 @@ class ProjectManager:
summary["skipped"].append(f"{item}: directories are not supported") summary["skipped"].append(f"{item}: directories are not supported")
continue continue
inside_projects = is_within_path(source_path, PROJECTS_ROOT)
if copy: if copy:
effective_move = False effective_move = False
elif move: elif inside_projects:
effective_move = True effective_move = True
elif is_within_path(source_path, REPO_ROOT):
effective_move = True
print(
f"note: {source_path} is inside the ppt-master repo; moved "
f"(not copied) to avoid accidental commit. Pass --copy to override.",
file=sys.stderr,
)
else: else:
effective_move = False effective_move = False
if move and not inside_projects:
print(
f"note: {source_path} is outside {PROJECTS_ROOT}; copied "
f"(not moved). Only sources under projects/ may be moved.",
file=sys.stderr,
)
elif inside_projects and not move and not copy:
print(
f"note: {source_path} is under projects/; moved into the target "
f"project. Pass --copy to preserve it.",
file=sys.stderr,
)
suffix = source_path.suffix.lower() suffix = source_path.suffix.lower()
if suffix in {".md", ".markdown"}: if suffix in {".md", ".markdown"}:
@@ -859,12 +866,11 @@ class ProjectManager:
else: else:
summary["notes"].append(f"{item}: archived only, no automatic conversion") summary["notes"].append(f"{item}: archived only, no automatic conversion")
# Cleanup: a supplied source directory that ends up empty (moved out or # Cleanup: only a projects-local source directory may be removed after
# empty from the start) is removed so no husk is left behind. Only under # its files move into the target project. Every other location is copied
# move semantics — explicit --copy, or default copy outside the repo, # and remains untouched, even when the caller passes --move.
# must not delete a user directory.
for directory in supplied_dirs: for directory in supplied_dirs:
if copy or not (move or is_within_path(directory, REPO_ROOT)): if copy or not is_within_path(directory, PROJECTS_ROOT):
continue continue
if directory.is_dir() and not any(directory.iterdir()): if directory.is_dir() and not any(directory.iterdir()):
try: try:
@@ -926,7 +932,7 @@ def build_parser() -> argparse.ArgumentParser:
formatter_class=argparse.RawDescriptionHelpFormatter, formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""Examples: epilog="""Examples:
python3 scripts/project_manager.py init demo --format ppt169 python3 scripts/project_manager.py init demo --format ppt169
python3 scripts/project_manager.py import-sources projects/demo file.md --move python3 scripts/project_manager.py import-sources projects/demo file.md
python3 scripts/project_manager.py scaffold-spec projects/demo_ppt169_20260718 python3 scripts/project_manager.py scaffold-spec projects/demo_ppt169_20260718
python3 scripts/project_manager.py scaffold-lock projects/demo_ppt169_20260718 python3 scripts/project_manager.py scaffold-lock projects/demo_ppt169_20260718
python3 scripts/project_manager.py validate projects/demo python3 scripts/project_manager.py validate projects/demo
@@ -949,7 +955,11 @@ def build_parser() -> argparse.ArgumentParser:
import_sources.add_argument("project_path", help="Project directory") import_sources.add_argument("project_path", help="Project directory")
import_sources.add_argument("sources", nargs="+", help="Source files, directories, or URLs") import_sources.add_argument("sources", nargs="+", help="Source files, directories, or URLs")
mode = import_sources.add_mutually_exclusive_group() mode = import_sources.add_mutually_exclusive_group()
mode.add_argument("--move", action="store_true", help="Move local source files") mode.add_argument(
"--move",
action="store_true",
help="Move local sources under projects/; sources elsewhere are copied",
)
mode.add_argument("--copy", action="store_true", help="Copy local source files") mode.add_argument("--copy", action="store_true", help="Copy local source files")
scaffold_spec = subparsers.add_parser( scaffold_spec = subparsers.add_parser(
@@ -47,6 +47,22 @@ SKILL_DIR = TOOLS_DIR.parent
SCHEMA_DIR = SKILL_DIR / "templates" / "schemas" SCHEMA_DIR = SKILL_DIR / "templates" / "schemas"
SCAFFOLD_DIR = SKILL_DIR / "templates" / "scaffolds" SCAFFOLD_DIR = SKILL_DIR / "templates" / "scaffolds"
_CUSTOM_REFERENCE_CATALOGS = (
("mode", "mode", "mode_references", SKILL_DIR / "references" / "modes"),
(
"visual_style",
"visual_style",
"visual_style_references",
SKILL_DIR / "references" / "visual-styles",
),
(
"colors",
"image_rendering",
"image_rendering_references",
SKILL_DIR / "references" / "image-renderings",
),
)
_MARKDOWN_H2_RE = re.compile(r"^##[ \t]+(.+?)[ \t]*$", re.MULTILINE) _MARKDOWN_H2_RE = re.compile(r"^##[ \t]+(.+?)[ \t]*$", re.MULTILINE)
_MARKDOWN_SUBHEADING_RE = re.compile(r"^#{3,6}[ \t]+(.+?)[ \t]*$", re.MULTILINE) _MARKDOWN_SUBHEADING_RE = re.compile(r"^#{3,6}[ \t]+(.+?)[ \t]*$", re.MULTILINE)
_MARKDOWN_DATA_LINE_RE = re.compile( _MARKDOWN_DATA_LINE_RE = re.compile(
@@ -59,18 +75,6 @@ _SCHEMA_MARKER_RE = re.compile(
re.IGNORECASE, re.IGNORECASE,
) )
# Confirmed `image_usage` source id → acceptable `## images` acquisition tokens.
# Mirrors the strategist.md §h mapping (ai→ai, web→web, provided→user,
# placeholder→placeholder); a confirmed `ai` plan may legitimately enter the
# lock only as sliced sheet elements, so `slice` also satisfies `ai`.
_CONFIRMED_IMAGE_SOURCE_TOKENS = {
"ai": ("ai", "slice"),
"web": ("web",),
"provided": ("user",),
"placeholder": ("placeholder",),
}
def _normalize_schema_value(value: str) -> str: def _normalize_schema_value(value: str) -> str:
"""Normalize a Markdown scalar before enum, pattern, and catalog checks.""" """Normalize a Markdown scalar before enum, pattern, and catalog checks."""
normalized = value.strip() normalized = value.strip()
@@ -693,40 +697,6 @@ def _validate_strict_data_surface(
return errors return errors
def _confirmed_image_sources(project_dir: Path) -> list[str]:
"""Return confirmed non-`none` image sources from the final Confirm UI state.
Reads ``confirm_ui/result.json`` only when it records a final confirmed
stage; a chat-delegated or superseded confirmation without that file keeps
the coverage check silent. Malformed payloads are treated as absent
the Confirm UI owns result integrity, not this validator.
"""
result_path = project_dir / "confirm_ui" / "result.json"
if not result_path.is_file():
return []
try:
payload = json.loads(result_path.read_text(encoding="utf-8-sig"))
except (OSError, UnicodeError, json.JSONDecodeError):
return []
if not isinstance(payload, dict):
return []
if payload.get("status") != "confirmed" or payload.get("stage") != "final":
return []
raw_usage = payload.get("image_usage")
if isinstance(raw_usage, str):
raw_usage = [raw_usage]
if not isinstance(raw_usage, list):
return []
sources: list[str] = []
for item in raw_usage:
if not isinstance(item, str):
continue
token = item.strip().casefold()
if token and token != "none" and token not in sources:
sources.append(token)
return sources
def _validate_spec_lock_relations( def _validate_spec_lock_relations(
markdown_path: Path, markdown_path: Path,
matched: Mapping[str, dict[str, object] | None], matched: Mapping[str, dict[str, object] | None],
@@ -743,6 +713,44 @@ def _validate_spec_lock_relations(
assert isinstance(raw_fields, dict) assert isinstance(raw_fields, dict)
return {str(key): str(value) for key, value in raw_fields.items()} return {str(key): str(value) for key, value in raw_fields.items()}
for section_id, selector_field, references_field, catalog_dir in (
_CUSTOM_REFERENCE_CATALOGS
):
section_fields = fields(section_id)
is_custom = (
_normalize_schema_value(section_fields.get(selector_field, "")) == "custom"
)
raw_references = _normalize_schema_value(
section_fields.get(references_field, "")
)
if not is_custom:
if raw_references:
errors.append(
f"{markdown_name} schema: field '{references_field}' is valid "
f"only when '{selector_field}' is custom"
)
continue
if not raw_references:
continue
references = [item.strip() for item in raw_references.split(",")]
duplicates = sorted(
reference
for reference in set(references)
if references.count(reference) > 1
)
if duplicates:
errors.append(
f"{markdown_name} schema: field '{references_field}' repeats "
f"catalog id(s) {', '.join(duplicates)}"
)
for reference in references:
catalog_file = catalog_dir / f"{reference}.md"
if reference == "custom" or not catalog_file.is_file():
errors.append(
f"{markdown_name} schema: field '{references_field}' references "
f"unknown catalog id '{reference}'"
)
rhythm = fields("page_rhythm") rhythm = fields("page_rhythm")
layouts = fields("pptx_layouts") layouts = fields("pptx_layouts")
page_pptx_layouts = fields("page_pptx_layouts") page_pptx_layouts = fields("page_pptx_layouts")
@@ -797,26 +805,6 @@ def _validate_spec_lock_relations(
f"{', '.join(unknown_chart_pages)}" f"{', '.join(unknown_chart_pages)}"
) )
confirmed_sources = _confirmed_image_sources(markdown_path.parent)
if confirmed_sources:
# Row values are free-form beyond the leading acquisition source;
# both `ai | ...` and `ai, ...` delimiter styles occur in practice.
image_tokens = {
_normalize_schema_value(re.split(r"[|,]", value, maxsplit=1)[0]).casefold()
for value in fields("images").values()
}
for source in confirmed_sources:
accepted = _CONFIRMED_IMAGE_SOURCE_TOKENS.get(source)
if accepted is None or not image_tokens.isdisjoint(accepted):
continue
expected = " or ".join(f"'{token}'" for token in accepted)
errors.append(
f"{markdown_name} schema: confirmed image source '{source}' "
f"(confirm_ui/result.json image_usage) has no '## images' row "
f"with acquisition {expected}; repair design_spec.md §VIII "
"from the final confirmation, then re-project the lock"
)
info = get_project_info_common(str(markdown_path.parent)) info = get_project_info_common(str(markdown_path.parent))
format_key = str(info.get("format", "unknown")) format_key = str(info.get("format", "unknown"))
canvas = CANVAS_FORMATS.get(format_key) canvas = CANVAS_FORMATS.get(format_key)
@@ -17,10 +17,10 @@
"file_budgets": { "file_budgets": {
"AGENTS.md": 2300, "AGENTS.md": 2300,
"skills/ppt-master/SKILL.md": 800, "skills/ppt-master/SKILL.md": 800,
"skills/ppt-master/references/executor-base.md": 6900, "skills/ppt-master/references/executor-base.md": 7060,
"skills/ppt-master/references/executor-structured.md": 5300, "skills/ppt-master/references/executor-structured.md": 5300,
"skills/ppt-master/references/executor-chart.md": 3100, "skills/ppt-master/references/executor-chart.md": 3100,
"skills/ppt-master/references/executor-image.md": 800, "skills/ppt-master/references/executor-image.md": 900,
"skills/ppt-master/references/executor-web-image.md": 500, "skills/ppt-master/references/executor-web-image.md": 500,
"skills/ppt-master/references/executor-notes.md": 900, "skills/ppt-master/references/executor-notes.md": 900,
"skills/ppt-master/references/shared-standards.md": 250, "skills/ppt-master/references/shared-standards.md": 250,
@@ -28,12 +28,12 @@
"skills/ppt-master/references/svg-effects.md": 11600, "skills/ppt-master/references/svg-effects.md": 11600,
"skills/ppt-master/references/native-data-interface.md": 6200, "skills/ppt-master/references/native-data-interface.md": 6200,
"skills/ppt-master/references/pptx-structure-interface.md": 4300, "skills/ppt-master/references/pptx-structure-interface.md": 4300,
"skills/ppt-master/references/strategist.md": 12250, "skills/ppt-master/references/strategist.md": 13350,
"skills/ppt-master/references/strategist-image.md": 1800, "skills/ppt-master/references/strategist-image.md": 2100,
"skills/ppt-master/references/strategist-template.md": 2100, "skills/ppt-master/references/strategist-template.md": 2100,
"skills/ppt-master/templates/design_spec_reference.md": 800, "skills/ppt-master/templates/design_spec_reference.md": 2650,
"skills/ppt-master/templates/spec_lock_reference.md": 1200, "skills/ppt-master/templates/spec_lock_reference.md": 2100,
"skills/ppt-master/workflows/generate-pptx.md": 11600, "skills/ppt-master/workflows/generate-pptx.md": 12450,
"skills/ppt-master/workflows/stages/apply-template-workspace.md": 1800 "skills/ppt-master/workflows/stages/apply-template-workspace.md": 1800
}, },
"load_sets": { "load_sets": {
@@ -77,7 +77,7 @@
"skills/ppt-master/templates/README.md", "skills/ppt-master/templates/README.md",
"skills/ppt-master/templates/decks/README.md" "skills/ppt-master/templates/decks/README.md"
], ],
"max_tokens": 57100 "max_tokens": 57250
}, },
"route.create-template.layout": { "route.create-template.layout": {
"description": "Create Layout path through Template_Designer, SVG core, and the structured PPTX interface.", "description": "Create Layout path through Template_Designer, SVG core, and the structured PPTX interface.",
@@ -95,7 +95,7 @@
"skills/ppt-master/templates/README.md", "skills/ppt-master/templates/README.md",
"skills/ppt-master/templates/layouts/README.md" "skills/ppt-master/templates/layouts/README.md"
], ],
"max_tokens": 57300 "max_tokens": 57400
}, },
"route.enhance-native-pptx": { "route.enhance-native-pptx": {
"description": "Finished-PPTX native enhancement route.", "description": "Finished-PPTX native enhancement route.",
@@ -106,7 +106,7 @@
"files": [ "files": [
"skills/ppt-master/workflows/native-enhance-pptx.md" "skills/ppt-master/workflows/native-enhance-pptx.md"
], ],
"max_tokens": 8100 "max_tokens": 8200
}, },
"route.fill-native-pptx": { "route.fill-native-pptx": {
"description": "Raw-PPTX native fill route.", "description": "Raw-PPTX native fill route.",
@@ -120,7 +120,7 @@
"max_tokens": 10800 "max_tokens": 10800
}, },
"route.generate.planning": { "route.generate.planning": {
"description": "Fixed Generate-PPTX planning context through the Strategist core; schemas and scaffolds are tool-consumed.", "description": "Generate-PPTX planning through Strategist, including reference-first whole-document authoring and representative multi-source custom mode/style synthesis; schemas and optional scaffolds are tool-consumed.",
"scope": "cumulative", "scope": "cumulative",
"include": [ "include": [
"bootstrap.routing" "bootstrap.routing"
@@ -129,21 +129,32 @@
"skills/ppt-master/workflows/generate-pptx.md", "skills/ppt-master/workflows/generate-pptx.md",
"skills/ppt-master/references/artifact-ownership.md", "skills/ppt-master/references/artifact-ownership.md",
"skills/ppt-master/references/strategist.md", "skills/ppt-master/references/strategist.md",
"skills/ppt-master/templates/design_spec_reference.md",
"skills/ppt-master/templates/spec_lock_reference.md",
"skills/ppt-master/references/modes/_index.md", "skills/ppt-master/references/modes/_index.md",
"skills/ppt-master/references/visual-styles/_index.md", "skills/ppt-master/references/visual-styles/_index.md",
"skills/ppt-master/scripts/docs/confirm_ui.md", "skills/ppt-master/scripts/docs/confirm_ui.md",
"skills/ppt-master/templates/icons/README.md", "skills/ppt-master/templates/icons/README.md",
{
"glob": "skills/ppt-master/references/modes/*.md",
"exclude": [
"*/_index.md"
],
"select": 3,
"load_event": "strategist-custom",
"registry": "modes"
},
{ {
"glob": "skills/ppt-master/references/visual-styles/*.md", "glob": "skills/ppt-master/references/visual-styles/*.md",
"exclude": [ "exclude": [
"*/_index.md" "*/_index.md"
], ],
"select": 1, "select": 3,
"load_event": "strategist", "load_event": "strategist-custom",
"registry": "visual-styles" "registry": "visual-styles"
} }
], ],
"max_tokens": 47000 "max_tokens": 62000
}, },
"route.generate.planning-image": { "route.generate.planning-image": {
"description": "Generate-PPTX planning context after a non-none image source is proposed or confirmed.", "description": "Generate-PPTX planning context after a non-none image source is proposed or confirmed.",
@@ -153,7 +164,7 @@
"stage.generate.strategist.image-layout" "stage.generate.strategist.image-layout"
], ],
"files": [], "files": [],
"max_tokens": 54500 "max_tokens": 70000
}, },
"route.generate.planning-formula": { "route.generate.planning-formula": {
"description": "Formula-only planning context with no non-formula image resource or layout catalog.", "description": "Formula-only planning context with no non-formula image resource or layout catalog.",
@@ -163,18 +174,27 @@
"stage.generate.strategist.image" "stage.generate.strategist.image"
], ],
"files": [], "files": [],
"max_tokens": 48500 "max_tokens": 64000
}, },
"route.generate.planning-ai": { "route.generate.planning-ai": {
"description": "Generate-PPTX planning context when AI image direction is available.", "description": "Generate-PPTX planning when AI image direction is available, including representative multi-source custom rendering synthesis.",
"scope": "cumulative", "scope": "cumulative",
"include": [ "include": [
"route.generate.planning-image" "route.generate.planning-image"
], ],
"files": [ "files": [
"skills/ppt-master/references/image-renderings/_index.md" "skills/ppt-master/references/image-renderings/_index.md",
{
"glob": "skills/ppt-master/references/image-renderings/*.md",
"exclude": [
"*/_index.md"
], ],
"max_tokens": 56500 "select": 3,
"load_event": "strategist-image-custom",
"registry": "image-renderings"
}
],
"max_tokens": 76000
}, },
"stage.generate.apply-template-workspace": { "stage.generate.apply-template-workspace": {
"description": "Conditional Step 3 workspace validation, installation, and fusion framework.", "description": "Conditional Step 3 workspace validation, installation, and fusion framework.",
@@ -286,10 +306,10 @@
"stage.generate.executor.notes" "stage.generate.executor.notes"
], ],
"files": [], "files": [],
"max_tokens": 67650 "max_tokens": 81250
}, },
"route.generate.flat-ai-two-types": { "route.generate.flat-ai-two-types": {
"description": "Generate-PPTX context with AI images, one rendering, and two local types.", "description": "Generate-PPTX context with AI images, representative multi-source custom rendering, and two local types.",
"scope": "cumulative", "scope": "cumulative",
"include": [ "include": [
"route.generate.planning-ai", "route.generate.planning-ai",
@@ -299,7 +319,7 @@
"stage.generate.image.ai-two-types" "stage.generate.image.ai-two-types"
], ],
"files": [], "files": [],
"max_tokens": 103500 "max_tokens": 121450
}, },
"route.generate.flat-in-hand-image": { "route.generate.flat-in-hand-image": {
"description": "Generate-PPTX context with provided, placeholder, or formula images and no acquisition role.", "description": "Generate-PPTX context with provided, placeholder, or formula images and no acquisition role.",
@@ -311,7 +331,7 @@
"stage.generate.executor.notes" "stage.generate.executor.notes"
], ],
"files": [], "files": [],
"max_tokens": 80650 "max_tokens": 94750
}, },
"route.generate.flat-web-image": { "route.generate.flat-web-image": {
"description": "Generate-PPTX context with web image acquisition.", "description": "Generate-PPTX context with web image acquisition.",
@@ -324,7 +344,7 @@
"stage.generate.image.web" "stage.generate.image.web"
], ],
"files": [], "files": [],
"max_tokens": 88000 "max_tokens": 102000
}, },
"route.enhance-native-pptx.audio": { "route.enhance-native-pptx.audio": {
"description": "Enhance Native PPTX with the shared narration-audio stage.", "description": "Enhance Native PPTX with the shared narration-audio stage.",
@@ -344,7 +364,7 @@
"profile.generate.beautify-pptx" "profile.generate.beautify-pptx"
], ],
"files": [], "files": [],
"max_tokens": 74400 "max_tokens": 88110
}, },
"route.generate.flat-no-image-chart": { "route.generate.flat-no-image-chart": {
"description": "Default flat Generate-PPTX path with chart authoring, native-data replacement, and verification.", "description": "Default flat Generate-PPTX path with chart authoring, native-data replacement, and verification.",
@@ -357,7 +377,7 @@
"stage.generate.verify-charts" "stage.generate.verify-charts"
], ],
"files": [], "files": [],
"max_tokens": 94900 "max_tokens": 105250
}, },
"route.generate.brand-flat-no-image": { "route.generate.brand-flat-no-image": {
"description": "Brand-preset flat Generate-PPTX path without image acquisition.", "description": "Brand-preset flat Generate-PPTX path without image acquisition.",
@@ -369,7 +389,7 @@
"stage.generate.template.brand" "stage.generate.template.brand"
], ],
"files": [], "files": [],
"max_tokens": 75000 "max_tokens": 88300
}, },
"route.generate.deck-structured-no-image": { "route.generate.deck-structured-no-image": {
"description": "Deck-preset structured Generate-PPTX path without image acquisition.", "description": "Deck-preset structured Generate-PPTX path without image acquisition.",
@@ -381,7 +401,7 @@
"stage.generate.template.deck" "stage.generate.template.deck"
], ],
"files": [], "files": [],
"max_tokens": 84600 "max_tokens": 98100
}, },
"route.generate.layout-structured-no-image": { "route.generate.layout-structured-no-image": {
"description": "Layout-preset structured Generate-PPTX path without image acquisition.", "description": "Layout-preset structured Generate-PPTX path without image acquisition.",
@@ -393,7 +413,7 @@
"stage.generate.template.layout" "stage.generate.template.layout"
], ],
"files": [], "files": [],
"max_tokens": 84900 "max_tokens": 98550
}, },
"route.generate.topic-only-flat-no-image": { "route.generate.topic-only-flat-no-image": {
"description": "Topic research followed by the default flat no-image Generate-PPTX path.", "description": "Topic research followed by the default flat no-image Generate-PPTX path.",
@@ -403,10 +423,10 @@
"route.generate.flat-no-image" "route.generate.flat-no-image"
], ],
"files": [], "files": [],
"max_tokens": 69450 "max_tokens": 82650
}, },
"stage.generate.executor.flat": { "stage.generate.executor.flat": {
"description": "Incremental flat Executor core with one locked mode and visual style.", "description": "Incremental flat Executor core with representative multi-source custom mode/style execution.",
"scope": "incremental", "scope": "incremental",
"files": [ "files": [
"skills/ppt-master/references/executor-base.md", "skills/ppt-master/references/executor-base.md",
@@ -417,22 +437,23 @@
"exclude": [ "exclude": [
"*/_index.md" "*/_index.md"
], ],
"select": 1, "select": 3,
"load_event": "executor", "load_event": "executor",
"registry": "modes" "registry": "modes",
"allow_repeat": true
}, },
{ {
"glob": "skills/ppt-master/references/visual-styles/*.md", "glob": "skills/ppt-master/references/visual-styles/*.md",
"exclude": [ "exclude": [
"*/_index.md" "*/_index.md"
], ],
"select": 1, "select": 3,
"load_event": "executor", "load_event": "executor",
"registry": "visual-styles", "registry": "visual-styles",
"allow_repeat": true "allow_repeat": true
} }
], ],
"max_tokens": 20700 "max_tokens": 36500
}, },
"stage.generate.executor.structured": { "stage.generate.executor.structured": {
"description": "Structured template execution layered on the flat/shared core.", "description": "Structured template execution layered on the flat/shared core.",
@@ -444,10 +465,10 @@
"skills/ppt-master/references/executor-structured.md", "skills/ppt-master/references/executor-structured.md",
"skills/ppt-master/references/pptx-structure-interface.md" "skills/ppt-master/references/pptx-structure-interface.md"
], ],
"max_tokens": 30150 "max_tokens": 37000
}, },
"stage.generate.image.ai-two-types": { "stage.generate.image.ai-two-types": {
"description": "Incremental AI-image role with one preset rendering and two selected local types.", "description": "Incremental AI-image role with representative multi-source custom rendering and two selected local types.",
"scope": "incremental", "scope": "incremental",
"files": [ "files": [
"skills/ppt-master/references/image-base.md", "skills/ppt-master/references/image-base.md",
@@ -464,8 +485,10 @@
"exclude": [ "exclude": [
"*/_index.md" "*/_index.md"
], ],
"select": 1, "select": 3,
"registry": "image-renderings" "load_event": "image-generator",
"registry": "image-renderings",
"allow_repeat": true
}, },
{ {
"glob": "skills/ppt-master/references/image-type-templates/*.md", "glob": "skills/ppt-master/references/image-type-templates/*.md",
@@ -476,7 +499,7 @@
"registry": "image-type-templates" "registry": "image-type-templates"
} }
], ],
"max_tokens": 21000 "max_tokens": 23000
}, },
"stage.generate.image.web": { "stage.generate.image.web": {
"description": "Incremental web-image acquisition role.", "description": "Incremental web-image acquisition role.",
@@ -500,7 +523,7 @@
"skills/ppt-master/workflows/stages/resume-execute.md", "skills/ppt-master/workflows/stages/resume-execute.md",
"skills/ppt-master/references/artifact-ownership.md" "skills/ppt-master/references/artifact-ownership.md"
], ],
"max_tokens": 43000 "max_tokens": 51000
}, },
"stage.generate.resume-execute-image": { "stage.generate.resume-execute-image": {
"description": "Fresh-session resume when the locked resource plan contains image rows.", "description": "Fresh-session resume when the locked resource plan contains image rows.",
@@ -510,10 +533,10 @@
"stage.generate.executor.image" "stage.generate.executor.image"
], ],
"files": [], "files": [],
"max_tokens": 48500 "max_tokens": 58350
}, },
"stage.generate.topic-research": { "stage.generate.topic-research": {
"description": "Topic-only intake stage.", "description": "Gap-targeted factual intake stage.",
"scope": "incremental", "scope": "incremental",
"files": [ "files": [
"skills/ppt-master/workflows/stages/topic-research.md" "skills/ppt-master/workflows/stages/topic-research.md"
@@ -526,7 +549,7 @@
"files": [ "files": [
"skills/ppt-master/workflows/profiles/beautify-pptx.md" "skills/ppt-master/workflows/profiles/beautify-pptx.md"
], ],
"max_tokens": 6800 "max_tokens": 6900
}, },
"stage.generate.refine-spec": { "stage.generate.refine-spec": {
"description": "Explicit post-confirmation spec refinement runbook.", "description": "Explicit post-confirmation spec refinement runbook.",
@@ -580,7 +603,7 @@
"skills/ppt-master/scripts/docs/pptx-transitions.md", "skills/ppt-master/scripts/docs/pptx-transitions.md",
"skills/ppt-master/scripts/docs/svg-pipeline.md" "skills/ppt-master/scripts/docs/svg-pipeline.md"
], ],
"max_tokens": 18500 "max_tokens": 18600
}, },
"stage.generate.animation-options": { "stage.generate.animation-options": {
"description": "Conditional deck-wide transition, auto-advance, and entrance-animation options.", "description": "Conditional deck-wide transition, auto-advance, and entrance-animation options.",
@@ -604,7 +627,7 @@
"files": [ "files": [
"skills/ppt-master/workflows/governance/failure-recovery.md" "skills/ppt-master/workflows/governance/failure-recovery.md"
], ],
"max_tokens": 2300 "max_tokens": 2500
}, },
"stage.shared.conversion-reference": { "stage.shared.conversion-reference": {
"description": "Conditional source-conversion and import compatibility reference.", "description": "Conditional source-conversion and import compatibility reference.",
@@ -638,7 +661,7 @@
"stage.generate.strategist.template" "stage.generate.strategist.template"
], ],
"files": [], "files": [],
"max_tokens": 48800 "max_tokens": 59200
}, },
"route.generate.planning-template-ai": { "route.generate.planning-template-ai": {
"description": "Explicit template workspace plus confirmed AI-image planning.", "description": "Explicit template workspace plus confirmed AI-image planning.",
@@ -648,16 +671,7 @@
"route.generate.planning-ai" "route.generate.planning-ai"
], ],
"files": [], "files": [],
"max_tokens": 58600 "max_tokens": 71400
},
"stage.generate.artifact-schema-guide": {
"description": "Compact human guide loaded only to diagnose planning-artifact schema or scaffold failures.",
"scope": "incremental",
"files": [
"skills/ppt-master/templates/design_spec_reference.md",
"skills/ppt-master/templates/spec_lock_reference.md"
],
"max_tokens": 2050
}, },
"stage.generate.executor.chart": { "stage.generate.executor.chart": {
"description": "Conditional chart/table page execution rules.", "description": "Conditional chart/table page execution rules.",
@@ -673,7 +687,7 @@
"files": [ "files": [
"skills/ppt-master/references/strategist-image.md" "skills/ppt-master/references/strategist-image.md"
], ],
"max_tokens": 1800 "max_tokens": 1950
}, },
"stage.generate.strategist.image-layout": { "stage.generate.strategist.image-layout": {
"description": "Non-formula image planning plus the image-layout pattern catalog required for Section VIII rows.", "description": "Non-formula image planning plus the image-layout pattern catalog required for Section VIII rows.",
@@ -684,7 +698,7 @@
"files": [ "files": [
"skills/ppt-master/references/image-layout-patterns.md" "skills/ppt-master/references/image-layout-patterns.md"
], ],
"max_tokens": 7800 "max_tokens": 8000
}, },
"stage.generate.strategist.template": { "stage.generate.strategist.template": {
"description": "Conditional Strategist module for an explicitly installed template workspace.", "description": "Conditional Strategist module for an explicitly installed template workspace.",
@@ -707,10 +721,11 @@
"scope": "incremental", "scope": "incremental",
"files": [ "files": [
"skills/ppt-master/references/executor-image.md", "skills/ppt-master/references/executor-image.md",
"skills/ppt-master/references/image-layout-patterns.md",
"skills/ppt-master/references/image-layout-spec.md", "skills/ppt-master/references/image-layout-spec.md",
"skills/ppt-master/references/svg-image-embedding.md" "skills/ppt-master/references/svg-image-embedding.md"
], ],
"max_tokens": 5600 "max_tokens": 11600
}, },
"stage.generate.executor.web-image": { "stage.generate.executor.web-image": {
"description": "Conditional sourced-image attribution layered on image execution.", "description": "Conditional sourced-image attribution layered on image execution.",
@@ -721,7 +736,7 @@
"files": [ "files": [
"skills/ppt-master/references/executor-web-image.md" "skills/ppt-master/references/executor-web-image.md"
], ],
"max_tokens": 6100 "max_tokens": 12000
}, },
"stage.generate.executor.notes": { "stage.generate.executor.notes": {
"description": "Post-SVG speaker-notes generation rules.", "description": "Post-SVG speaker-notes generation rules.",
@@ -1099,7 +1114,7 @@
}, },
{ {
"glob": "skills/ppt-master/templates/scaffolds/*.md", "glob": "skills/ppt-master/templates/scaffolds/*.md",
"reason": "Versioned tool-copy assets; project_manager materializes them into the project rather than prompt-loading them." "reason": "Optional versioned tool-copy assets; normal Generate authoring reads the reference guides and writes complete artifacts from scratch."
}, },
{ {
"glob": "skills/ppt-master/templates/charts/charts_index.json", "glob": "skills/ppt-master/templates/charts/charts_index.json",
@@ -54,7 +54,7 @@ from svg_to_pptx.canvas_contract import (
try: try:
from update_spec import parse_lock as _parse_spec_lock from update_spec import parse_lock as _parse_spec_lock
except ImportError: except ImportError:
_parse_spec_lock = None # spec_lock drift check will be skipped _parse_spec_lock = None # spec_lock anchor comparison will be skipped
try: try:
from svg_to_pptx.animation_config import ( from svg_to_pptx.animation_config import (
@@ -885,15 +885,10 @@ PPT_SAFE_FONTS = {
'impact', 'impact',
} }
# Ramp envelope for font-size drift detection. # Cheap numeric envelope for font-size role enforcement. Semantic role assignment
# From strategist.md §g — Font Size Ramp: the ramp spans # is prompt-owned; Checker only verifies that a used value is close to at least
# from page-number floor (0.5x body) to cover-title ceiling (5.0x body). # one declared size anchor.
# Intermediate px values within this envelope are permitted per FONT_SIZE_ANCHOR_TOLERANCE_PX = 2.0
# executor-base.md §2.1 ("Executor may use an intermediate size ... provided
# the size's ratio to body falls within the corresponding role's band"); only
# values outside every band — i.e. outside this envelope — are drift.
RAMP_MIN_RATIO = 0.5
RAMP_MAX_RATIO = 5.0
# Oversampling alone does not imply distortion and is often harmless for small # Oversampling alone does not imply distortion and is often harmless for small
# logos. Warn about downscaling only when the source also has material on-disk # logos. Warn about downscaling only when the source also has material on-disk
@@ -901,14 +896,6 @@ RAMP_MAX_RATIO = 5.0
IMAGE_DOWNSIZE_WARN_RATIO = 4.0 IMAGE_DOWNSIZE_WARN_RATIO = 4.0
IMAGE_DOWNSIZE_WARN_MIN_BYTES = 1024 * 1024 IMAGE_DOWNSIZE_WARN_MIN_BYTES = 1024 * 1024
# Modes / visual styles that legitimately use unbounded hero / poster type
# (huge cover numerals, act dividers, single-number reveals). For these the
# size-drift upper bound is dropped — the oversize is the design, not Executor
# drift. The lower bound still applies.
POSTER_SIZE_MODES = {'showcase'}
POSTER_SIZE_STYLES = {'zine'}
def _design_spec_is_brand(spec_path: Path) -> bool: def _design_spec_is_brand(spec_path: Path) -> bool:
"""Return True when a design_spec.md frontmatter declares ``kind: brand``. """Return True when a design_spec.md frontmatter declares ``kind: brand``.
@@ -1165,10 +1152,10 @@ class SVGQualityChecker:
'errors': 0 'errors': 0
} }
self.issue_types = defaultdict(int) self.issue_types = defaultdict(int)
# spec_lock drift state (populated only when _parse_spec_lock is available # spec_lock anchor comparison state (populated only when
# and a spec_lock.md is found near the SVG) # _parse_spec_lock is available and a spec_lock.md is found near the SVG)
self._lock_cache: Dict[Path, Dict] = {} self._lock_cache: Dict[Path, Dict] = {}
self._drift_summary: Dict[str, Dict[str, set]] = { self._anchor_value_summary: Dict[str, Dict[str, set]] = {
'colors': defaultdict(set), 'colors': defaultdict(set),
'fonts': defaultdict(set), 'fonts': defaultdict(set),
'sizes': defaultdict(set), 'sizes': defaultdict(set),
@@ -1382,11 +1369,13 @@ class SVGQualityChecker:
# 8e. Validate rendering-neutral page/structure compiler hints. # 8e. Validate rendering-neutral page/structure compiler hints.
self._check_semantic_markers(root, svg_path, result) self._check_semantic_markers(root, svg_path, result)
# 9. Check spec_lock drift (colors / font-family / font-size). # 9. Compare values with spec_lock anchors. Additional colors
# Templates do not ship a spec_lock.md, so skip in template # and fonts are informational. Generated-page type sizes
# mode to avoid noise. # outside every role band are errors; other spec-backed SVG
# locations retain advisory review. Templates do not ship a
# spec_lock.md, so skip in template mode to avoid noise.
if not self.template_mode: if not self.template_mode:
self._check_spec_lock_drift( self._check_spec_lock_alignment(
content, content,
svg_path, svg_path,
result, result,
@@ -4134,8 +4123,10 @@ class SVGQualityChecker:
samples += ', ...' samples += ', ...'
message = ( message = (
f'{len(ungrouped)} ungrouped top-level Slide-local element(s) ' f'{len(ungrouped)} ungrouped top-level Slide-local element(s) '
f'in svg_output ({samples}); wrap each logical content unit ' f'in svg_output ({samples}); group only logical content units '
'in a top-level <g id="...">' 'in a top-level <g id="...">. Keep genuine static page framing '
'as a root primitive and declare a supported data-pptx-role such '
'as "background" or "decoration"'
) )
prototype_root = self._active_prototype_root() prototype_root = self._active_prototype_root()
prototype_ungrouped = ( prototype_ungrouped = (
@@ -4776,7 +4767,7 @@ class SVGQualityChecker:
} }
return colors, fonts, sizes return colors, fonts, sizes
def _check_spec_lock_drift( def _check_spec_lock_alignment(
self, self,
content: str, content: str,
svg_path: Path, svg_path: Path,
@@ -4784,15 +4775,18 @@ class SVGQualityChecker:
*, *,
root: ET.Element, root: ET.Element,
): ):
"""Detect values used in the SVG that fall outside spec_lock.md. """Compare SVG values with reusable anchors in spec_lock.md.
Covers colors (fill / stroke / stop-color / flood-color / pattern Covers colors (fill / stroke / stop-color / flood-color / pattern
metadata), font-family, and font-size. metadata), font-family, and font-size.
Emits per-file warnings summarising the drift counts; exact drifting Additional colors and font families are valid contextual authoring and
values are accumulated in self._drift_summary for the end-of-run are recorded as information. Font sizes outside every declared role
aggregation. When spec_lock.md is missing, silently skip this local anchor's ±2px band are errors in generated ``svg_output`` pages and
drift check; the Generate route's required-artifact gate owns whether warnings in other spec-backed SVG locations. Exact mirror-prototype
execution may begin. values remain inherited information. Exact values are accumulated in
self._anchor_value_summary for the end-of-run aggregation. When
spec_lock.md is missing, silently skip this local comparison; the
Generate route's required-artifact gate owns whether execution may begin.
""" """
lock = self._get_spec_lock(svg_path) lock = self._get_spec_lock(svg_path)
if lock is None: if lock is None:
@@ -4877,18 +4871,20 @@ class SVGQualityChecker:
locked_fonts = set(allowed_fonts) locked_fonts = set(allowed_fonts)
allowed_fonts.update(prototype_fonts) allowed_fonts.update(prototype_fonts)
# Sizes: declared slots are anchors; body is the ramp baseline. # Sizes: declared slots are anchors. Checker cannot infer which role a
# text node carries, so it uses the union of their ±2px bands as a cheap
# numeric safety net; prompt rules own semantic role mapping.
allowed_sizes = set() allowed_sizes = set()
body_px = None anchor_sizes = []
for k, v in typo.items(): for k, v in typo.items():
if k == 'font_family' or k.endswith('_family'): if k == 'font_family' or k.endswith('_family'):
continue continue
allowed_sizes.add(self._normalize_size(v)) normalized_size = self._normalize_size(v)
if k == 'body': allowed_sizes.add(normalized_size)
try: try:
body_px = float(self._normalize_size(v)) anchor_sizes.append(float(normalized_size))
except (ValueError, TypeError): except (ValueError, TypeError):
body_px = None pass
locked_sizes = set(allowed_sizes) locked_sizes = set(allowed_sizes)
allowed_sizes.update(prototype_sizes) allowed_sizes.update(prototype_sizes)
@@ -4929,59 +4925,62 @@ class SVGQualityChecker:
): ):
inherited_fonts.add(val) inherited_fonts.add(val)
# Poster / showcase contexts use unbounded hero type — drop the ceiling.
mode = (lock.get('mode', {}).get('mode') or '').strip().lower()
vstyle = (lock.get('visual_style', {}).get('visual_style') or '').strip().lower()
max_ratio = (float('inf') if mode in POSTER_SIZE_MODES or vstyle in POSTER_SIZE_STYLES
else RAMP_MAX_RATIO)
size_drifts = set() size_drifts = set()
inherited_sizes = set() inherited_sizes = set()
used_sizes = []
for raw_value in self._svg_property_values(content, 'font-size'): for raw_value in self._svg_property_values(content, 'font-size'):
val = self._normalize_size(raw_value) val = self._normalize_size(raw_value)
used_sizes.append(val)
if val in prototype_sizes and val not in locked_sizes: if val in prototype_sizes and val not in locked_sizes:
inherited_sizes.add(val) inherited_sizes.add(val)
continue continue
if not allowed_sizes or val in allowed_sizes: if not allowed_sizes or val in allowed_sizes:
continue continue
# Intermediate values are allowed when they sit inside the ramp
# envelope (ratio to body within [RAMP_MIN_RATIO, max_ratio]).
if body_px and body_px > 0:
try: try:
ratio = float(val) / body_px used_px = float(val)
if RAMP_MIN_RATIO <= ratio <= max_ratio: except (TypeError, ValueError):
size_drifts.add(val)
continue
if any(
abs(used_px - anchor_px) <= FONT_SIZE_ANCHOR_TOLERANCE_PX
for anchor_px in anchor_sizes
):
continue continue
except ValueError:
pass
size_drifts.add(val) size_drifts.add(val)
template_size_drift = self._detect_template_size_drift( # Record in run-wide aggregation. Colors/fonts beyond the anchor set are
used_sizes, allowed_sizes, body_px # contextual values, not release issues. Generated-page sizes enforce
) # role-anchor ownership; other spec-backed locations retain review.
# Record in run-wide aggregation
fname = svg_path.name fname = svg_path.name
for v in color_drifts: for v in color_drifts:
self._drift_summary['colors'][v].add(fname) self._anchor_value_summary['colors'][v].add(fname)
for v in font_drifts: for v in font_drifts:
self._drift_summary['fonts'][v].add(fname) self._anchor_value_summary['fonts'][v].add(fname)
for v in size_drifts: for v in size_drifts:
self._drift_summary['sizes'][v].add(fname) self._anchor_value_summary['sizes'][v].add(fname)
# Per-file warning (one condensed line; details live in summary) contextual_values = {}
parts = []
if color_drifts: if color_drifts:
parts.append(f"{len(color_drifts)} color(s)") contextual_values['colors'] = sorted(color_drifts)
if font_drifts: if font_drifts:
parts.append(f"{len(font_drifts)} font-family value(s)") contextual_values['font_families'] = sorted(font_drifts)
if contextual_values:
result['info']['contextual_values'] = contextual_values
if size_drifts: if size_drifts:
parts.append(f"{len(size_drifts)} font-size value(s)") size_issue = (
if parts: f"{len(size_drifts)} font-size value(s) fall outside ±2px of "
"every declared spec_lock role anchor (see anchor comparison "
"summary)"
)
if svg_path.parent.name == 'svg_output':
result['errors'].append(
"spec_lock typography-size violation: "
f"{size_issue}. Reflow within a declared role band or repair "
"the Design Spec and spec_lock with a justified named role; "
"do not add a role only to silence the checker."
)
else:
result['warnings'].append( result['warnings'].append(
f"spec_lock drift: {', '.join(parts)} not in spec_lock.md " f"spec_lock typography-size review: {size_issue}"
"(see drift summary for details)"
) )
inherited_parts = [] inherited_parts = []
if inherited_colors: if inherited_colors:
@@ -4993,77 +4992,10 @@ class SVGQualityChecker:
if inherited_parts: if inherited_parts:
self._append_inherited_info( self._append_inherited_info(
result, result,
'spec_lock_drift', 'spec_lock_alignment',
f"{', '.join(inherited_parts)} come unchanged from mirror " f"{', '.join(inherited_parts)} come unchanged from mirror "
"prototype and are accepted without expanding spec_lock.md", "prototype and are accepted without expanding spec_lock.md",
) )
if template_size_drift:
result['warnings'].append(template_size_drift)
def _detect_template_size_drift(self, used_sizes, allowed_sizes, body_px):
"""Warn when template-like small sizes bypass the locked type ramp.
The normal drift check deliberately permits in-ramp feature sizes, so
it should not hard-fail valid hero numbers or one-off labels. This
warning targets the common executor failure mode: copying a template's
compact 12/15/16px text stack instead of mapping content roles to
spec_lock typography, then reflowing from those locked px values.
"""
if not allowed_sizes or not body_px or body_px <= 0:
return None
try:
declared_min = min(float(v) for v in allowed_sizes)
except ValueError:
declared_min = None
# Stay narrow on purpose: real decks carry legitimate undeclared
# sub-body sizes (intermediate levels, labels, emphasis) just below the
# locked body, so "any size < body" floods the warning and destroys its
# credibility. Only flag values that read as genuine template leftovers
# — at or below `body * 0.75`, or below the smallest declared slot. This
# under-warns (a stray 15/16 against a body of 18 can slip through) in
# exchange for not crying wolf on valid intermediate type.
template_like_limit = body_px * 0.75
template_like_sub_body = []
for raw in used_sizes:
if raw in allowed_sizes:
continue
try:
size = float(raw)
except (TypeError, ValueError):
continue
below_declared_floor = declared_min is not None and size < declared_min
if size <= template_like_limit or below_declared_floor:
template_like_sub_body.append(raw)
if not template_like_sub_body:
return None
counts = Counter(template_like_sub_body)
distinct = sorted(counts, key=lambda v: float(v))
repeated_total = sum(counts.values())
below_declared_floor = []
if declared_min is not None:
below_declared_floor = [v for v in distinct if float(v) < declared_min]
if len(distinct) < 2 and repeated_total < 4 and not below_declared_floor:
return None
sample = ', '.join(
f"{v}x{counts[v]}" if counts[v] > 1 else v
for v in distinct[:5]
)
more = len(distinct) - 5
suffix = f" (+{more} more)" if more > 0 else ""
return (
"possible template font-size drift: undeclared sub-body size(s) "
f"{sample}{suffix}. Map each text item to a spec_lock typography "
"role first, then reflow card height / y / dy / line-height from "
"the locked px values."
)
def _find_image_sources_manifest(self, svg_path: Path) -> Path | None: def _find_image_sources_manifest(self, svg_path: Path) -> Path | None:
"""Locate image_sources.json for a project SVG. """Locate image_sources.json for a project SVG.
@@ -5872,28 +5804,6 @@ class SVGQualityChecker:
f"{filename} is a Generated slice row but images/{filename} does not exist.", f"{filename} is a Generated slice row but images/{filename} does not exist.",
)) ))
has_coverage_note = 'Image-as-canvas' in spec_text or 'image-as-canvas' in spec_text
pattern_ids = self._collect_layout_pattern_ids(image_rows)
if len(image_rows) >= 4 and not any(38 <= pid <= 46 for pid in pattern_ids):
if not has_coverage_note:
self._illustration_issues.append((
'warning',
'missing_image_as_canvas',
"deck has 4+ image-bearing rows but no #38-#46 image-as-canvas "
"layout and no coverage note in design_spec.md §VIII.",
))
conventional_ids = {1, 2, 3, 5, 6}
if len(image_rows) >= 4 and pattern_ids and pattern_ids.issubset(conventional_ids):
if not has_coverage_note:
self._illustration_issues.append((
'warning',
'layout_pattern_degenerated',
"all image-bearing rows use only basic full-bleed / left-right / "
"top-bottom patterns (#1/#2/#3/#5/#6); re-check "
"references/image-layout-patterns.md for modifiers or image-as-canvas options.",
))
for row in image_rows: for row in image_rows:
self._check_decorative_image_row(row, project_path, svg_texts) self._check_decorative_image_row(row, project_path, svg_texts)
@@ -5978,14 +5888,6 @@ class SVGQualityChecker:
def _row_layout(row: Dict[str, str]) -> str: def _row_layout(row: Dict[str, str]) -> str:
return row.get('Layout pattern', '').strip() return row.get('Layout pattern', '').strip()
@staticmethod
def _collect_layout_pattern_ids(rows: List[Dict[str, str]]) -> set[int]:
ids: set[int] = set()
for row in rows:
for match in re.finditer(r'#(\d+)\b', SVGQualityChecker._row_layout(row)):
ids.add(int(match.group(1)))
return ids
def _load_project_lock_images(self, project_path: Path) -> set[str]: def _load_project_lock_images(self, project_path: Path) -> set[str]:
"""Return filenames listed under spec_lock.md images.""" """Return filenames listed under spec_lock.md images."""
lock_path = project_path / 'spec_lock.md' lock_path = project_path / 'spec_lock.md'
@@ -6604,12 +6506,11 @@ class SVGQualityChecker:
for error in result['errors']: for error in result['errors']:
print(f" [ERROR] {error}") print(f" [ERROR] {error}")
# Display warnings # Display the complete warning set from this run. The generation
# workflow reviews all findings before one consolidated repair pass.
if result['warnings']: if result['warnings']:
for warning in result['warnings'][:2]: # Only show first 2 warnings for warning in result['warnings']:
print(f" [WARN] {warning}") print(f" [WARN] {warning}")
if len(result['warnings']) > 2:
print(f" ... and {len(result['warnings']) - 2} more warning(s)")
print() print()
@@ -6634,8 +6535,8 @@ class SVGQualityChecker:
for issue_type, count in sorted(self.issue_types.items(), key=lambda x: x[1], reverse=True): for issue_type, count in sorted(self.issue_types.items(), key=lambda x: x[1], reverse=True):
print(f" {issue_type}: {count}") print(f" {issue_type}: {count}")
# spec_lock drift aggregation (only printed when a lock was found) # spec_lock anchor comparison (only printed when a lock was found)
self._print_drift_summary() self._print_anchor_value_summary()
# Template-mode aggregation (orphan/missing roster + placeholder hints) # Template-mode aggregation (orphan/missing roster + placeholder hints)
self._print_template_summary() self._print_template_summary()
@@ -6811,41 +6712,60 @@ class SVGQualityChecker:
for severity, _msg in self._pptx_structure_issues: for severity, _msg in self._pptx_structure_issues:
self.issue_types[f'pptx_structure_{severity}'] += 1 self.issue_types[f'pptx_structure_{severity}'] += 1
def _print_drift_summary(self): def _print_anchor_value_summary(self):
"""Print spec_lock drift aggregation if any was observed. """Print anchor comparisons without treating contextual paint/type as drift."""
Values are sorted by file-count descending so frequent drift surfaces
first. Frequent drift usually means spec_lock.md is missing entries
the Strategist should have included; rare drift is more likely actual
Executor drift and warrants SVG review.
"""
if not self._lock_seen: if not self._lock_seen:
return return
has_drift = any(self._drift_summary[cat] for cat in self._drift_summary) has_contextual = any(
if not has_drift: self._anchor_value_summary[category]
print("\n[OK] spec_lock drift: none — all colors, fonts, and sizes are anchored to spec_lock.md") for category in ('colors', 'fonts')
)
has_size_issues = bool(self._anchor_value_summary['sizes'])
if not has_contextual and not has_size_issues:
print(
"\n[OK] spec_lock anchor comparison: no additional contextual "
"colors/fonts or out-of-band font sizes"
)
return return
print("\nspec_lock drift — values used outside spec_lock.md:") if has_contextual:
labels = [('colors', 'Colors'), print("\nContextual values beyond spec_lock anchors (informational):")
for category, label in (
('colors', 'Colors'),
('fonts', 'Font families'), ('fonts', 'Font families'),
('sizes', 'Font sizes')] ):
for category, label in labels: items = self._anchor_value_summary.get(category, {})
items = self._drift_summary.get(category, {})
if not items: if not items:
continue continue
entries = sorted(items.items(), key=lambda x: (-len(x[1]), x[0])) entries = sorted(
items.items(), key=lambda item: (-len(item[1]), item[0])
)
print(f" {label}:") print(f" {label}:")
for val, files in entries: for val, files in entries:
n = len(files) count = len(files)
suffix = "file" if n == 1 else "files" suffix = "file" if count == 1 else "files"
print(f" {val} ({n} {suffix})") print(f" {val} ({count} {suffix})")
print( print(
"Tip: frequent out-of-lock values usually mean spec_lock.md is missing\n" "Note: contextual page paint, gradient/effect colors, and "
" entries — extend the lock (scripts/update_spec.py or manual edit).\n" "export-safe typefaces are allowed.\n"
" Rare ones are likely Executor drift — review the affected SVGs." " Add a spec_lock row only when a value becomes a "
"recurring named semantic role."
) )
if has_size_issues:
print(
"\nTypography sizes outside every declared role anchor ±2px "
"(blocking in generated svg_output; advisory elsewhere):"
)
entries = sorted(
self._anchor_value_summary['sizes'].items(),
key=lambda item: (-len(item[1]), item[0]),
)
for val, files in entries:
count = len(files)
suffix = "file" if count == 1 else "files"
print(f" {val} ({count} {suffix})")
def _percentage(self, count: int) -> int: def _percentage(self, count: int) -> int:
"""Calculate percentage""" """Calculate percentage"""
if self.summary['total'] == 0: if self.summary['total'] == 0:
@@ -6953,12 +6873,15 @@ class SVGQualityChecker:
else: else:
introduced.append(item) introduced.append(item)
# Keep the legacy `drift` JSON field for report compatibility. Its
# colors/fonts entries are informational anchor comparisons; size
# entries produce errors for generated pages and warnings elsewhere.
drift = { drift = {
category: { category: {
value: sorted(files) value: sorted(files)
for value, files in sorted(values.items()) for value, files in sorted(values.items())
} }
for category, values in self._drift_summary.items() for category, values in self._anchor_value_summary.items()
} }
source_import = dict(self._source_import_summary) source_import = dict(self._source_import_summary)
payload = { payload = {
@@ -239,7 +239,7 @@ def apply_master_text_style_spec(
extract_dir: Path, extract_dir: Path,
spec: MasterTextStyleSpec, spec: MasterTextStyleSpec,
) -> int: ) -> int:
"""Install locked title/body sizes into generated slide-master txStyles.""" """Install declared title/body anchors into generated slide-master txStyles."""
master_dir = extract_dir / "ppt" / "slideMasters" master_dir = extract_dir / "ppt" / "slideMasters"
master_paths = sorted(master_dir.glob("slideMaster*.xml")) master_paths = sorted(master_dir.glob("slideMaster*.xml"))
if not master_paths: if not master_paths:
@@ -4530,7 +4530,7 @@ def create_pptx_with_native_svg(
``preserve`` mode. ``preserve`` mode.
theme_font_spec: Locked project major/minor fonts for flat/structured theme_font_spec: Locked project major/minor fonts for flat/structured
release-theme inheritance. Direct diagnostic flat callers may omit it. release-theme inheritance. Direct diagnostic flat callers may omit it.
master_text_style_spec: Required locked title/body sizes for structured master_text_style_spec: Required declared title/body anchors for structured
and release flat slide-master text styles. Direct diagnostic flat and release flat slide-master text styles. Direct diagnostic flat
callers may omit it; other routes ignore this value. callers may omit it; other routes ignore this value.
theme_color_spec: Locked project color scheme for context-aware theme_color_spec: Locked project color scheme for context-aware
@@ -4605,7 +4605,7 @@ def create_pptx_with_native_svg(
) )
if pptx_structure == "structured" and master_text_style_spec is None: if pptx_structure == "structured" and master_text_style_spec is None:
raise ValueError( raise ValueError(
"Structured export requires locked typography title/body sizes " "Structured export requires declared typography title/body anchors "
"in master_text_style_spec" "in master_text_style_spec"
) )
if use_native_shapes and pptx_structure == "structured": if use_native_shapes and pptx_structure == "structured":
@@ -75,10 +75,11 @@ readable; directory shape alone does not indicate legacy Master/Layout semantics
## Design specification references ## Design specification references
[`schemas/design_spec.schema.json`](./schemas/design_spec.schema.json) and [`design_spec_reference.md`](./design_spec_reference.md) and
[`scaffolds/design_spec.md`](./scaffolds/design_spec.md) own the machine [`spec_lock_reference.md`](./spec_lock_reference.md) own normal whole-document
structure and starting artifact; [`design_spec_reference.md`](./design_spec_reference.md) authoring; their schemas own machine validation. Files under `scaffolds/` are
is their compact authoring index. Reusable template `design_spec.md` files are optional overwrite-safe CLI conveniences, not Generate-route starting artifacts.
Reusable template `design_spec.md` files are
deliberately smaller: they contain portable metadata and only the identity, deliberately smaller: they contain portable metadata and only the identity,
structure, or application rules owned by that package. General SVG rules live structure, or application rules owned by that package. General SVG rules live
in [`shared-standards-core.md`](../references/shared-standards-core.md), with in [`shared-standards-core.md`](../references/shared-standards-core.md), with
@@ -25,12 +25,14 @@
|---|---| |---|---|
| 可视化类型与数据到图形的映射 | 项目字体与字号体系 | | 可视化类型与数据到图形的映射 | 项目字体与字号体系 |
| 节点、连接、轴、系列和标签关系 | 项目调色板与品牌色 | | 节点、连接、轴、系列和标签关系 | 项目调色板与品牌色 |
| 构图骨架阅读顺序和容量边界 | 圆角、阴影、渐变、纹理和装饰语言 | | 可视化类型、构图骨架阅读顺序 | 实际分组、框架数量、项目数量与容量适配 |
| 必要的状态与语义区分 | 页面背景、页头、页脚和品牌 chrome | | 必要的状态与语义区分 | 页面背景、页头、页脚和品牌 chrome |
| 独立预览所需的中性样式 | 最终强调策略与页面级视觉层级 | | 独立预览所需的中性样式 | 最终强调策略与页面级视觉层级 |
**Hard rule**: Executor 适配模板时保留可视化类型、信息关系和数据准确性;最终视觉必须来自当前项目,而不是继承模板的示例审美。 **Hard rule**: Executor 适配模板时保留可视化类型、信息关系和数据准确性;最终视觉必须来自当前项目,而不是继承模板的示例审美。
**Reference — not a constraint**: 模板的分组、框架数、项目数和示例容量用于展示结构,不是项目上限。Executor 可按实际内容调整,但不能改变已选可视化类型、关系或数据语义。
### 1.2 保留判断 ### 1.2 保留判断
对每个视觉元素按顺序判断: 对每个视觉元素按顺序判断:
@@ -6,11 +6,11 @@ This directory contains the standardized SVG visualization templates used by PPT
[`charts_index.json`](./charts_index.json) is the single source of truth for the library: total count + one selection-rule `summary` per template (format: `"Pick for X. Skip if Y (use other_key)."`). [`chart_recall.py`](../../scripts/chart_recall.py) can recall a bounded candidate set from this live registry without maintaining a second category or keyword index. [`charts_index.json`](./charts_index.json) is the single source of truth for the library: total count + one selection-rule `summary` per template (format: `"Pick for X. Skip if Y (use other_key)."`). [`chart_recall.py`](../../scripts/chart_recall.py) can recall a bounded candidate set from this live registry without maintaining a second category or keyword index.
For one planned page, provide 3-8 English semantic content-shape tags, then inspect the positive-scoring returned summaries plus the explicit `no-template-match` option. The requested 3-8 limit is a cap: the tool never pads a weak result with zero-score keys, and zero positive matches returns only the fallback. See [`chart-recall.md`](../../scripts/docs/chart-recall.md). The Generate workflow remains the authority for when Strategist uses this helper; maintainers may still open the registry directly when editing or auditing the catalog. For one planned page, provide 3-8 English semantic content-shape tags, then inspect the positive-scoring summaries plus the explicit `no-template-match` option. The requested limit is a cap, not a padding target, and lexical confidence never expands the output automatically. When terminology mismatch or structural ambiguity suggests that bounded recall missed a relevant template, rerun with `--semantic-fallback` for an explicit full-catalog review. The flag is optional, not a gate before `no-template-match`. `no-template-match` stays inside planning: it creates no §VII row or `page_charts` entry, and the chosen fallback is described in the page's §IX block. See [`chart-recall.md`](../../scripts/docs/chart-recall.md). The Generate workflow remains the authority for when Strategist uses this helper; maintainers may still open the registry directly when editing or auditing the catalog.
## Authoring contract ## Authoring contract
[`CHART_STYLE_GUIDE.md`](./CHART_STYLE_GUIDE.md) owns readable standalone SVG, semantic and structural fidelity, root-group bounds, data encoding, and neutral previews. The active Design Spec and `spec_lock.md` own final palette, typography, containers, effects, and chrome. Preview styling is adaptable, but visible content and frames that express real grouping, hierarchy, or capacity remain authoritative until the template is fully migrated and reviewed. [`CHART_STYLE_GUIDE.md`](./CHART_STYLE_GUIDE.md) owns readable standalone SVG, semantic and structural fidelity, root-group bounds, data encoding, and neutral previews. The active Design Spec and `spec_lock.md` own final palette, typography, containers, effects, and chrome. The reference preserves visualization semantics; its preview styling, example frames, grouping implementation, item count, and capacity are adaptable to the actual authored content.
## Visualization output model ## Visualization output model
@@ -45,7 +45,7 @@ below. Conceptual diagrams and frameworks are never labeled as native charts.
## Native Chart/Table replacement markers ## Native Chart/Table replacement markers
Supported data chart templates include a `<g data-pptx-replace-with="chart">` marker by default, and pure text-grid table templates include a `<g data-pptx-replace-with="table">` marker the same way. The default SVG export path is unchanged: the fallback vector artwork is exported as shape-based DrawingML exactly as drawn. When `svg_to_pptx.py --native-charts-and-tables` is enabled, that fallback group is replaced with a native PowerPoint Chart or Table object using the JSON metadata inside its child `<metadata type="application/json">` node. The legacy `--native-objects` spelling remains a compatibility alias. Supported data chart templates include a `<g data-pptx-replace-with="chart">` marker by default, and pure text-grid table templates include the table form. These are capability examples, not project decisions: a generated page keeps and rewrites the marker only when its Design Spec §IX page block says `Native-ready: yes`; `no` omits it. Legacy specs may supply the decision in the matching §VII row when the page block has no field. The default SVG export path remains shape-based DrawingML. With `--native-charts-and-tables`, prepared groups become native PowerPoint Chart/Table objects from their JSON metadata. The legacy `--native-objects` spelling remains a compatibility alias.
`--native-charts-and-tables` is an explicit native-object opt-in and may be lossy or visually normalized. Replacement-local value labels, center KPIs, callouts, quadrant notes, fixed axis ranges, and custom binning/splits may not survive native replacement unless represented by the payload. ChartEx palette entries do survive when supplied through valid payload colors, but this does not preserve every ChartEx style detail. Detectable information-loss risks should be reported as warnings, not handled by disabling an otherwise supported replacement marker. Review those warnings and compare the native-object export with the default shape-based export before delivery. `--native-charts-and-tables` is an explicit native-object opt-in and may be lossy or visually normalized. Replacement-local value labels, center KPIs, callouts, quadrant notes, fixed axis ranges, and custom binning/splits may not survive native replacement unless represented by the payload. ChartEx palette entries do survive when supplied through valid payload colors, but this does not preserve every ChartEx style detail. Detectable information-loss risks should be reported as warnings, not handled by disabling an otherwise supported replacement marker. Review those warnings and compare the native-object export with the default shape-based export before delivery.
@@ -5,8 +5,8 @@
"formats": ["ppt169"], "formats": ["ppt169"],
"filePattern": "{key}.svg", "filePattern": "{key}.svg",
"libraryPositioning": "Visualization template library covering charts, infographics, diagrams, and strategic frameworks. The `charts/` path is retained for backward compatibility. Templates are named by visual structure, not by business-model name — model-specific frameworks (SWOT, BCG, PEST, OKR, Porter's Five Forces, Value Chain, etc.) are matched via keywords in `summary`.", "libraryPositioning": "Visualization template library covering charts, infographics, diagrams, and strategic frameworks. The `charts/` path is retained for backward compatibility. Templates are named by visual structure, not by business-model name — model-specific frameworks (SWOT, BCG, PEST, OKR, Porter's Five Forces, Value Chain, etc.) are matched via keywords in `summary`.",
"summaryGrammar": "Each chart's `summary` is a selection rule, not a description. Format: 'Pick for <content shape + scale>. Skip if <reason → alternative>'. Strategist supplies 38 semantic content-shape tags per candidate page; chart_recall.py returns a bounded shortlist without loading the full catalog into the runtime prompt.", "summaryGrammar": "Each chart's `summary` is a selection rule, not a description. Format: 'Pick for <content shape + scale>. Skip if <reason → alternative>'. Strategist supplies 38 semantic content-shape tags per candidate page; chart_recall.py returns a bounded lexical shortlist and exposes the full catalog only through an explicitly requested semantic fallback.",
"updated": "2026-07-03" "updated": "2026-07-21"
}, },
"charts": { "charts": {
"area_chart": { "area_chart": {
@@ -1,37 +1,200 @@
# Design Spec Structure # Design Spec Structure
Project-level `design_spec.md` is a human-readable English-heading Markdown artifact. [`schemas/design_spec.schema.json`](./schemas/design_spec.schema.json) provides structural lint for readable sections and page projection; it is not an execution lock and does not require textual equality with `spec_lock.md`. Authoring starts from [`scaffolds/design_spec.md`](./scaffolds/design_spec.md). Project-level `design_spec.md` is a human-readable English-heading Markdown artifact. This file owns its normal authoring structure. [`schemas/design_spec.schema.json`](./schemas/design_spec.schema.json) provides structural lint for readable sections and page projection; it is not an execution lock and does not require textual equality with `spec_lock.md`.
Strategist writes this artifact from the complete final confirmation plus source analysis, audits every confirmed field here first, and only then projects `spec_lock.md` from the completed Design Spec. Strategist reads the complete final confirmation once, writes this artifact from that retained state plus source analysis, and audits every confirmed field here. Afterward, `spec_lock.md` is authored from the completed Design Spec plus current project/page/template context; normal lock authoring never reopens `result.json`.
## 1. Create the artifact ## 1. Author the complete artifact
Run the scaffold command once, then replace every `[fill]` value while preserving the section headings: After final confirmation, compose the entire document in active context from the retained final state, source analysis, and project context. Then create `<project_path>/design_spec.md` once, from the first line through §X.
```bash **Mandatory — new-project write**: The first non-empty line is exactly `<!-- ppt-master-schema: design-spec/v1 -->`, followed by `# <Project Name> - Design Spec`. Write every required section with final values and the complete page roster; include conditional §VII only when a real catalog reference is selected. Do not create a placeholder-bearing project file, copy example rows, or patch a scaffold field by field.
python3 skills/ppt-master/scripts/project_manager.py scaffold-spec <project_path>
```
The command refuses to shadow any recognized existing design-spec artifact, including legacy filenames. Re-running it in an otherwise equivalent empty project produces the same bytes. `project_manager.py scaffold-spec` remains an optional manual convenience and overwrite-safe troubleshooting tool. It is not part of normal Generate authoring. Resume and refine paths edit an existing completed Design Spec rather than replacing it with a scaffold.
--- ---
## 2. Section contract ## 2. Exact document contract
| Section | Required content | Conditional content | Angle-bracketed text below is authoring notation, not project content. Resolve every universal value before writing the file; omit only rows explicitly marked conditional. Keep every required `##` heading; omit §VII when no real catalog reference is selected, while §VIII remains present even with no data rows. Do not copy examples, notation tokens, or a second schema description into the project artifact.
### 2.1 Header and project contract
Start with this exact heading order:
```markdown
<!-- ppt-master-schema: design-spec/v1 -->
# <Project Name> - Design Spec
## I. Project Information
| Item | Value |
| --- | --- |
| Project Name | <resolved project name> |
| Canvas Format | <canonical format and dimensions> |
| Page Count | <exact final count matching §IX> |
| Target Audience | <confirmed audience> |
| Communication Intent | <confirmed intent, including priority or sequence> |
| Desired Audience Outcome | <confirmed observable outcome> |
| Core Message / Ask / Action | <confirmed core message or ask> |
| Delivery Context | <confirmed delivery context> |
| Artifact Afterlife | <confirmed afterlife> |
| Reading Mode | <text, balanced, presentation, or the active non-PPT equivalent> |
| Content Strategy | <confirmed material-divergence prose or balanced default> |
| Design Style | <resolved design direction> |
| Formula Policy | <mixed, render-all, or text-only> |
| AI Image Acquisition Path | <confirmed path or not applicable> |
| Generation Mode | <continuous or split> |
| Spec Refinement | <enabled or disabled> |
| Created Date | <YYYY-MM-DD> |
## II. Canvas Specification
| Property | Value |
| --- | --- |
| Format | <canonical format name> |
| Dimensions | <width × height> |
| viewBox | `<exact viewBox>` |
| Margins | <safe margins> |
| Content Area | <usable bounds> |
```
When a template workspace is active, append exactly one line after the §I table: `- **Template Application**: <confirmed or Strategist-resolved natural-language plan>`. Omit it for free design. Never replace this prose with internal reuse/adherence ids.
### 2.2 Visual, typography, layout, and icons
Use these exact subsections and field shapes:
```markdown
## III. Visual Theme
### Theme Style
- **Mode**: <confirmed preset or custom>
- **Visual style**: <confirmed preset or custom>
- **Theme**: <resolved identity direction>
- **Tone**: <resolved tone>
### Color Scheme
| Role | HEX | Purpose |
| --- | --- | --- | | --- | --- | --- |
| I. Project Information | Project and confirmed communication context | `Template Application` prose when template-active | | Background | <HEX> | <semantic use> |
| II. Canvas Specification | Format, dimensions, viewBox, margins | — | | Secondary background | <HEX> | <semantic use> |
| III. Visual Theme | Mode, visual style, colors | `### AI Image Strategy` when §VIII contains an `ai` row | | Primary | <HEX> | <semantic use> |
| IV. Typography System | Per-role stacks and locked size slots | Additional recurring roles when used | | Accent | <HEX> | <semantic use> |
| V. Layout Principles | Page regions and project spacing | Template-specific constraints only when active | | Secondary accent | <HEX> | <semantic use> |
| VI. Icon Usage Specification | Approved icon inventory | Empty table when no icons are used | | Body text | <HEX> | <semantic use> |
| VII. Visualization Reference List | Selected candidates or an empty table | Only pages with visualization work |
| VIII. Image Resource List | Image acquisition and placement rows or an empty table | AI-only columns apply to `ai` rows |
| IX. Content Outline | Ordered Slide blocks; each has `Audience move` | Page-specific facts, charts, images, and template mappings |
| X. Speaker Notes Requirements | Filename and content policy | — |
**Hard rule**: Keep all ten `##` headings, even when §VII or §VIII contains no rows. Do not add a second schema description inside the project artifact. ## IV. Typography System
### Font Plan
| Role | Chinese | English | Fallback tail |
| --- | --- | --- | --- |
| Title | <family> | <family> | <fallback> |
| Body | <family> | <family> | <fallback> |
- **Title stack**: <complete ordered stack>
- **Body stack**: <complete ordered stack>
- **Role rationale**: <why the confirmed Title/Body system fits the content; name justified recurring family overrides, or state that none is needed>
### Font Size Hierarchy
| Purpose | Anchor Size (px) |
| --- | ---: |
| Body | <confirmed value> |
| Title | <confirmed value> |
| Subtitle | <confirmed value> |
| Annotation | <confirmed value> |
## V. Layout Principles
### Page Structure
- **Header area**: <rule>
- **Content area**: <rule>
- **Footer area**: <rule>
### Spacing Specification
| Element | Current Project |
| --- | --- |
| Safe margin | <value> |
| Content block gap | <value> |
| Icon-text gap | <value> |
## VI. Icon Usage Specification
- **Library**: <confirmed library, custom, or none>
| Purpose | Icon Path | Page |
| --- | --- | --- |
```
Preserve the confirmed Title/Body system, then add every Strategist-established recurring family override justified by the completed page plan. Append the same semantic role to the Font Plan table and add `- **<Role> stack**: <complete ordered stack>`. Typical optional roles include `Annotation`, `Footer`, `Footnote`, `Data`, `Emphasis`, `Quote`, and `Code`; add only roles that recur and intentionally differ. `Role rationale` records the decision but does not itself become a lock field. Do not collapse distinct Title/Body stacks or discard a declared optional role. Treat every Font Size Hierarchy value as a role anchor: Executor may adjust one occurrence within anchor `±2px`, but a new semantic role or any planned feature outside that band needs its own 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. Leave the §VI table empty when no icons are used.
When §VIII contains any `Acquire Via: ai` row, add this subsection under §III and preserve the complete confirmed AI direction:
```markdown
### AI Image Strategy
- **Image Rendering**: <confirmed preset or custom>
- **Visual**: <confirmed visual treatment>
- **Mood**: <confirmed mood and analogy>
```
For a selected custom rendering, also add `Image Rendering Behavior`; add `Image Rendering References` only when the confirmed custom direction actually uses catalog material. Never add a separate image palette.
### 2.3 Visualization and image resources
Use the §VII table only when at least one real catalog reference is selected. Always keep the §VIII table, including when it has no data rows:
```markdown
## VII. Visualization Reference List
| Page | Template | Path | Summary-quote (verbatim) | Usage |
| --- | --- | --- | --- | --- |
## VIII. Image Resource List
| Filename | Dimensions | Ratio | Purpose | Type | Layout pattern | Crop Policy | Acquire Via | Status | Reference | text_policy | page_role |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
```
§VII is a positive reference inventory. Every row uses a returned catalog key, its real `templates/charts/<key>.svg` path, and its verbatim summary. Never emit an empty §VII, a `no-template-match` / `n/a` placeholder row, or prose explaining that no reference exists. When recall finds no fit, omit that page from §VII and describe the custom fallback in the page's §IX `Visualization` / `Layout`. List real runners-up only for pages with a selected §VII reference, as `- <returned_key> | rejected for P<NN>: <page-specific reason>`.
For every independent data chart or pure text-grid table, add `- **Native-ready**: yes|no` to its §IX Slide block. Choose `yes` only when the confirmed requirement or artifact afterlife benefits from an editable native data object; otherwise use `no`. Conceptual visualizations and incidental sparklines, KPI trends, or insets omit this field and remain ordinary SVG.
In §VIII, author every planned or explicitly required resource from the confirmed source boundary. Copy the selected `Layout pattern` id/name and modifiers verbatim; set `Crop Policy` to `adaptive` or `no-crop`; set `Acquire Via` to `ai`, `web`, `user`, `formula`, `placeholder`, or `slice`. Preserve unresolved required assets as `Pending` or `Needs-Manual` instead of dropping or reclassifying them.
### 2.4 Complete page roster and notes
Write one ordered Slide block per page. Slide count and order must equal §I `Page Count`; `Content` is a complete page brief, not a skeleton.
```markdown
## IX. Content Outline
### Part 1: <section name>
#### Slide 01 - <page name>
- **Audience move**: <audience state before → after>
- **Layout**: <composition; include the chosen prototype when template-active>
- **Title**: <preferred page title>
- **Core message**: <one governing assertion>
- **Content**: <complete intended on-slide content and hierarchy>
## X. Speaker Notes Requirements
- **Filename**: match each SVG filename under `notes/`
- **Content**: <notes content and source-handling policy>
- **Total duration**: <resolved duration>
- **Notes style**: <formal, conversational, interactive, or resolved equivalent>
- **Presentation purpose**: <inform, persuade, inspire, instruct, report, or resolved combination>
```
Add `Visualization` and `Images` to a Slide block when it consumes §VII/§VIII rows or uses a page-local visualization. State whether `Visualization` is data-driven when source values determine geometry; this page-level declaration remains authoritative even when no catalog reference fits. Add `Native-ready: yes|no` only for independent data charts or pure text-grid tables. Add `Fact IDs` for sourced claims and `Data class: scenario` for invented demo values. Add `Cover impact` to P01 except on preservation paths; add `Closing impact` only when the final page genuinely resolves the deck. Roster ids/count/order and final content are authoritative. Layout, cover/closing composition, and image/chart patterns are References whose selected semantics remain fixed while Executor realizes their geometry, hierarchy, treatment, and sparse local garnish.
--- ---
@@ -41,32 +204,6 @@ The command refuses to shadow any recognized existing design-spec artifact, incl
python3 skills/ppt-master/scripts/project_manager.py validate <project_path> python3 skills/ppt-master/scripts/project_manager.py validate <project_path>
``` ```
Validation reads the Markdown directly. It reports missing or out-of-order IX sections, unresolved `[fill...]` scaffold placeholders, missing per-slide `Audience move`, and a missing §III `AI Image Strategy` when an §VIII table selects `ai` acquisition. Validation reads the Markdown directly. It reports missing or out-of-order IX sections, unresolved `[fill...]` placeholders, missing per-slide `Audience move`, and a missing §III `AI Image Strategy` when an §VIII table selects `ai` acquisition.
The schema owns structure only. Strategist role modules own field meaning, recommendation logic, page planning, image policy, and template policy. `spec_lock.md` owns the compact execution projection. On divergence, first repair the Design Spec from the final confirmation when Gate 1 fails; otherwise repair the lock from the audited Design Spec. Never use the lock to overwrite a valid Design Spec decision. The schema validates structure only. Strategist role modules own field meaning, recommendation logic, page planning, image policy, and template policy. `spec_lock.md` owns stable execution anchors and routing selected in context; it is not an exhaustive value projection. On divergence, repair the Design Spec from the retained final state when Gate 1 fails, then re-author affected lock anchors from the audited Design Spec and current context. Never reopen `result.json` merely to author or validate the lock, and never use the lock to overwrite a valid Design Spec decision.
---
## 4. Minimal filled shape
```markdown
## III. Visual Theme
### Theme Style
- **Mode**: briefing
- **Visual style**: swiss-minimal
### Color Scheme
| Role | HEX | Purpose |
| --- | --- | --- |
| Background | `#FFFFFF` | Canvas |
## IX. Content Outline
#### Slide 01 - Decision frame
- **Audience move**: undecided → understands the decision
- **Layout**: claim + evidence
- **Title**: Choose the funded path
```
Use the scaffold for the complete shape; this excerpt is not a second template.
@@ -19,10 +19,10 @@ This directory provides **11,600+ high-quality SVG icons** across five libraries
This directory is the **global library**. At selection time the Strategist copies the chosen icons into the deck's own `<project>/icons/<lib>/` with `icon_sync.py`: This directory is the **global library**. At selection time the Strategist copies the chosen icons into the deck's own `<project>/icons/<lib>/` with `icon_sync.py`:
```bash ```bash
python3 skills/ppt-master/scripts/icon_sync.py <project_path> chunk-filled/home tabler-outline/bulb python3 skills/ppt-master/scripts/icon_sync.py <project_path> tabler-outline/home tabler-outline/bulb simple-icons/github
``` ```
A name the library does not have is reported and the command exits non-zero — re-pick a real one then, not at export. `finalize_svg.py embed-icons` embeds **project-first** (from `<project>/icons/`), falling back to this global library per-icon. Missing names and batches that mix stylistic libraries exit non-zero; `simple-icons` may coexist for real brand marks. `finalize_svg.py embed-icons` embeds **project-first** from `<project>/icons/`, with per-icon global fallback.
**Custom icons**: drop your own `.svg` into `<project>/icons/<lib>/` (any `<lib>`, e.g. `custom/`) and reference it as `data-icon="<lib>/<name>"` — it embeds like any library icon. **Custom icons**: drop your own `.svg` into `<project>/icons/<lib>/` (any `<lib>`, e.g. `custom/`) and reference it as `data-icon="<lib>/<name>"` — it embeds like any library icon.
@@ -71,16 +71,17 @@ python3 scripts/svg_finalize/embed_icons.py svg_output/*.svg
## Searching for Icons ## Searching for Icons
Use `ls | grep` — zero token cost: For a known basename, run `icon_sync.py` directly; it copies and validates without a per-file precheck.
For an uncertain basename, search only the chosen stylistic library; use `simple-icons` only for a real brand mark:
```bash ```bash
ls skills/ppt-master/templates/icons/chunk-filled/ | grep home rg --files "skills/ppt-master/templates/icons/tabler-outline" -g '*chart*.svg'
ls skills/ppt-master/templates/icons/tabler-filled/ | grep home rg --files "skills/ppt-master/templates/icons/simple-icons" -g '*github*.svg'
ls skills/ppt-master/templates/icons/tabler-outline/ | grep chart
ls skills/ppt-master/templates/icons/phosphor-duotone/ | grep house
ls skills/ppt-master/templates/icons/simple-icons/ | grep github
``` ```
Do not load a full index or enumerate broad keyword families. Re-pick from the narrow result and rerun the final batch until clean; never switch stylistic libraries for a missing generic icon.
--- ---
## Style Rules ## Style Rules
@@ -7,7 +7,7 @@
| --- | --- | | --- | --- |
| Project Name | {{PROJECT_NAME}} | | Project Name | {{PROJECT_NAME}} |
| Canvas Format | {{CANVAS_NAME}} ({{CANVAS_DIMENSIONS}}) | | Canvas Format | {{CANVAS_NAME}} ({{CANVAS_DIMENSIONS}}) |
| Page Count | [fill] | | Page Count | [fill exact final count matching §IX] |
| Target Audience | [fill] | | Target Audience | [fill] |
| Communication Intent | [fill] | | Communication Intent | [fill] |
| Desired Audience Outcome | [fill] | | Desired Audience Outcome | [fill] |
@@ -17,6 +17,10 @@
| Reading Mode | [fill] | | Reading Mode | [fill] |
| Content Strategy | [fill] | | Content Strategy | [fill] |
| Design Style | [fill] | | Design Style | [fill] |
| Formula Policy | [fill] |
| AI Image Acquisition Path | [fill or not applicable] |
| Generation Mode | [fill] |
| Spec Refinement | [fill] |
| Created Date | {{CREATED_DATE}} | | Created Date | {{CREATED_DATE}} |
## II. Canvas Specification ## II. Canvas Specification
@@ -95,15 +99,10 @@
| --- | --- | --- | | --- | --- | --- |
| [fill] | [fill] | [fill] | | [fill] | [fill] | [fill] |
## VII. Visualization Reference List
| Page | Template | Path | Summary-quote | Usage |
| --- | --- | --- | --- | --- |
## VIII. Image Resource List ## VIII. Image Resource List
| Filename | Dimensions | Ratio | Purpose | Type | Layout pattern | Acquire Via | Status | Reference | text_policy | page_role | | Filename | Dimensions | Ratio | Purpose | Type | Layout pattern | Crop Policy | Acquire Via | Status | Reference | text_policy | page_role |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
## IX. Content Outline ## IX. Content Outline
@@ -25,6 +25,8 @@
## typography ## typography
- font_family: [fill] - font_family: [fill]
- title_family: [fill]
- body_family: [fill]
- body: [fill] - body: [fill]
- title: [fill] - title: [fill]
@@ -11,7 +11,6 @@
"typography_system", "typography_system",
"layout_principles", "layout_principles",
"icon_usage", "icon_usage",
"visualization_reference",
"image_resource_list", "image_resource_list",
"content_outline", "content_outline",
"speaker_notes" "speaker_notes"
@@ -55,7 +54,7 @@
{ {
"id": "visualization_reference", "id": "visualization_reference",
"pattern": "^VII\\. Visualization Reference List(?: \\(if needed\\))?$", "pattern": "^VII\\. Visualization Reference List(?: \\(if needed\\))?$",
"required": true "required": false
}, },
{ {
"id": "image_resource_list", "id": "image_resource_list",
@@ -25,13 +25,24 @@
"type": "object", "type": "object",
"properties": { "properties": {
"image_rendering": {"type": "string"}, "image_rendering": {"type": "string"},
"image_rendering_behavior": {"type": "string"} "image_rendering_behavior": {"type": "string"},
"image_rendering_references": {"type": "string"}
} }
}, },
"typography": { "typography": {
"type": "object", "type": "object",
"properties": { "properties": {
"font_family": {"type": "string", "minLength": 1}, "font_family": {"type": "string", "minLength": 1},
"title_family": {"type": "string", "minLength": 1},
"body_family": {"type": "string", "minLength": 1},
"subtitle_family": {"type": "string", "minLength": 1},
"annotation_family": {"type": "string", "minLength": 1},
"footer_family": {"type": "string", "minLength": 1},
"footnote_family": {"type": "string", "minLength": 1},
"data_family": {"type": "string", "minLength": 1},
"emphasis_family": {"type": "string", "minLength": 1},
"quote_family": {"type": "string", "minLength": 1},
"code_family": {"type": "string", "minLength": 1},
"body": {"type": "number"}, "body": {"type": "number"},
"title": {"type": "number"} "title": {"type": "number"}
} }
@@ -97,7 +108,10 @@
"pattern": "^mode$", "pattern": "^mode$",
"required": true, "required": true,
"required_fields": ["mode"], "required_fields": ["mode"],
"allowed_fields": ["mode", "mode_behavior"], "allowed_fields": ["mode", "mode_behavior", "mode_references"],
"field_patterns": {
"mode_references": "^[a-z0-9][a-z0-9-]*(?:\\s*,\\s*[a-z0-9][a-z0-9-]*)*$"
},
"field_enums": { "field_enums": {
"mode": ["pyramid", "narrative", "instructional", "showcase", "briefing", "custom"] "mode": ["pyramid", "narrative", "instructional", "showcase", "briefing", "custom"]
} }
@@ -107,15 +121,37 @@
"pattern": "^visual_style$", "pattern": "^visual_style$",
"required": true, "required": true,
"required_fields": ["visual_style"], "required_fields": ["visual_style"],
"allowed_fields": ["visual_style", "visual_style_behavior"] "allowed_fields": ["visual_style", "visual_style_behavior", "visual_style_references"],
"field_patterns": {
"visual_style_references": "^[a-z0-9][a-z0-9-]*(?:\\s*,\\s*[a-z0-9][a-z0-9-]*)*$"
}
},
{
"id": "colors",
"pattern": "^colors$",
"required": true,
"min_entries": 1,
"field_patterns": {
"image_rendering_references": "^[a-z0-9][a-z0-9-]*(?:\\s*,\\s*[a-z0-9][a-z0-9-]*)*$"
}
}, },
{"id": "colors", "pattern": "^colors$", "required": true, "min_entries": 1},
{ {
"id": "typography", "id": "typography",
"pattern": "^typography$", "pattern": "^typography$",
"required": true, "required": true,
"required_fields": ["font_family", "body", "title"], "required_fields": ["font_family", "body", "title"],
"field_patterns": { "field_patterns": {
"font_family": "^\\S(?:.*\\S)?$",
"title_family": "^\\S(?:.*\\S)?$",
"body_family": "^\\S(?:.*\\S)?$",
"subtitle_family": "^\\S(?:.*\\S)?$",
"annotation_family": "^\\S(?:.*\\S)?$",
"footer_family": "^\\S(?:.*\\S)?$",
"footnote_family": "^\\S(?:.*\\S)?$",
"data_family": "^\\S(?:.*\\S)?$",
"emphasis_family": "^\\S(?:.*\\S)?$",
"quote_family": "^\\S(?:.*\\S)?$",
"code_family": "^\\S(?:.*\\S)?$",
"body": "^[0-9]+(?:\\.[0-9]+)?$", "body": "^[0-9]+(?:\\.[0-9]+)?$",
"title": "^[0-9]+(?:\\.[0-9]+)?$" "title": "^[0-9]+(?:\\.[0-9]+)?$"
} }
@@ -1,16 +1,14 @@
# Execution Lock Structure # Execution Lock Structure
`spec_lock.md` is the machine projection of an audited `design_spec.md`. After confirmation fidelity passes, start from [`scaffolds/spec_lock.md`](./scaffolds/spec_lock.md); [`schemas/spec_lock.schema.json`](./schemas/spec_lock.schema.json) owns its grammar. `spec_lock.md` is the compact execution contract from audited `design_spec.md` plus current context. It keeps stable cross-page anchors and routes, not every page-local paint or typeface. This file owns authoring structure; [`schemas/spec_lock.schema.json`](./schemas/spec_lock.schema.json) owns grammar.
## 1. Create the artifact ## 1. Author the complete artifact
After Generate Step 4 Gate 1, run once and project values from the Design Spec: After Generate Step 4 Gate 1, read the completed Design Spec and current page/resource/template context, compose the entire lock in active context, then create `<project_path>/spec_lock.md` once.
```bash **Mandatory — new-project write**: The first non-empty line is exactly `<!-- ppt-master-schema: spec-lock/v1 -->`, followed by `# Execution Lock`. Write only final sections and values; do not create a blank lock, copy inactive optional sections, or patch scaffold placeholders. Do not reopen final confirmation or interpret it independently.
python3 skills/ppt-master/scripts/project_manager.py scaffold-lock <project_path>
```
The command refuses to overwrite an existing `spec_lock.md`. Re-running it with the same project metadata produces the same bytes. `project_manager.py scaffold-lock` remains an optional manual convenience and overwrite-safe troubleshooting tool. It is not part of normal Generate authoring. When a credible completed Design Spec/lock pair needs correction, repair only the affected projection after auditing the Design Spec. When the Design Spec was missing and an orphan lock survived, discard that lock as authority and re-author the complete lock from the recovered, audited Design Spec plus current context.
**Hard rule**: A project lock contains only `##` sections and `- key: value` data lines, except `## forbidden`, whose list items are literal rules. Do not copy guidance paragraphs into the lock. **Hard rule**: A project lock contains only `##` sections and `- key: value` data lines, except `## forbidden`, whose list items are literal rules. Do not copy guidance paragraphs into the lock.
@@ -24,8 +22,8 @@ The command refuses to overwrite an existing `spec_lock.md`. Re-running it with
| `communication` | `audience`, `objective`, `core_message` | Compact execution projection; `objective` combines intent and audience outcome; `consumption_mode` is optional off PPT canvases | | `communication` | `audience`, `objective`, `core_message` | Compact execution projection; `objective` combines intent and audience outcome; `consumption_mode` is optional off PPT canvases |
| `mode` | `mode` | Preset or `custom` | | `mode` | `mode` | Preset or `custom` |
| `visual_style` | `visual_style` | Preset or `custom` | | `visual_style` | `visual_style` | Preset or `custom` |
| `colors` | Used color roles | `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` | Sizes are unitless 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` | `stroke_width` is conditional | | `icons` | `library`, `inventory` | `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` |
@@ -33,15 +31,24 @@ The command refuses to overwrite an existing `spec_lock.md`. Re-running it with
Optional data sections: `images`, `page_charts`. Optional data sections: `images`, `page_charts`.
The required universal block is:
```markdown
## forbidden
- Mixing icon libraries
- `mask`, `<style>`, `class`, external CSS, `<foreignObject>`, `textPath`, `@font-face`, `<animate*>`, `<set>`, `<script>` / event attributes, `<iframe>`
- HTML named entities in text; write typography as raw Unicode and escape XML reserved characters
```
--- ---
## 3. Conditional sections and fields ## 3. Conditional sections and fields
| Trigger | Required addition | | Trigger | Required addition |
| --- | --- | | --- | --- |
| `mode.mode: custom` | `mode_behavior` in `mode` | | `mode.mode: custom` | `mode_behavior` in `mode`; optional `mode_references` only when catalog modes are actually used |
| `visual_style.visual_style: custom` | `visual_style_behavior` in `visual_style` | | `visual_style.visual_style: custom` | `visual_style_behavior` in `visual_style`; optional `visual_style_references` only when catalog styles are actually used |
| `colors.image_rendering: custom` | `image_rendering_behavior` in `colors` | | `colors.image_rendering: custom` | `image_rendering_behavior` in `colors`; optional `image_rendering_references` only when catalog renderings are actually used |
| `icons.library: tabler-outline` | `stroke_width: 1.5`, `2`, or `3` | | `icons.library: tabler-outline` | `stroke_width: 1.5`, `2`, or `3` |
| `pptx_structure.mode: structured` | `template_reuse_scope: layout\|mirror`, `template_adherence`, plus `pptx_masters`, `pptx_layouts`, `page_pptx_layouts`, and `page_layouts` | | `pptx_structure.mode: structured` | `template_reuse_scope: layout\|mirror`, `template_adherence`, plus `pptx_masters`, `pptx_layouts`, `page_pptx_layouts`, and `page_layouts` |
| `pptx_structure.template_reuse_scope: mirror` | `mode: structured` and `template_adherence: strict` | | `pptx_structure.template_reuse_scope: mirror` | `mode: structured` and `template_adherence: strict` |
@@ -64,15 +71,29 @@ Structured section value shapes:
- P01: 03_content - P01: 03_content
``` ```
`page_charts` values must exist as keys in `charts/charts_index.json`; pages using the explicit `no-template-match` result do not appear there. `page_charts` values must exist as keys in `charts/charts_index.json`; a `no-template-match` result stays out of both Design Spec §VII and `page_charts`, while its custom fallback remains in the page's §IX block.
Typography projection is role-for-role, not a lossy summary:
| Design Spec §IV declaration | `spec_lock.md` field |
| --- | --- |
| Title font stack | `title_family` |
| Body font stack | `body_family` and compatibility/default `font_family` |
| Any additional recurring font role `<role>` | `<role>_family` |
| Every Font Size Hierarchy role `<role>` | lowercase `<role>` with its numeric anchor |
New locks always write `title_family` and `body_family`, even when their values happen to match. Every additional recurring family row and every size-anchor row in the Design Spec must appear under the same lowercase snake_case role; omit only family roles that inherit without an explicit override. Existing locks without family-role fields remain readable through `font_family` fallback. Executor may choose the anchor or a value within that role's `±2px` band; the lock does not enumerate intermediate values.
--- ---
## 4. Field Grammar Index ## 4. Field Grammar Index
- `font_family` grammar: one PPT-safe family name; role-specific families may extend it in the same section. - `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 unitless px anchor. Intermediate values need no lock row when they stay within the mapped role's anchor `±2px`; a new semantic role or an outside-band size requires Design Spec repair and a new anchor.
- `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=#2 Left image | crop=no-crop`. Omit unplaced sheets.
- Custom reference grammar: comma-separated exact catalog ids with no duplicates. Reference fields are valid only for `custom`; omit them for a genuinely novel direction.
- `stroke_width` grammar: `1.5`, `2`, or `3`; present only for `tabler-outline`. - `stroke_width` grammar: `1.5`, `2`, or `3`; present only for `tabler-outline`.
- `page_rhythm` grammar: `P` + at least two digits (`P01`, `P100`) followed by `anchor|dense|breathing`. - `page_rhythm` grammar: `P` + at least two digits (`P01`, `P100`) followed by `anchor|dense|breathing`.
- `page_charts` grammar: `P` + at least two digits followed by a `charts_index` key; the key and `<key>.svg` must both exist. - `page_charts` grammar: `P` + at least two digits followed by a `charts_index` key; the key and `<key>.svg` must both exist.
@@ -81,6 +102,15 @@ Structured section value shapes:
- `page_pptx_layouts` grammar: `P` + at least two digits followed by a declared Layout key. - `page_pptx_layouts` grammar: `P` + at least two digits followed by a declared Layout key.
- `page_layouts` grammar: `P` + at least two digits followed by a template SVG basename. - `page_layouts` grammar: `P` + at least two digits followed by a template SVG basename.
Catalog-based custom example:
```markdown
## mode
- mode: custom
- mode_references: pyramid, narrative
- mode_behavior: Lead each act with the decision-first clarity of pyramid, then develop it through a narrative tension-and-resolution arc.
```
--- ---
## 5. Machine Validation ## 5. Machine Validation
@@ -92,3 +122,11 @@ python3 skills/ppt-master/scripts/project_manager.py validate <project_path>
Validation reports unresolved `[fill...]` placeholders, wrong casing, unknown sections or fields, illegal enums, malformed page keys, missing catalog assets, broken structured-layout references, and unmet conditions. It neither rewrites the lock nor checks semantic projection; Generate Step 4 Gate 2 owns that check. Validation reports unresolved `[fill...]` placeholders, wrong casing, unknown sections or fields, illegal enums, malformed page keys, missing catalog assets, broken structured-layout references, and unmet conditions. It neither rewrites the lock nor checks semantic projection; Generate Step 4 Gate 2 owns that check.
Field meaning and selection logic stay in the owning Strategist modules. Executor branch references own consumption behavior. The schema owns only artifact grammar and structural conditions. Field meaning and selection logic stay in the owning Strategist modules. Executor branch references own consumption behavior. The schema owns only artifact grammar and structural conditions.
## 6. Anchor and extension semantics
- Confirmed core palette roles and every declared typography family/size role remain stable cross-page anchors.
- Page-local tints, gradient stops, shadow/glow paints, transparency composites, and one-off export-safe display families may be authored from context without adding a lock row.
- Executor may adjust one occurrence within its declared size role's anchor `±2px` while preserving hierarchy and readability; intermediate values are realization choices, not new lock rows.
- When a contextual value becomes a recurring semantic role, or typography needs a value outside every applicable anchor band, add the descriptive color/family/size role and regenerate page-context before later pages use it.
- Do not expand the lock merely to make an informational checker comparison empty. A lock edit should express reuse or identity, not enumerate incidental literals.
@@ -6,13 +6,13 @@ description: Generate PPTX route authority for source intake, planning, SVG auth
> Load only after [`routing.md`](./routing.md) selects Generate PPTX. This file owns the route's Step 17 sequence, gates, role switching, and mandatory commands. > Load only after [`routing.md`](./routing.md) selects Generate PPTX. This file owns the route's Step 17 sequence, gates, role switching, and mandatory commands.
**Core Pipeline**: `Source Document → Create Project → [Template] → Strategist Structured Plan → [Image_Generator] → Executor Live Preview → Quality Check → Post-processing → Export` **Core Pipeline**: `Initial Materials → [Fact Research] → Create Project → [Template] → Strategist Structured Plan → [Image Acquisition] → Executor Live Preview → Quality Check → Post-processing → Export`
**Generate-specific execution discipline**: **Generate-specific execution discipline**:
- The current main agent hand-writes every SVG page; never delegate page generation or run a Python, Node, or shell generator over `svg_output/`. - The current main agent hand-writes every SVG page; never delegate page generation or run a Python, Node, or shell generator over `svg_output/`.
- Initial SVG cadence: P01 → first-page gate → uninterrupted remaining pages → final gate. Grouped batches and mid-run checker calls are forbidden. - Initial SVG cadence: P01 → first-page gate → uninterrupted remaining pages → final gate. Grouped batches and mid-run checker calls are forbidden.
- Before each page, load compact page-context; its lock projection guards drift while unchanged large references stay in context. - Before each page, load compact page-context; its repeated global values are continuity anchors, while unchanged large references stay in context.
- `preset_shape_svg.py` may provide one stdout fragment only after the main agent chooses its semantic role, frame, and paint; it cannot choose layout or write a page. - `preset_shape_svg.py` may provide one stdout fragment only after the main agent chooses its semantic role, frame, and paint; it cannot choose layout or write a page.
### SVG Page-Design Boundary ### SVG Page-Design Boundary
@@ -42,9 +42,9 @@ description: Generate PPTX route authority for source intake, planning, SVG auth
### Step 1: Source Content Processing ### Step 1: Source Content Processing
🚧 **GATE**: User has provided source material (PDF / DOCX / EPUB / URL / Markdown file / text description / conversation content — any form is acceptable). 🚧 **GATE**: The user has provided a topic / desired outcome and any available initial material.
> **No source content?** When the user supplies only a topic name or requirements without any file or substantive description, run the [`topic-research`](stages/topic-research.md) intake stage first, then return here with its products as input. > **Topic-only**: run [`topic-research`](stages/topic-research.md) immediately, then use its factual supplement as source content.
When the user provides non-Markdown content, convert immediately through the When the user provides non-Markdown content, convert immediately through the
unified dispatcher. It preserves the backend converters' existing behavior, unified dispatcher. It preserves the backend converters' existing behavior,
@@ -64,6 +64,16 @@ Use `-o` only when a specific output file/directory is required; with multiple
inputs or directory inputs, `-o` is an output directory. Backend converter details are documented in inputs or directory inputs, `-o` is an output directory. Backend converter details are documented in
[`scripts/docs/conversion.md`](../scripts/docs/conversion.md). [`scripts/docs/conversion.md`](../scripts/docs/conversion.md).
After reading direct and converted content, assess factual sufficiency:
| Material state | Action |
|---|---|
| Requested outcome is supported | Continue Step 2 |
| Required externally verifiable claims remain unsupported | Run [`topic-research`](stages/topic-research.md) for those gaps only |
| Closed corpus / source-only / no external enrichment | Stay within supplied material |
**Sufficiency test**: research only to avoid inventing, omitting, or leaving unsupported a factual claim the requested outcome requires; file presence or length is irrelevant. It gathers facts only. Step 5 acquires Strategist-selected images after final confirmation.
> **Office vector assets (EMF/WMF) from DOCX/PPTX sources**: > **Office vector assets (EMF/WMF) from DOCX/PPTX sources**:
> Source conversion extracts embedded Office vector images (.emf/.wmf) > Source conversion extracts embedded Office vector images (.emf/.wmf)
> alongside bitmap images when the source format exposes them. After `import-sources`, these land in `images/` > alongside bitmap images when the source format exposes them. After `import-sources`, these land in `images/`
@@ -78,7 +88,7 @@ inputs or directory inputs, `-o` is an output directory. Backend converter detai
> Browser-based live preview cannot render EMF (will show blank) — this is expected; > Browser-based live preview cannot render EMF (will show blank) — this is expected;
> the PPTX output is the source of truth. > the PPTX output is the source of truth.
**✅ Checkpoint — Confirm source content is ready, proceed to Step 2.** **✅ Checkpoint — Confirm source content and any factual supplement are ready, proceed to Step 2.**
--- ---
@@ -96,7 +106,7 @@ Import source content (choose based on the situation):
| Situation | Action | | Situation | Action |
|-----------|--------| |-----------|--------|
| Has source files (PDF/MD/etc.) | `python3 ${SKILL_DIR}/scripts/project_manager.py import-sources <project_path> <source_files_or_dirs...> --move` | | Has source files (PDF/MD/etc.) | `python3 ${SKILL_DIR}/scripts/project_manager.py import-sources <project_path> <source_files_or_dirs...>` |
| User provided text directly in conversation | No import needed — content is already in conversation context; subsequent steps can reference it directly | | User provided text directly in conversation | No import needed — content is already in conversation context; subsequent steps can reference it directly |
For PPTX sources, `import-sources` automatically runs the standard intake enrichment: For PPTX sources, `import-sources` automatically runs the standard intake enrichment:
@@ -109,7 +119,7 @@ For each PPTX it writes `<stem>.identity.json` (canvas, theme palette/fonts, obs
Multi-deck: several PPTX files may be imported into one main-pipeline project — each gets its own `<stem>.*` artifacts and a deck entry in `source_profile.json`. `source_profile.json` stays the single must-read index (one entry for a one-deck project, several for a combined-source project). Stems must be distinct; re-importing the same stem replaces that deck's entry. The beautify profile and Fill Native PPTX route remain single-deck (1:1 to one chosen source deck) and read that deck's `<stem>.*` artifacts. Multi-deck: several PPTX files may be imported into one main-pipeline project — each gets its own `<stem>.*` artifacts and a deck entry in `source_profile.json`. `source_profile.json` stays the single must-read index (one entry for a one-deck project, several for a combined-source project). Stems must be distinct; re-importing the same stem replaces that deck's entry. The beautify profile and Fill Native PPTX route remain single-deck (1:1 to one chosen source deck) and read that deck's `<stem>.*` artifacts.
> ⚠️ **MUST use `--move`** (not copy): all source files — Step 1's generated Markdown, original PDFs / MDs / images — go into `sources/` via `import-sources --move`. If Step 1 wrote Markdown beside the original sources, pass that source path/directory once. If Step 1 used `-o` to write Markdown elsewhere, pass both the original source path(s)/directory and the Markdown output path(s)/directory. After execution they no longer exist at the original location, and a source directory left empty by the move is removed automatically. Intermediate artifacts (e.g., `_files/`) are handled automatically. **Source ownership boundary**: Use the automatic import mode shown above. Only inputs already under the repository's `projects/` tree move into the target project's `sources/`; every other local path is copied and remains untouched, even if `--move` is supplied. Use `--copy` when a projects-local input must also remain in place. If Step 1 wrote Markdown beside the original sources, pass that source path/directory once. If Step 1 used `-o` to write Markdown elsewhere, pass both the original source path(s)/directory and the Markdown output path(s)/directory. Intermediate artifacts (e.g., `_files/`) are handled automatically.
**✅ Checkpoint — Confirm project structure created successfully, `sources/` contains all source files, converted materials are ready. Proceed to Step 3.** **✅ Checkpoint — Confirm project structure created successfully, `sources/` contains all source files, converted materials are ready. Proceed to Step 3.**
@@ -152,7 +162,7 @@ Read references/strategist.md
The core first chooses the proposed Stage 2 source ids. Load the image module before writing Stage 2 whenever that proposal is non-`none`; after confirmation, keep it active only for confirmed non-`none` sources or an active formula plan. A confirmed `none` path with no formula work writes no image rows. Bare template names and style language do not load the template module. The core first chooses the proposed Stage 2 source ids. Load the image module before writing Stage 2 whenever that proposal is non-`none`; after confirmation, keep it active only for confirmed non-`none` sources or an active formula plan. A confirmed `none` path with no formula work writes no image rows. Bare template names and style language do not load the template module.
> ⚠️ **Mandatory artifact gates**: scaffold the human-readable `design_spec.md` first; after it passes confirmation fidelity, scaffold the machine-readable `spec_lock.md` and project from the Design Spec. Do not reconstruct either grammar from memory. The design schema keeps the brief readable and page-projectable; the lock schema keeps its execution projection machine-readable. Cross-file textual equality is not required, but semantic projection fidelity is. Reference indexes are for troubleshooting, not normal generation load. > ⚠️ **Mandatory artifact gates**: after final confirmation, read `templates/design_spec_reference.md` and author the complete `design_spec.md` from scratch; after Gate 1, read `templates/spec_lock_reference.md` and author the complete `spec_lock.md` from the Design Spec plus current context. For a new project, create each finished artifact once—do not materialize a placeholder scaffold and fill it piecemeal. The references own authoring structure, the schemas own machine validation, and semantic projection fidelity remains mandatory. `scaffold-spec` / `scaffold-lock` are optional manual conveniences, not normal Generate steps.
**Artifact ownership**: fact-channel and source/derived artifact boundaries are defined in [`references/artifact-ownership.md`](../references/artifact-ownership.md). This Step uses those ownership rules; it does not redefine them. **Artifact ownership**: fact-channel and source/derived artifact boundaries are defined in [`references/artifact-ownership.md`](../references/artifact-ownership.md). This Step uses those ownership rules; it does not redefine them.
@@ -190,7 +200,7 @@ The core first chooses the proposed Stage 2 source ids. Load the image module be
python3 ${SKILL_DIR}/scripts/confirm_ui/server.py <project_path> --wait-only python3 ${SKILL_DIR}/scripts/confirm_ui/server.py <project_path> --wait-only
``` ```
4. After the final wait returns, re-read the complete `result.json` even when the wait succeeded. Proceed from the file only when it carries `stage: final` and `status: confirmed`. On a non-zero wait, perform this same read before using the documented chat fallback. A stage-skip result returns to the missing stage; it is not a browser failure. 4. After the final wait returns, read the complete `result.json` exactly once and retain that object through Design Spec authoring and its fidelity audit. Proceed only when it carries `stage: final` and `status: confirmed`. Do not reopen the file during normal lock authoring or downstream execution. On a non-zero wait, this same single read determines whether the persisted result succeeded before using the documented chat fallback. A stage-skip result returns to the missing stage; it is not a browser failure.
5. After final confirmation or chat fallback, always release the server: 5. After final confirmation or chat fallback, always release the server:
@@ -200,7 +210,7 @@ The core first chooses the proposed Stage 2 source ids. Load the image module be
If the user opted out of the page but did not delegate confirmation, skip launch and run the same three stages in chat with explicit user responses. If the user explicitly delegated confirmation, consolidate the same three stages into one AI-authored summary and proceed without `result.json`. Otherwise report the launch URL and keep the staged chat summaries available as fallback. If the user opted out of the page but did not delegate confirmation, skip launch and run the same three stages in chat with explicit user responses. If the user explicitly delegated confirmation, consolidate the same three stages into one AI-authored summary and proceed without `result.json`. Otherwise report the launch URL and keep the staged chat summaries available as fallback.
**GATE — write the Design Spec from the final state, then project the lock.** Treat every explicitly present final value as a user-owned Design Spec requirement. The Strategist may autonomously elaborate only unconfirmed implementation details; it must not omit, delete, substitute, narrow, weaken, reinterpret, or re-recommend a confirmed value. First author and audit the complete `design_spec.md` through [`strategist.md`](../references/strategist.md) §6.2, including every confirmed image source and explicit `image_notes` role. Only after that audit passes, derive `spec_lock.md` from the completed Design Spec without making another design decision. Apply `strategist-template.md` §3 for an active template, and never write a separate image palette. If one confirmed value cannot be honored, follow [`failure-recovery.md`](governance/failure-recovery.md) instead of silently changing it. **GATE — consume the final state once into the Design Spec, then author the lock from context.** Treat every explicitly present final value as user-owned input and consume it at the semantic type defined by [`strategist.md`](../references/strategist.md) §1 and its field owner. Do not omit or substitute a value, and do not silently strengthen or weaken its type; accepting a recommendation does not turn a Reference or Permission into a Literal requirement. First author and audit the complete `design_spec.md` through [`strategist.md`](../references/strategist.md) §6.2 from the retained final object, including production mechanics, the complete recurring typography-role system, the confirmed image-source boundary, and explicit `image_notes` obligations. Do not reopen `result.json` afterward. Only after that audit passes, author `spec_lock.md` from the completed Design Spec and current project/page/template context: preserve confirmed identity, project every declared recurring typography family and size-anchor role without collapsing it into one default stack, choose reusable execution anchors and routing values, project each placed image's source/pattern/crop policy without reselection, and do not enumerate page-local paint or font-family garnish. Apply `strategist-template.md` §3 for an active template, and never write a separate image palette. If a confirmed requirement cannot be honored, follow [`failure-recovery.md`](governance/failure-recovery.md) instead of silently changing it.
**Mandatory — split-mode note** (not a separate confirmation): after listing the Strategist confirmation stage details, you MUST append exactly one short line (rendered in the user's language, prefixed with 💡) about generation mode. Pick the variant by qualitative read of upstream-load signals — recommended page count, source-material bulk, whether `topic-research` ran with substantial web-fetch accumulation: **Mandatory — split-mode note** (not a separate confirmation): after listing the Strategist confirmation stage details, you MUST append exactly one short line (rendered in the user's language, prefixed with 💡) about generation mode. Pick the variant by qualitative read of upstream-load signals — recommended page count, source-material bulk, whether `topic-research` ran with substantial web-fetch accumulation:
@@ -225,34 +235,29 @@ python3 ${SKILL_DIR}/scripts/analyze_images.py <project_path>/images
> ⚠️ **Image handling**: NEVER directly read / open / view image files (`.jpg`, `.png`, etc.). All image info comes from `analyze_images.py` output (`analysis/image_analysis.csv`) or the Design Spec's Image Resource List. > ⚠️ **Image handling**: NEVER directly read / open / view image files (`.jpg`, `.png`, etc.). All image info comes from `analyze_images.py` output (`analysis/image_analysis.csv`) or the Design Spec's Image Resource List.
**Output**: **Output**:
- `<project_path>/design_spec.md` — human-readable design narrative - `<project_path>/design_spec.md` complete human-readable design narrative and durable confirmed production state
- `<project_path>/spec_lock.md` — machine-readable communication + execution contract; Executor consumes its current-page projection from `page-context` - `<project_path>/spec_lock.md` — machine-readable communication + stable execution anchors/routing contract; Executor consumes its current-page projection from `page-context`
For a new project, scaffold the Design Spec after final confirmation; the command refuses to overwrite an existing artifact: For a new project, use the reference-first whole-document sequence:
```bash 1. Read `templates/design_spec_reference.md`. Compose the complete IX document in active context from the retained final confirmation, source analysis, and project context; then create `<project_path>/design_spec.md` once with no `[fill]` placeholders or example rows. This is the only normal consumption of the final result.
python3 ${SKILL_DIR}/scripts/project_manager.py scaffold-spec <project_path> 2. Audit the finished Design Spec field by field against that retained confirmation. Gate 1 must pass before lock authoring.
``` 3. Read `templates/spec_lock_reference.md`. Compose the complete execution projection from the audited Design Spec plus current page/resource/template context; then create `<project_path>/spec_lock.md` once. Do not reopen `result.json` or make an independent design choice.
4. Compare the lock's identity anchors and routing values against the completed Design Spec, then run `python3 ${SKILL_DIR}/scripts/project_manager.py validate <project_path>`.
Fill and audit `design_spec.md` against the complete final confirmation. Only after that audit passes, scaffold and fill the machine lock from the completed Design Spec: A retained final state → Design Spec mismatch or Design Spec/context → lock mismatch is blocking even when the standalone Markdown schemas pass. `validate` reads the planning artifacts only; it does not reopen `confirm_ui/result.json` or prove semantic fidelity. Repair the Design Spec from the retained final state; only a fresh recovery turn with no retained state reads persisted final evidence once. Then re-author the affected lock rows from the corrected Design Spec and current context. A resume or refine path edits existing completed files in the same order; it does not replace them with scaffolds.
```bash
python3 ${SKILL_DIR}/scripts/project_manager.py scaffold-lock <project_path>
```
After filling the lock, compare its machine-relevant values against the completed Design Spec, then run `python3 ${SKILL_DIR}/scripts/project_manager.py validate <project_path>`. A `result.json` → Design Spec mismatch or Design Spec → lock projection mismatch is blocking even when the standalone Markdown schemas pass. `validate` mechanically enforces one slice of this: with a final confirmed `confirm_ui/result.json`, every confirmed non-`none` `image_usage` source must be represented by at least one `## images` row (`ai` is also satisfied by `slice`); the remaining semantic comparison stays with this gate. Repair the Design Spec only from the final confirmation state, then re-project the affected lock rows. A resume path edits existing files in the same order and never re-scaffolds them.
**✅ Checkpoint — Phase deliverables complete, auto-proceed to next step**: **✅ Checkpoint — Phase deliverables complete, auto-proceed to next step**:
```markdown ```markdown
## ✅ Strategist Phase Complete ## ✅ Strategist Phase Complete
- [x] Read the auto-extracted facts already in `analysis/` (e.g. `source_profile.json`) before the Strategist confirmation stage - [x] Read the auto-extracted facts already in `analysis/` (e.g. `source_profile.json`) before the Strategist confirmation stage
- [x] Strategist confirmation stage completed (user confirmed via Confirm UI `result.json` or chat fallback) - [x] Strategist confirmation stage completed (user confirmed via Confirm UI `result.json` or chat fallback)
- [x] Final confirmation re-read before Design Spec authoring - [x] Final confirmation read once and retained through Design Spec authoring/audit; `result.json` not reopened in the normal path
- [x] Split-mode note appended below the confirmation fields (heavy or normal variant) - [x] Split-mode note appended below the confirmation fields (heavy or normal variant)
- [x] Spec-refinement opt-in line appended (default OFF; only the user's explicit request enters the refine-spec stage) - [x] Spec-refinement opt-in line appended (default OFF; only the user's explicit request enters the refine-spec stage)
- [x] Design Specification & Content Outline generated - [x] Design Specification & Content Outline generated
- [x] Gate 1 passed: Design Spec preserves every confirmed value - [x] Gate 1 passed: Design Spec preserves every confirmed value at its owner-defined semantic type
- [x] Execution lock (`spec_lock.md`) projected from the completed Design Spec with no independent design decision - [x] Execution lock (`spec_lock.md`) authored from the completed Design Spec + current context as stable anchors/routing, not an exhaustive value whitelist
- [x] Communication trace validated: Design Spec retains the contract; the lock has compact `audience` / `objective` / `core_message` fields (plus PPT `consumption_mode`); every §IX Slide has an `Audience move` - [x] Communication trace validated: Design Spec retains the contract; the lock has compact `audience` / `objective` / `core_message` fields (plus PPT `consumption_mode`); every §IX Slide has an `Audience move`
- [ ] **Next**: Auto-proceed to [Image_Generator / Executor] phase - [ ] **Next**: Auto-proceed to [Image_Generator / Executor] phase
``` ```
@@ -263,7 +268,7 @@ After filling the lock, compare its machine-relevant values against the complete
🚧 **GATE**: Step 4 complete; `<project_path>/design_spec.md` and `<project_path>/spec_lock.md` both exist. If either required artifact is missing, stop before any acquisition or generation and follow [`failure-recovery.md`](governance/failure-recovery.md) §3. Formula rows already have `Acquire Via: formula` and status `Rendered` or `Needs-Manual`. 🚧 **GATE**: Step 4 complete; `<project_path>/design_spec.md` and `<project_path>/spec_lock.md` both exist. If either required artifact is missing, stop before any acquisition or generation and follow [`failure-recovery.md`](governance/failure-recovery.md) §3. Formula rows already have `Acquire Via: formula` and status `Rendered` or `Needs-Manual`.
> **Trigger**: At least one row in the resource list has `Acquire Via: ai`, `web`, and/or `slice`. If every row is `user`, `formula`, or `placeholder`, skip to Step 6. A final confirmation that selected `ai` or `web` without a matching §VIII resource row is an incomplete Design Spec, not a reason to skip Step 5; return to Step 4 Gate 1, repair the Design Spec from the final confirmation, and re-project the lock. > **Trigger**: At least one row in the resource list has `Acquire Via: ai`, `web`, and/or `slice`. If every row is `user`, `formula`, or `placeholder`, skip to Step 6. A permitted but unused image source creates no row and does not trigger acquisition. If §VIII omits a source, asset, or page role that `image_notes` explicitly requires, the Design Spec is incomplete; return to Step 4 Gate 1, repair it from the retained final state, and re-author the affected lock anchors from context. Do not reopen `result.json` during this check.
**Failure recovery**: stop/continue behavior for AI/web/slice/image-readiness failures is defined in [`workflows/governance/failure-recovery.md`](governance/failure-recovery.md). This Step keeps the acquisition procedure. **Failure recovery**: stop/continue behavior for AI/web/slice/image-readiness failures is defined in [`workflows/governance/failure-recovery.md`](governance/failure-recovery.md). This Step keeps the acquisition procedure.
@@ -290,7 +295,7 @@ A deck with only `ai` rows never loads `image-searcher.md`; a deck with only `we
> 💡 **ai path — spot illustrations as one sheet**: when the §VIII image resource plan needs ≥3 same-family spot illustrations as decorative accessories, generate **one grid sheet** (a single `ai` sheet row) instead of one row per element, then slice it (workflow step 2.5 below). Choose sheet geometry from intended placement: `1xN` / `Nx1` are useful for extreme portrait / landscape cells, and a designed `MxN` grid is valid when its cell ratio fits the planned elements. The sheet row is generated but not placed; each cut **element row** (`Acquire Via: slice`) is placed and must appear in `spec_lock.md images`. One generation = one coherent style across all pieces. Resource contract + the geometry rules: [image-generator.md](../references/image-generator.md) §4.3. > 💡 **ai path — spot illustrations as one sheet**: when the §VIII image resource plan needs ≥3 same-family spot illustrations as decorative accessories, generate **one grid sheet** (a single `ai` sheet row) instead of one row per element, then slice it (workflow step 2.5 below). Choose sheet geometry from intended placement: `1xN` / `Nx1` are useful for extreme portrait / landscape cells, and a designed `MxN` grid is valid when its cell ratio fits the planned elements. The sheet row is generated but not placed; each cut **element row** (`Acquire Via: slice`) is placed and must appear in `spec_lock.md images`. One generation = one coherent style across all pieces. Resource contract + the geometry rules: [image-generator.md](../references/image-generator.md) §4.3.
> ⚠️ **Honor the 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 — a confirmed choice other than `auto` wins, whether it came from chat or, when the page was used, `result.json.image_ai_path`. `host-native` forces Path B even when `IMAGE_BACKEND` is configured; `api` forces Path A; `manual` forces offline. Never run `image_gen.py --manifest` when the confirmed 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.
Workflow: Workflow:
@@ -311,7 +316,7 @@ Workflow:
- [x] analyze_images.py re-run so image_analysis.csv covers the acquired web / AI / sliced images - [x] analyze_images.py re-run so image_analysis.csv covers the acquired web / AI / sliced images
``` ```
**Default — auto-proceed to Step 6.** Only when the user's Step 4 response explicitly opted into split mode (in chat or via Confirm UI `result.json` with `generation_mode: "split"`), output the planning-session handoff below and stop this conversation: **Default — auto-proceed to Step 6.** Only when `design_spec.md §I` records `generation_mode: split`, output the planning-session handoff below and stop this conversation:
```markdown ```markdown
## ✅ Planning Session Complete ## ✅ Planning Session Complete
@@ -328,6 +333,10 @@ Workflow:
🚧 **GATE**: Step 4 (and Step 5 if triggered) complete; all prerequisite deliverables are ready. 🚧 **GATE**: Step 4 (and Step 5 if triggered) complete; all prerequisite deliverables are ready.
**Exact page roster**: render `design_spec.md §IX` one-for-one, in order. Any add/drop/merge/split/reorder requires Spec repair/refinement first.
**Page content**: §IX is preferred wording and semantic authority. Use it when it works; adapt it when presentation benefits while preserving intent, facts, and explicit literal requirements. Read sources only to verify requested evidence; return incomplete blocks to Step 4 instead of enriching them during execution.
**Artifact ownership**: `svg_output/` is the author source, `svg_final/` is derived, and image facts come from the regenerated `analysis/image_analysis.csv`; see [`references/artifact-ownership.md`](../references/artifact-ownership.md). **Artifact ownership**: `svg_output/` is the author source, `svg_final/` is derived, and image facts come from the regenerated `analysis/image_analysis.csv`; see [`references/artifact-ownership.md`](../references/artifact-ownership.md).
Read the execution references for this deck's locked `mode` + `visual_style` (from `spec_lock.md`): Read the execution references for this deck's locked `mode` + `visual_style` (from `spec_lock.md`):
@@ -335,18 +344,18 @@ Read the execution references for this deck's locked `mode` + `visual_style` (fr
Read references/executor-base.md # REQUIRED: flat/shared execution core Read references/executor-base.md # REQUIRED: flat/shared execution core
Read references/shared-standards-core.md # REQUIRED: SVG compatibility core Read references/shared-standards-core.md # REQUIRED: SVG compatibility core
Read references/semantic-svg.md # REQUIRED: semantic metadata boundary Read references/semantic-svg.md # REQUIRED: semantic metadata boundary
Read references/modes/<locked-mode>.md # narrative skeleton (spec_lock.md `mode`) Read references/modes/<resolved-id>.md # one preset id, or each `mode_references` id
Read references/visual-styles/<locked-style>.md # aesthetic (spec_lock.md `visual_style`) Read references/visual-styles/<resolved-id>.md # one preset id, or each `visual_style_references` id
``` ```
> Read only the five always-on references above plus the conditionally triggered modules below. For `mode: custom` or `visual_style: custom`, skip that preset file and follow `mode_behavior` / `visual_style_behavior` from `spec_lock.md` instead. Never glob `modes/` or `visual-styles/`. > Read only the five always-on references above plus the conditionally triggered modules below. A preset reads its one locked file. For `mode: custom` or `visual_style: custom`, read every exact file named by the optional `mode_references` / `visual_style_references`, then synthesize those sources under the corresponding behavior. If the reference field is absent, the direction is genuinely novel: read no preset file and follow the behavior directly. Never infer adjacent references or glob `modes/` / `visual-styles/`.
| Deterministic trigger | Additional references | | Deterministic trigger | Additional references |
|---|---| |---|---|
| `pptx_structure.mode: structured` | `executor-structured.md` + `pptx-structure-interface.md` | | `pptx_structure.mode: structured` | `executor-structured.md` + `pptx-structure-interface.md` |
| Any data chart/table, including mini or inset charts and sparklines | `executor-chart.md` | | Any data chart/table, including mini or inset charts and sparklines | `executor-chart.md` |
| Preset pattern or supported native chart/table | `native-data-interface.md` before drawing | | Preset pattern or supported native chart/table | `native-data-interface.md` before drawing |
| `spec_lock.md images` or §VIII contains at least one image/formula row, or an active template carries bundled images | `executor-image.md` + `image-layout-spec.md` + `svg-image-embedding.md` | | `spec_lock.md images` or §VIII contains at least one image/formula row, or an active template carries bundled images | `executor-image.md` + `image-layout-patterns.md` + `image-layout-spec.md` + `svg-image-embedding.md` |
| At least one placed image has `Status: Sourced` | `executor-web-image.md` after the image branch | | At least one placed image has `Status: Sourced` | `executor-web-image.md` after the image branch |
| The locked style/current page calls for noncanonical or alpha paint, dash/cap/join, tracking/decoration/outline, gradient/filter/glow/shadow, path/transform/clipping, or another constructed effect | `svg-effects.md` before authoring that value or effect | | The locked style/current page calls for noncanonical or alpha paint, dash/cap/join, tracking/decoration/outline, gradient/filter/glow/shadow, path/transform/clipping, or another constructed effect | `svg-effects.md` before authoring that value or effect |
| A page calls for a literal PowerPoint stock shape | `native-shape-authoring.md` before selecting or emitting that shape | | A page calls for a literal PowerPoint stock shape | `native-shape-authoring.md` before selecting or emitting that shape |
@@ -377,7 +386,7 @@ python3 ${SKILL_DIR}/scripts/svg_editor/server.py <project_path> --live --daemon
python3 skills/ppt-master/scripts/project_manager.py page-context <project_path> P<NN> --record-usage python3 skills/ppt-master/scripts/project_manager.py page-context <project_path> P<NN> --record-usage
``` ```
Use `global` as the lock drift guard, `page_context` as the page delta, and `reference_set` under the Executor load policy. The retained Design Spec owns optional `Template Application`. This replaces neither gate artifacts nor source facts. See [`executor-base.md`](../references/executor-base.md) §2.1. Use `global` as the compact cross-page anchor set, `page_context` as the page delta, and `reference_set` under the Executor load policy. Anchors are defaults and reusable semantic roles, not a color/font allowlist. The retained Design Spec owns optional `Template Application`. This replaces neither gate artifacts nor source facts. See [`executor-base.md`](../references/executor-base.md) §2.1.
> ⚠️ **Main-agent only**: SVG generation MUST stay in the current main agent — page design depends on full upstream context. Do NOT delegate to sub-agents. > ⚠️ **Main-agent only**: SVG generation MUST stay in the current main agent — page design depends on full upstream context. Do NOT delegate to sub-agents.
> ⚠️ **Generation rhythm**: P01 → first-page gate → uninterrupted remaining pages → final gate, in one context without batches or mid-run checker calls. > ⚠️ **Generation rhythm**: P01 → first-page gate → uninterrupted remaining pages → final gate, in one context without batches or mid-run checker calls.
@@ -386,7 +395,7 @@ Use `global` as the lock drift guard, `page_context` as the page delta, and `ref
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. When a page actually needs a literal stock shape, load and apply [`native-shape-authoring.md`](../references/native-shape-authoring.md) before drawing it. Diagram relationships remain Shape-first; 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. When a page actually needs a literal stock shape, load and apply [`native-shape-authoring.md`](../references/native-shape-authoring.md) before drawing it. Diagram relationships remain Shape-first; do not infer a preset from contour similarity.
`template_reuse_scope: mirror|layout` pages MUST start from the complete `page_layouts` SVG, keep inherited visible objects, and preserve root Master/Layout identity plus stable atoms/slots. Strict preserves that reusable contract; under `layout`, the once-loaded Design Spec's `Template Application` may still authorize carrier text/tspan reflow inside unchanged slot bounds. Adaptive assigns a new Layout key/name and updates `spec_lock.md` when fixed atoms or slot topology/bounds change. `mirror` changes only visible text values while preserving text/tspan topology and attributes. `style` follows the flat paragraph below without structure metadata. `template_reuse_scope: mirror|layout` pages MUST start from the complete `page_layouts` SVG, keep inherited visible objects, and preserve root Master/Layout identity plus stable atoms/slots. Strict preserves that reusable contract; under `layout`, the once-loaded Design Spec's `Template Application` may still authorize carrier text/tspan reflow inside unchanged slot bounds. Adaptive uses the current or new Layout key/name already declared by Strategist. If construction proves that fixed atoms or slot topology/bounds must change, stop and return upstream for Strategist to repair the owning plan and lock, regenerate the page context, then resume; Executor never mutates `spec_lock.md`. `mirror` changes only visible text values while preserving text/tspan topology and attributes. `style` follows the flat paragraph below without structure metadata.
`template_reuse_scope: style`, free-design, and brand-only pages use `pptx_structure.mode: flat`. Draw the complete page directly: keep backgrounds, repeated chrome, headings, text, images, and decoration as ordinary Slide-local SVG content. Do not plan `pptx_masters` / `pptx_layouts` / `page_pptx_layouts`, do not add root Master/Layout identity, and do not add `data-pptx-layer` or `data-pptx-placeholder` metadata. Group logical content normally with top-level `<g id>` elements. Export materializes one clean project-owned Master plus one Blank Layout, applies the locked theme colors/fonts/title-body defaults, removes stock content placeholders and unused built-in Layouts, and retains only the standard date/footer/slide-number capability hooks. It does not promote or deduplicate page content. `template_reuse_scope: style`, free-design, and brand-only pages use `pptx_structure.mode: flat`. Draw the complete page directly: keep backgrounds, repeated chrome, headings, text, images, and decoration as ordinary Slide-local SVG content. Do not plan `pptx_masters` / `pptx_layouts` / `page_pptx_layouts`, do not add root Master/Layout identity, and do not add `data-pptx-layer` or `data-pptx-placeholder` metadata. Group logical content normally with top-level `<g id>` elements. Export materializes one clean project-owned Master plus one Blank Layout, applies the locked theme colors/fonts/title-body defaults, removes stock content placeholders and unused built-in Layouts, and retains only the standard date/footer/slide-number capability hooks. It does not promote or deduplicate page content.
@@ -394,16 +403,17 @@ Do not duplicate specialized identity with `data-pptx-role`. Add it only to stru
**First-page gate (Mandatory)** — after the **first** SVG page, before drawing page 2: **First-page gate (Mandatory)** — after the **first** SVG page, before drawing page 2:
```bash ```bash
python3 ${SKILL_DIR}/scripts/svg_quality_checker.py <project_path> --stage first-page python3 ${SKILL_DIR}/scripts/svg_quality_checker.py <project_path> --stage first-page --json
``` ```
Fix P01 errors and rerun this gate as needed. After it passes, draw P02 through the final page without checker calls. Run the command unfiltered—do not pipe it through `tail`, `head`, `grep`, or another output truncator. Review the complete P01 issue set from that one run before editing. Select any advisory warnings worth addressing, fix all blocking errors and selected warnings in one consolidated edit pass, then perform one verification rerun. Do not rerun merely to reveal the next issue. If verification still fails, treat its complete output as the next batch and repeat the same review → consolidated edit → single verification cycle; never check between individual fixes. If the terminal output itself is truncated, read only the relevant issue arrays from `validation/svg_quality_first_page_report.json`; do not launch another checker run for discovery. After the gate passes, draw P02 through the final page without checker calls.
**Quality Check Gate (Mandatory)** — only after every planned SVG exists, BEFORE annotation handling and speaker notes: **Quality Check Gate (Mandatory)** — only after every planned SVG exists, BEFORE annotation handling and speaker notes:
```bash ```bash
python3 ${SKILL_DIR}/scripts/svg_quality_checker.py <project_path> --stage final --json python3 ${SKILL_DIR}/scripts/svg_quality_checker.py <project_path> --stage final --json
``` ```
- **MUST**: Before this gate, every supported chart/table—including mini charts and sparklines—already has its own draw-time marker plus JSON metadata. - **MUST**: Before this gate, every chart/table whose Design Spec §IX page block says `Native-ready: yes` already has its own draw-time marker plus JSON metadata. Rows marked `no` and incidental microvisuals remain ordinary SVG. For legacy specs only, a matching §VII value may supply the decision when §IX has no field.
- Any `error` (banned/unsupported SVG features, invalid values, unresolved references, viewBox mismatch, etc.) MUST be fixed before proceeding — return to Visual Construction, regenerate that page, re-run check. - Run the command unfiltered—do not pipe it through `tail`, `head`, `grep`, or another output truncator. One invocation already scans every page and reports the complete issue set.
- On failure, review all `blocking` errors and all advisory warnings from that run before editing. Choose which warnings merit work, fix every blocking error and the selected warnings in one consolidated edit pass, then perform one verification rerun. If it still fails, its complete output begins the next batch cycle; never run the checker between individual fixes or use repeated invocations to discover one next issue at a time. If terminal output is truncated, extract only `categories.blocking.issues` and, when needed, `categories.introduced.issues` from the report written by that same run.
- Every `warning` is advisory and non-blocking: do not return the page for mandatory modification, do not auto-normalize user-authored compatible syntax, and do not require an acknowledgement/disposition line. Recommendation warnings identify the generated-SVG default; fidelity/quality warnings may be reported when material, but the existing input may ship unchanged. If a condition must be corrected before release, the checker must classify it as an `error`, not a `warning`. - Every `warning` is advisory and non-blocking: do not return the page for mandatory modification, do not auto-normalize user-authored compatible syntax, and do not require an acknowledgement/disposition line. Recommendation warnings identify the generated-SVG default; fidelity/quality warnings may be reported when material, but the existing input may ship unchanged. If a condition must be corrected before release, the checker must classify it as an `error`, not a `warning`.
- The same rule applies to structured-template warnings (empty/framing-only Layout, bare Master, duplicate layout keys): they may guide an optional template cleanup, but warnings alone never fail the quality gate. Flat `style`, free-design, and brand-only routes still rely on their existing hard errors for invalid structure metadata or incomplete required locks. - The same rule applies to structured-template warnings (empty/framing-only Layout, bare Master, duplicate layout keys): they may guide an optional template cleanup, but warnings alone never fail the quality gate. Flat `style`, free-design, and brand-only routes still rely on their existing hard errors for invalid structure metadata or incomplete required locks.
- Run against `svg_output/` (not after `finalize_svg.py` — finalize rewrites SVG and masks violations). - Run against `svg_output/` (not after `finalize_svg.py` — finalize rewrites SVG and masks violations).
@@ -417,7 +427,8 @@ python3 ${SKILL_DIR}/scripts/svg_quality_checker.py <project_path> --stage final
## ✅ Executor Phase Complete ## ✅ Executor Phase Complete
- [x] Live preview started before the first SVG and kept available at the reported URL - [x] Live preview started before the first SVG and kept available at the reported URL
- [x] P01 gate passed; remaining pages authored without checker calls - [x] P01 gate passed; remaining pages authored without checker calls
- [x] All SVGs generated to svg_output/ - [x] Each failing checker run was reviewed as one complete issue set and followed by one consolidated edit pass; checker output was not filtered
- [x] `svg_output/` matches the ordered §IX roster exactly, one SVG per planned page
- [x] Every wrapped prose paragraph uses one `<text>` frame with `<tspan>` line breaks - [x] Every wrapped prose paragraph uses one `<text>` frame with `<tspan>` line breaks
- [x] svg_quality_checker.py passed (0 errors) - [x] svg_quality_checker.py passed (0 errors)
- [x] Speaker notes generated at notes/total.md - [x] Speaker notes generated at notes/total.md
@@ -18,8 +18,9 @@ Global stop/continue rules for all four top-level routes, plus concrete failure
| Confirm UI wait timeout | No, if no final result yet | Re-check `result.json` once; keep server cleanup mandatory | Only if user still wants the page | Step 4 same stage or chat fallback | | Confirm UI wait timeout | No, if no final result yet | Re-check `result.json` once; keep server cleanup mandatory | Only if user still wants the page | Step 4 same stage or chat fallback |
| Confirm UI Stage 1 completed then interrupted | Yes until Stage 2 is written/confirmed | Read existing Stage 1 `result.json`, write Stage 2 recommendations, then `--wait-only --wait-stage stage2` | Usually no | Step 4 Stage 2 write/wait | | Confirm UI Stage 1 completed then interrupted | Yes until Stage 2 is written/confirmed | Read existing Stage 1 `result.json`, write Stage 2 recommendations, then `--wait-only --wait-stage stage2` | Usually no | Step 4 Stage 2 write/wait |
| Missing final confirmation | Yes | None | User must confirm or change the values | Step 4 final confirmation | | Missing final confirmation | Yes | None | User must confirm or change the values | Step 4 final confirmation |
| Final confirmed value is missing, changed, substituted, or weakened in `design_spec.md` | Yes | Re-read the complete final confirmation state and repair the Design Spec before touching the lock | Only when the confirmed value genuinely cannot be honored | Step 4 Gate 1 — confirmation fidelity | | Final confirmed value is missing, changed, substituted, or weakened in `design_spec.md` | Yes | Repair from the retained final-confirmation object; only a fresh recovery turn with no retained state reads persisted final evidence once | Only when the confirmed value genuinely cannot be honored | Step 4 Gate 1 — confirmation fidelity |
| `spec_lock.md` changes or omits a machine-relevant Design Spec decision | Yes | Re-project the affected lock rows from the completed Design Spec without making a new design choice | No unless the Design Spec itself is incomplete | Step 4 Gate 2 — lock projection fidelity | | `spec_lock.md` changes confirmed identity or omits a required execution anchor/routing decision | Yes | Re-author the affected lock rows from the completed Design Spec and current context; do not enumerate page-local literals | No unless the Design Spec itself is incomplete | Step 4 Gate 2 — lock context fidelity |
| Execution exposes a missing Strategist-owned semantic role or plan detail | Yes for the affected page | In the same main-agent context, return to Strategist; repair the owning Design Spec fragments, then affected lock rows; targeted-readback, validate, rerun `page-context` for affected pages, and rebind the new SHA per [`executor-base.md`](../../references/executor-base.md) §2.1. Fresh or unknown context reads the complete Design Spec once | Only if the repair changes confirmed identity or user intent | Step 4 Gate 1/2 → Step 6 current page |
| Step 3 rejects a legacy or incomplete template contract | Yes | Stop template consumption; create a new current workspace through Create Template from the original PPTX/reference, then return with its exact workspace root | Only when required source evidence or template choices are unavailable | Create Template → Generate PPTX Step 3 | | Step 3 rejects a legacy or incomplete template contract | Yes | Stop template consumption; create a new current workspace through Create Template from the original PPTX/reference, then return with its exact workspace root | Only when required source evidence or template choices are unavailable | Create Template → Generate PPTX Step 3 |
| Formula rendering provider failure | No until the Step 7 readiness gate | Exhaust the provider chain; if unresolved, mark only the affected formula rows `Needs-Manual` and continue | Supply the exact target PNG or change formula policy | Step 4 / Step 7 image readiness gate | | Formula rendering provider failure | No until the Step 7 readiness gate | Exhaust the provider chain; if unresolved, mark only the affected formula rows `Needs-Manual` and continue | Supply the exact target PNG or change formula policy | Step 4 / Step 7 image readiness gate |
| AI image generation failure | No | `auto`: follow A → B → Offline Manual. Explicit `api` / `host-native`: retry only that path, then mark the row `Needs-Manual` without switching automated providers | Only when missing files are required before export | Step 5 / Step 7 image readiness gate | | AI image generation failure | No | `auto`: follow A → B → Offline Manual. Explicit `api` / `host-native`: retry only that path, then mark the row `Needs-Manual` without switching automated providers | Only when missing files are required before export | Step 5 / Step 7 image readiness gate |
@@ -30,7 +31,7 @@ Global stop/continue rules for all four top-level routes, plus concrete failure
| Live preview fails to start | No | Continue generation; report that preview is unavailable | Only if user requires browser preview | Step 6 or `live-preview` Step 1 | | Live preview fails to start | No | Continue generation; report that preview is unavailable | Only if user requires browser preview | Step 6 or `live-preview` Step 1 |
| Live preview closed by user | No | Continue generation | No | Restart through `live-preview` only if requested | | Live preview closed by user | No | Continue generation | No | Restart through `live-preview` only if requested |
| Browser annotations submitted during generation | No | Defer application until after Step 7 | User asks to apply annotations | `live-preview` Step 2 | | Browser annotations submitted during generation | No | Defer application until after Step 7 | User asks to apply annotations | `live-preview` Step 2 |
| `svg_quality_checker.py` error | Yes | Fix the affected SVG, then rerun checker | No unless required asset is missing | Step 6 Visual Construction | | `svg_quality_checker.py` error | Yes | Review the complete issue set from one unfiltered run; fix all errors and selected warnings in one consolidated edit pass, then perform one verification rerun. If it still fails, use that complete result as the next batch; never check between individual fixes | No unless required asset is missing | Step 6 Visual Construction |
| `svg_quality_checker.py` warning | No | Continue without mandatory modification or acknowledgement; preserve compatible user syntax, and report material fidelity/quality advice when useful | No | Step 6 advisory warning handling | | `svg_quality_checker.py` warning | No | Continue without mandatory modification or acknowledgement; preserve compatible user syntax, and report material fidelity/quality advice when useful | No | Step 6 advisory warning handling |
| Missing `notes/total.md` | Yes | Generate speaker notes before Step 7 | No | Step 6 Logic Construction | | Missing `notes/total.md` | Yes | Generate speaker notes before Step 7 | No | Step 6 Logic Construction |
| Step 7 image readiness missing manual files | Yes | None for manual assets; list required filenames and prompts | Yes | Step 7 image readiness gate | | Step 7 image readiness missing manual files | Yes | None for manual assets; list required filenames and prompts | Yes | Step 7 image readiness gate |
@@ -67,10 +68,10 @@ Here, **final confirmation evidence** means either the explicit final confirmati
|---|---| |---|---|
| Stage 1 confirmation exists, Stage 2 missing | Write Stage 2 recommendations, then `confirm_ui/server.py <project> --wait-only --wait-stage stage2` | | Stage 1 confirmation exists, Stage 2 missing | Write Stage 2 recommendations, then `confirm_ui/server.py <project> --wait-only --wait-stage stage2` |
| Stage 2 confirmation exists, final confirmation missing | Resume [`generate-pptx`](../generate-pptx.md) Step 4 confirmation orchestration at Stage 3: derive production mechanics from the confirmed solution, then perform the final wait. | | Stage 2 confirmation exists, final confirmation missing | Resume [`generate-pptx`](../generate-pptx.md) Step 4 confirmation orchestration at Stage 3: derive production mechanics from the confirmed solution, then perform the final wait. |
| Final confirmation evidence exists; `design_spec.md` is missing, with or without a surviving `spec_lock.md` | Return to Generate Step 4 and [`strategist.md`](../../references/strategist.md) §6.2; scaffold and write `design_spec.md` from the final confirmation and source analysis, pass Gate 1, then replace the orphan lock's affected rows by projecting from the completed Design Spec. Never reconstruct the Design Spec from an orphan lock. | | Final confirmation evidence exists; `design_spec.md` is missing, with or without a surviving `spec_lock.md` | Return to Generate Step 4 and [`strategist.md`](../../references/strategist.md) §6.2; read final evidence once into the fresh context, read [`design_spec_reference.md`](../../templates/design_spec_reference.md), author the complete `design_spec.md` from scratch using that state plus source analysis, and pass Gate 1. Then read [`spec_lock_reference.md`](../../templates/spec_lock_reference.md) and re-author the complete `spec_lock.md` from the audited Design Spec plus current context, replacing any orphan lock. Never reconstruct the Design Spec from an orphan lock or retain orphan-lock choices as authority. |
| Final confirmation evidence exists; `design_spec.md` exists and `spec_lock.md` missing | Return to Generate Step 4, audit the existing Design Spec against the final confirmation first, then run `project_manager.py scaffold-lock` and fill the lock only from the audited Design Spec. | | Final confirmation evidence exists; `design_spec.md` exists and `spec_lock.md` missing | Return to Generate Step 4; in this fresh recovery context read final evidence once to audit the existing Design Spec, then read [`spec_lock_reference.md`](../../templates/spec_lock_reference.md) and author the complete lock from the audited Design Spec plus current context. |
| Final confirmation evidence and both planning artifacts exist, but Gate 1 fails | Repair `design_spec.md` from the final confirmation, then re-project every affected lock row. Do not reopen recommendations or infer a replacement from the current lock. | | Final confirmation evidence and both planning artifacts exist, but Gate 1 fails | In a fresh recovery context read final evidence once, repair `design_spec.md`, then re-author every affected lock row. Do not reopen recommendations or infer a replacement from the current lock. |
| Gate 1 passes but Gate 2 fails | Keep the Design Spec unchanged and re-project only the mismatched lock rows from it. | | Gate 1 passes but Gate 2 fails | Keep the Design Spec unchanged and re-author only the mismatched lock anchors/routing rows from it plus current context. |
| No final confirmation evidence is available | Resume Step 4 from the latest stage evidenced by `confirm_ui/result.json`; if no stage is persisted, restart Step 4 at Stage 1. Do not infer confirmed choices from partial planning artifacts. | | No final confirmation evidence is available | Resume Step 4 from the latest stage evidenced by `confirm_ui/result.json`; if no stage is persisted, restart Step 4 at Stage 1. Do not infer confirmed choices from partial planning artifacts. |
| `design_spec.md` and `spec_lock.md` complete, split mode selected | [`resume-execute`](../stages/resume-execute.md) | | `design_spec.md` and `spec_lock.md` complete, split mode selected | [`resume-execute`](../stages/resume-execute.md) |
| Images acquired but SVGs not started | [`generate-pptx`](../generate-pptx.md) Step 6 | | Images acquired but SVGs not started | [`generate-pptx`](../generate-pptx.md) Step 6 |
@@ -24,7 +24,7 @@ Maintainer-only inventory for adding, moving, or removing workflow documents. Ru
| `create-brand` | Template child workflow | [`create-template/create-brand.md`](./create-template/create-brand.md) | Create Template | | `create-brand` | Template child workflow | [`create-template/create-brand.md`](./create-template/create-brand.md) | Create Template |
| `create-layout` | Template child workflow | [`create-template/create-layout.md`](./create-template/create-layout.md) | Create Template | | `create-layout` | Template child workflow | [`create-template/create-layout.md`](./create-template/create-layout.md) | Create Template |
| `create-deck` | Template child workflow | [`create-template/create-deck.md`](./create-template/create-deck.md) | Create Template | | `create-deck` | Template child workflow | [`create-template/create-deck.md`](./create-template/create-deck.md) | Create Template |
| `topic-research` | Intake stage | [`stages/topic-research.md`](./stages/topic-research.md) | Before Generate Step 1 | | `topic-research` | Factual-preparation stage | [`stages/topic-research.md`](./stages/topic-research.md) | Inside Generate Step 1 |
| `resume-execute` | Control stage | [`stages/resume-execute.md`](./stages/resume-execute.md) | Generate Step 6 resume | | `resume-execute` | Control stage | [`stages/resume-execute.md`](./stages/resume-execute.md) | Generate Step 6 resume |
| `refine-spec` | Planning stage | [`stages/refine-spec.md`](./stages/refine-spec.md) | After Generate confirmation | | `refine-spec` | Planning stage | [`stages/refine-spec.md`](./stages/refine-spec.md) | After Generate confirmation |
| `verify-charts` | Quality gate | [`stages/verify-charts.md`](./stages/verify-charts.md) | Before Generate Step 7 | | `verify-charts` | Quality gate | [`stages/verify-charts.md`](./stages/verify-charts.md) | Before Generate Step 7 |
@@ -53,7 +53,7 @@ Match the canvas to the source so 1:1 pages and paste-back align. Determine the
```bash ```bash
python3 ${SKILL_DIR}/scripts/project_manager.py init <project_name> --format <format> python3 ${SKILL_DIR}/scripts/project_manager.py init <project_name> --format <format>
python3 ${SKILL_DIR}/scripts/project_manager.py import-sources <project_path> <source.pptx> --move python3 ${SKILL_DIR}/scripts/project_manager.py import-sources <project_path> <source.pptx>
``` ```
--- ---
@@ -87,7 +87,7 @@ python3 ${SKILL_DIR}/scripts/pptx_intake.py <project_path>/sources/<source.pptx>
| `<stem>.slide_library.json` field | Use | | `<stem>.slide_library.json` field | Use |
|---|---| |---|---|
| `slides[].charts[]` (`chart_type` / `categories` / `series[].values`) | regenerate as a native SVG chart via the `§VII` `templates/charts/` path | | `slides[].charts[]` (`chart_type` / `categories` / `series[].values`) | regenerate as a native SVG chart; use the §VII `templates/charts/` path only when recall selects a real reference, otherwise plan the custom chart in §IX |
| `slides[].tables[]` (`row_count` / `column_count` / cell text) | regenerate as a native SVG table | | `slides[].tables[]` (`row_count` / `column_count` / cell text) | regenerate as a native SVG table |
**Hard rule — regenerate visuals, do not carry them over**: charts / tables / images are rebuilt from their data in the inherited style, never spliced in byte-for-byte. This keeps the deck style-consistent and natively editable. **Data values are frozen** (categories / series / cell text / numbers unchanged); only their rendering is the deck's own. Pictures (`ppt_to_md`-extracted files) are reused but re-laid-out — position / crop / size follow the new layout, not the source slot. A user who wants an original element verbatim copies it across themselves. **Hard rule — regenerate visuals, do not carry them over**: charts / tables / images are rebuilt from their data in the inherited style, never spliced in byte-for-byte. This keeps the deck style-consistent and natively editable. **Data values are frozen** (categories / series / cell text / numbers unchanged); only their rendering is the deck's own. Pictures (`ppt_to_md`-extracted files) are reused but re-laid-out — position / crop / size follow the new layout, not the source slot. A user who wants an original element verbatim copies it across themselves.
@@ -206,17 +206,17 @@ Write `<project_path>/confirm_ui/recommendations.json` and launch the same confi
python3 ${SKILL_DIR}/scripts/confirm_ui/server.py <project_path> --daemon --wait python3 ${SKILL_DIR}/scripts/confirm_ui/server.py <project_path> --daemon --wait
``` ```
Read the confirmed canvas + palette + typography (incl. `body_size`) and any other overrides from `<project_path>/confirm_ui/result.json`. Chat is the canonical fallback when the page cannot open (remote / headless) — present the same fields in chat and honor the reply identically. Always run `--shutdown` on exit (page-confirm or chat-fallback) so port 5050 is free for Step 6 live preview. After the final wait returns, read `<project_path>/confirm_ui/result.json` exactly once and retain the complete confirmed object through Design Spec authoring. Chat is the canonical fallback when the page cannot open (remote / headless) — present the same fields in chat and retain the reply identically. Always run `--shutdown` on exit (page-confirm or chat-fallback) so port 5050 is free for Step 6 live preview.
On confirmation, enter [`generate-pptx`](../generate-pptx.md) Step 4 as Strategist with the plan pre-resolved. The two beautify invariants always hold: the content-faithful clause ([`strategist.md`](../../references/strategist.md) §d Layer 1) and page count = source slide count (strict 1:1). Everything else comes from the **confirmed** `result.json``mode` (recommended `briefing`), canvas, `visual_style`, color (e) + typography (g) incl. `body_size` (the reviewed values; skip both recommendation flows) — honoring whatever the user kept or overrode. §VII = chart/table data → `templates/charts/`, §VIII = source pictures for re-layout. On confirmation, enter [`generate-pptx`](../generate-pptx.md) Step 4 as Strategist with the plan pre-resolved. The two beautify invariants always hold: the content-faithful clause ([`strategist.md`](../../references/strategist.md) §d Layer 1) and page count = source slide count (strict 1:1). Write the retained confirmed object completely into `design_spec.md``mode` (recommended `briefing`), canvas, `visual_style`, color (e) + typography (g) incl. `body_size` (the reviewed values; skip both recommendation flows) — honoring whatever the user kept or overrode. Do not reopen `result.json` afterward. §VII contains only selected `templates/charts/` references; unmatched chart/table plans stay in their §IX page blocks. §VIII contains source pictures for re-layout.
**Hard rule — §IX is verbatim and 1:1**: each source slide becomes exactly one page, in source order, its text transcribed word-for-word from `sources/<stem>.md`. Do not merge, split, drop, or rewrite. Write `design_spec.md` + `spec_lock.md` per `strategist.md` §6, then hand off to the Executor. **Hard rule — §IX is verbatim and 1:1**: each source slide becomes exactly one page, in source order, its text transcribed word-for-word from `sources/<stem>.md`. Do not merge, split, drop, or rewrite. Complete and audit `design_spec.md` first, then author `spec_lock.md` from that Design Spec plus the source/page/template context per `strategist.md` §6 before handing off to the Executor.
--- ---
## 6. Executor + Export ## 6. Executor + Export
Run the standard pipeline ([`generate-pptx`](../generate-pptx.md) Steps 67). The Executor re-lays-out each page — hierarchy, spacing, alignment, page rhythm — using **only** the inherited palette + fonts from `spec_lock.md`, regenerates charts / tables as native SVG from the extracted data, and re-lays-out the source pictures. Run the standard pipeline ([`generate-pptx`](../generate-pptx.md) Steps 67). The Executor re-lays-out each page — hierarchy, spacing, alignment, page rhythm — using the semantic anchors in `spec_lock.md` plus current page/source/template context; valid page-local colors, gradients, effects, and export-safe display faces need not be added to the lock. It regenerates charts / tables as native SVG from the extracted data and re-lays-out the source pictures.
Follow [`generate-pptx`](../generate-pptx.md) Step 7 for the canonical serial Follow [`generate-pptx`](../generate-pptx.md) Step 7 for the canonical serial
post-processing commands, gates, success criteria, and export artifacts. post-processing commands, gates, success criteria, and export artifacts.
@@ -41,7 +41,7 @@ route selection. After selection, the route authority owns execution.
| Request condition | Generate-route behavior | | Request condition | Generate-route behavior |
|---|---| |---|---|
| Topic only, no substantive source facts | Run [`topic-research`](./stages/topic-research.md), then return to [`generate-pptx`](./generate-pptx.md) Step 1 | | Topic only, or supplied sources leave planning-critical factual gaps | Run [`topic-research`](./stages/topic-research.md) inside [`generate-pptx`](./generate-pptx.md) Step 1: immediately for topic-only input, or after conversion and reading for source-backed input; research only the identified gaps, then continue Step 2 |
| Existing PPTX may be split, merged, dropped, reordered, or re-outlined | Treat the PPTX as source content through [`generate-pptx`](./generate-pptx.md) Step 1 and its PPTX intake; use the default Generate pipeline | | Existing PPTX may be split, merged, dropped, reordered, or re-outlined | Treat the PPTX as source content through [`generate-pptx`](./generate-pptx.md) Step 1 and its PPTX intake; use the default Generate pipeline |
| Existing PPTX must preserve wording, page count, and page order 1:1 | Activate the [`beautify-pptx`](./profiles/beautify-pptx.md) profile inside the main pipeline | | Existing PPTX must preserve wording, page count, and page order 1:1 | Activate the [`beautify-pptx`](./profiles/beautify-pptx.md) profile inside the main pipeline |
| Explicit current brand/layout/deck workspace root | Enter [`generate-pptx`](./generate-pptx.md) Step 3 and conditionally load [`apply-template-workspace`](./stages/apply-template-workspace.md); consume the workspace root, never only its inner `templates/` directory | | Explicit current brand/layout/deck workspace root | Enter [`generate-pptx`](./generate-pptx.md) Step 3 and conditionally load [`apply-template-workspace`](./stages/apply-template-workspace.md); consume the workspace root, never only its inner `templates/` directory |
@@ -26,7 +26,7 @@ The user **explicitly asks** to refine / review / revise the spec before generat
## Step 1: Produce the full spec ## Step 1: Produce the full spec
Run the default Strategist output exactly as [`generate-pptx`](../generate-pptx.md) Step 4 specifies: write `design_spec.md` (§IX) from the final confirmation, pass the confirmation-fidelity gate, then project `spec_lock.md` from the completed Design Spec. Read the relevant `sources/` files so the content outline (`§IX`) carries real facts, not skeleton points. Nothing special here — this is the normal spec, just produced under the knowledge that the user is about to review it. Run the default Strategist output exactly as [`generate-pptx`](../generate-pptx.md) Step 4 specifies: consume the retained final confirmation once into `design_spec.md` (§IX), pass the confirmation-fidelity gate, then author `spec_lock.md` from the completed Design Spec plus current context as stable anchors/routing rather than an exhaustive value list. Read the relevant `sources/` files so the content outline (`§IX`) carries real facts, not skeleton points. Nothing special here — this is the normal spec, just produced under the knowledge that the user is about to review it.
--- ---
@@ -47,9 +47,9 @@ The user may revise **any part of the spec**, not just the outline — content o
These overlap with what the confirmed `mode`, visual style, and §6.1 already shape — treat them as discussion angles to surface what is worth talking about, not permission for the Strategist to redo a decision without the user's explicit revision. These overlap with what the confirmed `mode`, visual style, and §6.1 already shape — treat them as discussion angles to surface what is worth talking about, not permission for the Strategist to redo a decision without the user's explicit revision.
**Revise the Design Spec first, then re-project the lock.** An explicit revision the user approves becomes the latest authority for the affected decision and supersedes its earlier confirmation value. Apply it to `design_spec.md` first, then re-project every affected machine value into `spec_lock.md`; lock authoring never decides or overrides the revision. On divergence, repair the lock from the approved Design Spec (see [`strategist.md`](../../references/strategist.md) §6.2). Iterate as many rounds as the user wants. The loop ends only when the user explicitly approves the spec. **Revise the Design Spec first, then re-author affected lock anchors in context.** An explicit revision the user approves becomes the latest authority for the affected decision and supersedes its earlier confirmation value. Apply it to `design_spec.md` first, then update only the reusable anchors and routing values that the revised Design Spec plus current project/page/template context justify; lock authoring never decides or overrides the revision and never enumerates every legal page-local value. On divergence, repair the lock from the approved Design Spec (see [`strategist.md`](../../references/strategist.md) §6.2). Iterate as many rounds as the user wants. The loop ends only when the user explicitly approves the spec.
**Re-run the route/template preflight after reuse revisions.** If the user changes `template_reuse_scope`, `template_adherence`, `page_layouts`, the Master/Layout definition roster, or any `page_pptx_layouts` assignment, repeat the preflight in [`strategist-template.md`](../../references/strategist-template.md) before approval and hand-back. Switching to `style` rewrites the route to `pptx_structure.mode: flat` and removes all structure mappings/adherence; switching to `mirror` / `layout` restores a complete structured contract. Every newly selected structured prototype must declare root Master/Layout identity, direct atomic Master/Layout visuals, and valid top-level slot groups with positive bounds plus one compatible carrier or explicit composite `object` proxy. A zero-slot Layout is valid. Update the human-facing prototype decisions in the Design Spec first, then re-project `pptx_masters`, unique `pptx_layouts` definitions, and complete `page_pptx_layouts` assignments into the lock. A legacy prototype is not selectable; create a current workspace through [`create-template`](../create-template.md) before refinement. **Re-run the route/template preflight after reuse revisions.** If the user changes `template_reuse_scope`, `template_adherence`, `page_layouts`, the Master/Layout definition roster, or any `page_pptx_layouts` assignment, repeat the preflight in [`strategist-template.md`](../../references/strategist-template.md) before approval and hand-back. Switching to `style` rewrites the route to `pptx_structure.mode: flat` and removes all structure mappings/adherence; switching to `mirror` / `layout` restores a complete structured contract. Every newly selected structured prototype must declare root Master/Layout identity, direct atomic Master/Layout visuals, and valid top-level slot groups with positive bounds plus one compatible carrier or explicit composite `object` proxy. A zero-slot Layout is valid. Update the human-facing prototype decisions in the Design Spec first, then author the required `pptx_masters`, unique `pptx_layouts` definitions, and complete `page_pptx_layouts` assignments in the lock from the revised template context. A legacy prototype is not selectable; create a current workspace through [`create-template`](../create-template.md) before refinement.
--- ---
@@ -50,19 +50,19 @@ Read skills/ppt-master/workflows/generate-pptx.md
Then jump to `### Step 6: Executor Phase` and run the documented pipeline: Then jump to `### Step 6: Executor Phase` and run the documented pipeline:
- Read the Step 6 flat core (`executor-base`, `shared-standards-core`, and the locked mode / visual-style files), then only the branches selected by its condition table - Read the Step 6 flat core (`executor-base`, `shared-standards-core`, and the locked preset mode / visual-style files); for custom directions, reload every optional `*_references` file from `spec_lock.md` before applying the behavior, then only the branches selected by the condition table
- Design Parameter Confirmation - Design Parameter Confirmation
- Read the project Design Spec and, when structured, the template Design Spec once; retain both in the fresh execution context - Read the project Design Spec and, when structured, the template Design Spec once; retain both in the fresh execution context. A later repair authored by this same main agent follows [`executor-base.md`](../../references/executor-base.md) §2.1 targeted readback/rebind instead of loading the project Design Spec a second time
- Per-page `python3 skills/ppt-master/scripts/project_manager.py page-context <project_path> P<NN> --record-usage` load + sequential page generation; the lock projection repeats intentionally, while each prototype/chart SVG is loaded only before its first use or after its SHA changes - Per-page `python3 skills/ppt-master/scripts/project_manager.py page-context <project_path> P<NN> --record-usage` load + sequential page generation; the stable anchor projection repeats intentionally without becoming a color/font allowlist, while each prototype/chart SVG is loaded only before its first use or after its SHA changes
- Quality Check Gate - Quality Check Gate
- Speaker notes generation - Speaker notes generation
- Step 7: Post-processing & Export (`total_md_split``finalize_svg``svg_to_pptx`) - Step 7: Post-processing & Export (`total_md_split``finalize_svg``svg_to_pptx`)
Reload the Generate authority and required execution references; do not reconstruct or replay the earlier planning conversation. Reload the Generate authority and required execution references; do not reconstruct or replay the earlier planning conversation.
**Source materials**: the execution session is fresh; `<project_path>/sources/<file>.md` is NOT in context. The Executor SHOULD read the relevant `sources/` files when crafting per-page content — they hold the concrete facts, quotes, names, and details that turn skeleton outlines into substantive slides. `design_spec.md §IX` only carries the per-page intent; the source materials carry the texture. **Source verification**: the execution session is fresh, but `design_spec.md §IX` already owns the complete approved page wording. Read only the relevant `sources/` passages needed to resolve explicit `Fact IDs` / source references or verify exact facts, quotes, names, and data already required by the current §IX block. Never turn §IX back into a skeleton, add new claims or details, or independently rewrite its content. If §IX lacks executable wording or evidence, stop and return to Generate Step 4 for Design Spec repair.
> Note: this stage does NOT duplicate Step 6 / Step 7 content. `generate-pptx.md` is the authoritative procedure; resume-execute only adds the resumption entry, sanity check, and source-materials guidance. > Note: this stage does NOT duplicate Step 6 / Step 7 content. `generate-pptx.md` is the authoritative procedure; resume-execute only adds the resumption entry, sanity check, and source-verification guidance.
--- ---
@@ -1,99 +1,86 @@
--- ---
description: Main-pipeline intake stage that gathers source material for topic-only requests. description: Generate Step 1 intake stage that fills externally verifiable factual gaps before Strategist confirmation.
--- ---
# Topic Research Stage # Topic Research Stage
> Generate-PPTX intake stage. Run before [`generate-pptx`](../generate-pptx.md) Step 1 when the user supplies only a topic or requirements with no source files. Output is a research document, a stable fact-provenance file, and an image folder, all shaped to feed `project_manager.py import-sources` directly. > Strategist-owned factual preparation inside [`generate-pptx`](../generate-pptx.md) Step 1. Run immediately for topic-only input, or after supplied material is converted and read when it leaves planning-critical factual gaps. Output is a research supplement plus stable fact provenance for Step 2 import.
This stage is **context-independent**: it owns source acquisition when no file exists; subsequent [`generate-pptx`](../generate-pptx.md) steps proceed normally with the produced materials as input. This stage supplies facts needed to plan the requested deck. It does not select, download, or generate images; image selection belongs to the final Strategist plan, and AI / web / slice acquisition runs in Generate Step 5 after final confirmation.
## When to Run ## When to Run
| User-supplied input | Action | | Material state | Action |
|---|---| |---|---|
| Topic name only (e.g. "做一个关于宫崎骏的 PPT") | Run this stage before Generate Step 1 | | Topic or requirements with no supporting facts | Research the factual baseline needed for the requested outcome |
| Requirement description without facts (e.g. "介绍我们公司新产品") | Run this stage before Generate Step 1 | | Supplied files or chat content cover only part of the requested outcome | After conversion and reading, research only the identified externally verifiable gaps |
| ≥1 page of substantive content already in chat | Skip — feed chat content into [`generate-pptx`](../generate-pptx.md) Step 1 directly | | Supplied material already supports the requested outcome | Skip this stage and continue Generate Step 1 |
| Source file attached (PDF / DOCX / URL / Markdown) | Skip — go to [`generate-pptx`](../generate-pptx.md) Step 1 source conversion | | User requires a closed corpus, source-only transformation, or no external enrichment | Skip this stage and keep planning within supplied material |
**Sufficiency test**: a gap exists when the Strategist would otherwise need to invent, omit, or leave unsupported an externally verifiable claim required by the user's requested outcome. File presence, source length, and a generic topic taxonomy do not decide sufficiency.
**Hard rule — preserve supplied facts**: supplement the user's material; never silently replace it. Record a material source conflict for Strategist review instead of choosing a different claim without disclosure. Do not research omissions outside the requested scope.
--- ---
## Step 1: Confirm topic ## Step 1: Define the gap brief
**BLOCKING**: confirm scope as a single bundled clarifier. Skip when the user's initial message already covers it. **BLOCKING**: confirm only missing scope or research-boundary decisions as one bundled clarifier. Skip when the request and supplied material already make them clear.
| Item | Default if user did not specify | | Item | Default if unspecified |
|---|---| |---|---|
| Topic | (from user input) | | Topic | From the user request |
| Scope / focus | Broad overview | | Requested scope / outcome | From the user request; otherwise broad overview |
| Depth | General-knowledge level | | Supplied-material baseline | Facts and claims already available |
| Research gaps | Only facts needed to support the requested outcome |
| External-source boundary | External factual enrichment allowed; supplied facts remain authoritative inputs |
| Output language | Match user input | | Output language | Match user input |
| Target audience | Audience implied by the request; otherwise state a provisional general audience | | Target audience / communication intent | Use what is already explicit; leave final confirmation to the main Strategist stage |
| Research-facing communication intent | Open prose describing what the eventual presentation should accomplish; may combine several purposes | | Research stem (`<research_slug>`) | `<topic_slug>_research`; choose another unused snake_case stem rather than overwrite an existing file |
| Desired audience outcome | What the audience should know, understand, believe, decide, or do after the presentation |
| Slug for files (`<topic_slug>`) | snake_case English identifier derived from topic |
**Forbidden — itemized confirmation**: do NOT ask each row separately. One bundled clarifier or none. Do not repeat the full main-pipeline confirmation here. Generate Step 4 still confirms the complete communication contract and deck solution after the factual inputs are ready.
**Communication intent is not a pick-list.** You may mention inform / explain / persuade / decide / align / teach / report and account / mobilize / record and hand off as examples that help the user answer, but never ask them to select one label. Preserve multiple purposes plus their priority / sequence. This intake captures only the subset needed to aim research; main-pipeline Stage 1 confirms the full communication contract—audience, outcome, core message / ask, delivery context, artifact afterlife, and source-treatment intent—after the facts exist. Every editable Stage-1 prose field may remain blank after confirmation. Stage 2 confirms reading mode with the complete deck solution.
--- ---
## Step 2: Gather via web search ## Step 2: Gather factual sources
**Tools** — use the web search and web fetch tools the current IDE provides: Use the web search and fetch tools supplied by the current IDE. If none are available, pause and ask the user for authoritative URLs covering the declared gaps, then fetch each with:
| IDE | Web search | Web fetch |
|---|---|---|
| Claude Code | `WebSearch` | `WebFetch` |
| Cursor / Codebuddy / VS Code + Copilot | provider-equivalent built-in | provider-equivalent built-in |
| None available | — | fallback below |
**Fallback when no IDE web tools** — pause, ask the user for 24 authoritative URLs (Wikipedia / official site / institutional release), then fetch each:
```bash ```bash
python3 ${SKILL_DIR}/scripts/source_to_md/web_to_md.py <URL> python3 ${SKILL_DIR}/scripts/source_to_md/web_to_md.py <URL>
``` ```
**Search strategy**:
| Phase | Action | | Phase | Action |
|---|---| |---|---|
| Landscape | One broad search; identify authoritative sources | | Orient | Search only far enough to map authoritative sources to the declared gaps |
| Deep fetch | Pull 24 highest-signal pages in full | | Deep fetch | Read the highest-signal primary or authoritative pages in full |
| Targeted fill | Search for subtopics the deep fetch flagged | | Targeted fill | Search only for gaps still unsupported after those reads |
**Source priority**: | Priority | Source |
| Tier | Source |
|---|---| |---|---|
| 1 | Wikipedia / Wikimedia Commons | | 1 | Primary sources, official sites, institutional releases, standards, or original research |
| 2 | Official sites, institutional releases | | 2 | Authoritative reference works and reputable academic sources |
| 3 | Reputable news / academic articles | | 3 | Reputable reporting or analysis when primary evidence is unavailable |
| Avoid | Stock-aggregator watermarked images, social-media reposts without source | | Avoid | Unsourced reposts, unverifiable summaries, and stock-aggregator pages |
**Stop condition**: stop when gathered material covers overview / history / key aspects / impact / sources with concrete facts and named entities. Endless searching produces noise. **Stop condition**: stop when every declared gap has enough sourced evidence for the Strategist to decide whether and how to include it. Do not expand into unrelated overview / history / outlook sections merely to make the research look complete.
--- ---
## Step 3: Save materials ## Step 3: Save the factual supplement
Three artifacts under `projects/`: Write two artifacts under `projects/`:
| Artifact | Path | | Artifact | Path |
|---|---| |---|---|
| Research document | `projects/<topic_slug>.md` | | Research supplement | `projects/<research_slug>.md` |
| Fact provenance | `projects/<topic_slug>.facts.json` | | Fact provenance | `projects/<research_slug>.facts.json` |
| Image folder | `projects/<topic_slug>/` |
**Hard rule — naming**: filename (without `.md`) and folder name MUST match. **Hard rule — location**: under `projects/`, never the repository root. **Hard rule — location and preservation**: write both files under `projects/`, never the repository root. Do not overwrite an existing user file; choose a new research stem instead. This stage creates no image folder.
**Document structure** — begin with a compact `## Research Brief` carrying Target audience, Communication intent, and Desired audience outcome in open prose. Then let section layout follow the topic: person → biography / works / impact; technology → background / mechanism / applications / outlook; company → overview / products / market / culture. The file MUST end with a `## Sources` section listing the URLs used. Begin the research Markdown with a compact `## Research Brief` containing the supplied-material baseline, declared gaps, audience / intent already known, and requested outcome. Organize the body by gap, include concrete facts only, flag material conflicts, and end with `## Sources` listing every URL used.
**Content density** — concrete facts (dates, names, numbers, quotes). Skip filler prose; the Strategist composes final slide copy. Write every externally sourced claim that may enter the deck to `<research_slug>.facts.json` with a stable sequential ID, especially quantitative, date, ranking, attribution, and named-entity claims. Do not include user-supplied claims or invented scenario values. When no external claim is retained, write the schema with an empty `facts` array.
**Fact provenance** — write every externally sourced, verifiable claim that may enter the deck to `<topic_slug>.facts.json` with a stable sequential ID, especially quantitative, date, ranking, attribution, and named-entity claims. Do not put invented demonstration values in this file; Strategist marks those as `scenario` later. When research yields no external claims, still write the schema with an empty `facts` array.
```json ```json
{ {
@@ -112,38 +99,24 @@ Three artifacts under `projects/`:
} }
``` ```
IDs are immutable within the file. If a claim is corrected, update its value/source under the same ID; if a claim is removed, do not silently reuse its ID for a different fact. The research Markdown and `facts.json` must agree. IDs are immutable within the file. Correct a claim under the same ID; never reuse a removed ID for a different fact. The research Markdown and provenance file must agree.
**Images**:
| Decision | Rule |
|---|---|
| Quantity | Cover the deck's likely scenes (cover, key aspects, key entities); the Strategist decides the final cut |
| Resolution | Prefer originals. Wikimedia: strip `/thumb/` and the `Npx-` prefix from the URL to get full resolution |
| License | Wikimedia / public-domain / CC-licensed; avoid stock-aggregator watermarks and unsourced uploads |
| Filename | descriptive English snake_case (`joe_hisaishi_concert.jpg`, not `image1.jpg`) |
```bash
mkdir -p "projects/<topic_slug>"
curl -L -o "projects/<topic_slug>/<descriptive_name>.<ext>" "<image_url>"
```
--- ---
## Hand-off ## Hand-off
Output a checkpoint, then continue with the main pipeline. The artifacts feed directly into Step 2's `import-sources`: Import the research supplement and provenance alongside any user-supplied sources in Generate Step 2:
The Research Brief is evidence-facing context, not a locked presentation contract. Strategist reads it when preparing Stage 1, then confirms / edits the full contract with the user before choosing narrative mode, template reuse, or visual direction. ```bash
python3 ${SKILL_DIR}/scripts/project_manager.py import-sources projects/<project_name> [<source_paths...>] projects/<research_slug>.md projects/<research_slug>.facts.json
```
The Research Brief remains evidence-facing context, not a locked presentation contract. Strategist reads all imported source material, then confirms the complete contract and decides the content, page roster, and image resource plan. Generate Step 5 acquires only the selected image rows after that confirmation.
```markdown ```markdown
## ✅ Topic Research Complete ## ✅ Topic Research Complete
- [x] Document: `projects/<topic_slug>.md` (N sections) - [x] Research supplement: `projects/<research_slug>.md` (N declared gaps covered)
- [x] Facts: `projects/<topic_slug>.facts.json` (N external facts) - [x] Fact provenance: `projects/<research_slug>.facts.json` (N external facts)
- [x] Images: `projects/<topic_slug>/` (N files) - [x] No images acquired before Strategist confirmation
- [ ] **Next**: [`generate-pptx`](../generate-pptx.md) Step 2 → - [ ] **Next**: return to [`generate-pptx`](../generate-pptx.md) Step 1, then import all source artifacts in Step 2
`project_manager.py init <project_name> --format <format>`
`project_manager.py import-sources projects/<project_name> projects/<topic_slug>.md projects/<topic_slug>.facts.json projects/<topic_slug>/*.* --move`
``` ```
`<project_name>` is the user's chosen project identifier (typically `<format>_<topic_slug>`, e.g. `ppt169_joe_hisaishi`); `--move` removes the research artifacts from `projects/<topic_slug>` after they are imported and deletes the folder itself once it is empty.
@@ -20,7 +20,9 @@ The calculator has direct CLI models for simple bars, lines/scatter, pie/donut,
## Step 1: Build the page list from the design spec ## Step 1: Build the page list from the design spec
Read `<project_path>/design_spec.md` §VII Visualization Reference List (authoritative deck plan; cross-check against §IX page outline) and include every page whose SVG geometry is driven by data values. Classify each included page into exactly one mode: Read `<project_path>/design_spec.md` §IX Content Outline as the authoritative page roster and include every page whose `Visualization` explicitly declares SVG geometry driven by data values. Cross-check §VII when present to resolve a selected catalog key; absence from §VII means only that no reusable reference was selected. For legacy specs, a real §VII data-chart row may enumerate the page when its §IX block predates the explicit data-driven declaration. Classify each included page into exactly one mode:
Incidental microvisuals not promoted in the §IX page plan are not inferred into this list. If one requires coordinate verification, repair that page's §IX `Visualization` first so the Strategist-owned plan remains authoritative.
| Mode | `charts_index.json` keys | Notes | | Mode | `charts_index.json` keys | Notes |
|------|--------------------------|-------| |------|--------------------------|-------|
@@ -48,7 +50,7 @@ P11 11_share_split.svg type=pie mode=direct-calc
P15 15_pareto.svg type=pareto mode=decomposable-calc P15 15_pareto.svg type=pareto mode=decomposable-calc
``` ```
If §VII is absent (legacy project / free-structure deck), skip this stage and report: "design_spec.md has no §VII — chart pages cannot be enumerated authoritatively, verify-charts skipped". Do NOT fall back to guessing from SVG content; that reintroduces the silent-skip failure this stage was built to eliminate. If §VII is absent, continue from §IX; this is the normal state when all chart pages use custom structures. Do NOT guess from SVG content when §IX declares no data-driven page—that reintroduces the silent-skip failure this stage was built to eliminate.
If the filtered list is empty, output `verify-charts: spec declares no data-driven chart geometry, nothing to verify` and stop. If the filtered list is empty, output `verify-charts: spec declares no data-driven chart geometry, nothing to verify` and stop.
@@ -144,7 +144,7 @@ post-processing and export.
- **Iteration budget**: default 1 iteration. Bumping to 2 doubles render cost and roughly triples token cost. Only worth it for high-stakes / final-cut decks. - **Iteration budget**: default 1 iteration. Bumping to 2 doubles render cost and roughly triples token cost. Only worth it for high-stakes / final-cut decks.
- **Don't-touch (rubric §3)** is hard-enforced by subagents. If you want the subagent to e.g. change a brand color, that is **out of scope** — make the change manually first, then re-render & re-review. - **Don't-touch (rubric §3)** is hard-enforced by subagents. If you want the subagent to e.g. change a brand color, that is **out of scope** — make the change manually first, then re-render & re-review.
- **Backups**: every modified SVG has a `.review/backup/<page>.iter<N>.svg` rollback anchor. Restore by `cp`. - **Backups**: every modified SVG has a `.review/backup/<page>.iter<N>.svg` rollback anchor. Restore by `cp`.
- **The rubric is not the designer**: it catches collisions, drift, and rhythm errors — it does not improve a fundamentally weak layout. If 80%+ of pages come back `needs_human`, the design spec or the executor's choice of layout patterns is the root cause, not this stage. - **The rubric is not the designer**: it catches collisions, drift, and rhythm errors — it does not improve a fundamentally weak layout. If 80%+ of pages come back `needs_human`, the Design Spec's pattern selection or Executor's realization geometry is the root cause, not this stage.
- **Playwright output discipline**: when an agent uses the playwright MCP tool `browser_take_screenshot` directly (outside the `visual_review.py` script), the `filename` parameter is resolved against the CWD (typically the repo root) — passing a bare relative path will create stray directories inside the repository. Always pass an absolute path: - **Playwright output discipline**: when an agent uses the playwright MCP tool `browser_take_screenshot` directly (outside the `visual_review.py` script), the `filename` parameter is resolved against the CWD (typically the repo root) — passing a bare relative path will create stray directories inside the repository. Always pass an absolute path:
- One-off probe / ad-hoc inspection → `/tmp/probe-<topic>-<n>.png` - One-off probe / ad-hoc inspection → `/tmp/probe-<topic>-<n>.png`
- Project artifact (replaces what the script would have produced) → `<project_path>/.preview/<page>.png` (absolute) - Project artifact (replaces what the script would have produced) → `<project_path>/.preview/<page>.png` (absolute)
@@ -60,7 +60,7 @@ python3 skills/ppt-master/scripts/project_manager.py init "<project_name>" --for
python3 skills/ppt-master/scripts/project_manager.py import-sources "<project_dir>" "<source.pptx>" "<material...>" python3 skills/ppt-master/scripts/project_manager.py import-sources "<project_dir>" "<source.pptx>" "<material...>"
``` ```
**Source import rule**: `project_manager.py import-sources` copies files from outside the repository and moves repo-local files by default, unless `--copy` / `--move` is explicitly supplied. Keep this shared behavior; do not create a separate template-fill import path. **Source import rule**: `project_manager.py import-sources` moves only sources under repository `projects/` and copies all others. `--copy` preserves a projects-local input; `--move` never widens that scope. Reuse this path.
Use this fixed layout: Use this fixed layout:
@@ -2,8 +2,8 @@
"sourceId": "taste-skill", "sourceId": "taste-skill",
"repo": "https://github.com/Leonxlnx/taste-skill.git", "repo": "https://github.com/Leonxlnx/taste-skill.git",
"ref": "main", "ref": "main",
"commit": "7c397f22d3af6f2b3f1925eb147d8e8801086151", "commit": "98565e65bc3274ddf6eb0838734341714057178b",
"adapter": "skill-collection", "adapter": "skill-collection",
"sourcePath": "skills", "sourcePath": "skills",
"syncedAt": "2026-07-18T16:00:01Z" "syncedAt": "2026-07-21T16:00:00Z"
} }
@@ -2,8 +2,8 @@
"sourceId": "ui-ux-pro-max", "sourceId": "ui-ux-pro-max",
"repo": "https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git", "repo": "https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git",
"ref": "main", "ref": "main",
"commit": "b484e8338c25b9cea3a25981a992d2817188971a", "commit": "1307d97a72e6c1cda572cb65471ae5ce82995218",
"adapter": "claude-skill", "adapter": "claude-skill",
"sourcePath": ".claude/skills/ui-ux-pro-max", "sourcePath": ".claude/skills/ui-ux-pro-max",
"syncedAt": "2026-07-20T16:00:00Z" "syncedAt": "2026-07-21T16:00:00Z"
} }