# PowerPoint

> Create, edit, render, verify, and export PowerPoint slide decks. Use when Codex needs to build or modify visually ambitious editable .pptx decks.

- Skill: `pandelisz/powerpoint` (Agent Skill, multi-file: 7 files)
- Install (CLI): `npx skillmds@latest add pandelisz/powerpoint`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pandelisz/powerpoint/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: PandelisZ (https://skillmd.com/u/pandelisz)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/pandelisz/powerpoint

---


# PowerPoint

Use this skill whenever the user asks Codex to create, generate, edit, revise, improve, render, verify, or export a PowerPoint or slide deck, unless the user explicitly requests a different slide skill or tool.

This is the internal PPTX workflow for visually ambitious editable decks.

## Core Contract

- Use the existing artifact-tool Node environment with `@oai/artifact-tool` for final deck construction, scratch preview rendering, internal editable-text verification, and `.pptx` export.
- Use the installed `@oai/artifact-tool` package from the default Codex runtime node_modules path: `~/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules`.
- Final user-facing response must include a short summary of the deck/slides created or edited, plus standalone Markdown link(s) only to final `.pptx` artifact(s), with link text formatted as `<deck or slide title> - <filename>` and an absolute filesystem path as the target.
- Use the output-chip-compatible Markdown link shape, for example `[Smoke Test - output.pptx](/absolute/path/to/output.pptx)`; replace the example title, filename, and placeholder path with the actual values. If there are multiple requested final decks, put each final `.pptx` Markdown link on its own line. Do not wrap final artifact links in backticks or code fences, and do not put them in bullets, headings, or prose sentences.
- The final response summary must describe only the user-visible result. Do not mention implementation details such as `artifact_tool`, artifact-tool, `@oai/artifact-tool`, the Node/JS builder, scripts, package manifests, export workflow, verification workflow, or internal tooling unless explicitly requested.
- Do not link to or mention support files such as previews, narrative plans, verification records, scripts, package manifests, or scratch files unless explicitly requested. Keep support files on disk if useful.
- If the artifact-tool Node environment is missing, install or refresh the Codex runtime bundle before building.
- Do not use the Python deck-authoring client for final deck construction. Do not start, repair, or depend on the artifact-tool Python RPC daemon.
- If you need to read PDFs locally, use the Codex primary runtime Python with `pypdf` since we don't have the javascript version installed.
- Use the platform-native imagegen tool for generated art plates.
- Helper scripts in this skill are Node.js scripts. Do not use Python helper scripts for the JS skill path.
- Use generated reference images directly and let the deck builder place them with `fit: "cover"`.
- Keep the pro workflow quality bar: text-free art-direction plates, deterministic editable design-system geometry, scratch rendered previews, editability checks, and native chart verification.
- Do not use `PptxGenJS`, `pptxgenjs_helpers`, `python-pptx`, LibreOffice rendering, or screenshot-only slides.
- For data charts, graphs, plots, trend lines, bars/columns, scatterplots, pie/donut charts, treemaps, and maps, use native artifact-tool chart objects via `slide.charts.add(...)`. Do not hand-draw these with shapes, connectors, dots, or text boxes when the chart API can represent them.
- Use shapes for cards, labels, icons, non-data diagrams, connectors, annotations, and decorative structure. Shape-drawn sparklines or microcharts are exceptions only when native chart APIs cannot represent the visual; record the exception in scratch verification notes instead of mentioning it in the final response.
- Do not make ImageMagick, `montage`, or `magick` part of the required authoring path. Contact sheets are optional and must never block `.pptx` export, rendered previews, or lightweight verification.
- During eval harness runs, do not spawn sub-agents or delegate. Finish the deck, verification, and final response in the current agent so the run does not fail on model-selection or handoff issues.

## Node Authoring Surface

Start deck authoring in a local builder file with:

```ts
const {
  Presentation,
  PresentationFile,
} = await import("@oai/artifact-tool");
```

For existing decks, also import `FileBlob`:

```ts
const { FileBlob, PresentationFile } = await import("@oai/artifact-tool");
const pptx = await FileBlob.load("input.pptx");
const presentation = await PresentationFile.importPptx(pptx);
```

Default new deck size is 16:9 at `1280x720`:

```ts
const presentation = Presentation.create({
  slideSize: { width: 1280, height: 720 },
});
```

Write final deck deliverables under `outputs/<unique_thread_id>/` unless the user gives an explicit output folder. The final deck artifact is `output.pptx`; any agent-authored `narrative_plan.md` created during planning is a support file. Keep previews, inspect records, reference images, and verification scratch under `tmp/slides/<deck-id>/`.

## Using artifact_tool Presentation APIs

Use this quick surface first when editing the JS builder. It is a curated subset of the full TypeScript presentation API, not the full reference.

### Build rules
- Use `@oai/artifact-tool` from Node for final deck construction, rendering, editable-text verification, and `.pptx` export.
- All size and positioning units are pixels unless a specific API notes otherwise.
- Author meaningful content as editable PowerPoint objects: `slide.shapes`, `shape.text`, `slide.tables`, `slide.charts`, and `slide.speakerNotes`.
- Always use `slide.charts.add(...)` for data-backed charts and graphs that the API can express; do not construct bar, line, scatter, pie, treemap, map, axis, series, or data-label systems out of raw shapes/text boxes.
- Treat native charts as designed slide elements, not default exports: set chart fonts, label sizes, gridlines, axis lines, legend placement, series colors/strokes, and plot area styling so the chart matches the rest of the slide.
- Use imagegen output as text-free visual plates only; keep real slide words, labels, numbers, tables, and chart text in editable objects.
- Common geometry strings include `"rect"`, `"roundRect"`, `"ellipse"`, `"rightArrow"`, and `"connector"`; use `geometry: "custom"` with `customPaths` when a preset cannot express the vector shape.
- Render with `presentation.export({ slide, format: "png", scale: 1 })` before final export, and use scratch inspect records to confirm important copy is editable.

### Conventions and gotchas
- New deck: `Presentation.create({ slideSize: { width: 1280, height: 720 } })`.
- `Presentation` is the in-memory deck object; `PresentationFile` is for `.pptx` import/export.
- Existing deck: `PresentationFile.importPptx(await FileBlob.load("input.pptx"))`.
- Export: `const pptx = await PresentationFile.exportPptx(presentation); await pptx.save("output.pptx")`.
- Rendering: `await presentation.export({ slide, format: "png", scale: 1 })`.
- Fill values can be theme aliases or hex strings: `"accent1"`, `"background1"`, `"text1"`, `"#FF6600"`, `"#11223380"`.
- Stroke values use `{ style: "solid" | "dashed" | "dotted" | "dash-dot" | "dash-dot-dot", fill, width }`.
- Gradient stop offsets are `0..100000`; rotation is in degrees.
- Set font faces with `typeface`.
- Style assignment is paragraph-scoped: `shape.text.get("word").style = "heading1"` styles the whole paragraph and clears inline overrides. Apply inline overrides after assigning styles.
- `shape.text.insets` and `fontSize` are pixels, but `spacingBefore` and `spacingAfter` are 1/100 point.
- Use `fit: "cover"` to fill a frame with possible cropping; use `fit: "contain"` to preserve the entire image.
- For local images, read bytes with `readImageBlob(...)` and pass an exact `ArrayBuffer` as `{ blob: await readImageBlob(path) }`.
- For line charts, set `chart.lineOptions.grouping = "standard"` before export. Leaving grouping unset can produce repair-prone PowerPoint chart XML.
- Avoid `chart.chartFill` in PPTX exports for now; prefer a panel/background shape behind the chart plus `chart.plotAreaFill`. If whole chart-space styling is required, verify the file opens in PowerPoint without repair.
- Match chart typography to the slide: set `chart.titleTextStyle.typeface`, `chart.legend.textStyle.typeface`, `chart.xAxis.textStyle.typeface`, `chart.yAxis.textStyle.typeface`, and `chart.dataLabels.textStyle.typeface` to the same body/title font family used elsewhere.

Local image helper:
```ts
const fs = await import("node:fs/promises");

async function readImageBlob(imagePath) {
  const bytes = await fs.readFile(imagePath);
  return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
}
```

### Core deck and slide APIs
- `const { FileBlob, Presentation, PresentationFile } = await import("@oai/artifact-tool")`
- `const presentation = Presentation.create({ slideSize: { width: 1280, height: 720 } })`
- `const imported = await PresentationFile.importPptx(await FileBlob.load("input.pptx"))`
- `const pptx = await PresentationFile.exportPptx(presentation); await pptx.save("output.pptx")`
- `const slide = presentation.slides.add()`
- `const { slide, index } = presentation.slides.insert({ after: presentation.slides.getItem(0) })`
- `presentation.slides.getItem(0)`, `presentation.slides.items`, `presentation.slides.count`
- `slide.duplicate()`, `slide.moveTo(index)`, `slide.delete()`
- `slide.background.fill = "background1"` or a solid/gradient fill object

### Shapes and rich text
- `slide.shapes.add({ geometry, position, fill, line })`
- Common `geometry` values are non-exhaustive; use known preset names first and verify each shape by rendering the deck.
- `shape.position = { left?: number, top?: number, width?: number, height?: number, rotation?: number, horizontalFlip?: boolean, verticalFlip?: boolean }`
- `shape.fill = "accent1"`; `shape.line.width = 1.5`; `shape.rotation = 15`
- `shape.text = "Quarterly Results"` or `shape.text = ["Line 1", "Line 2"]`
- Round-rect corner control: `adjustmentList: [{ name: "adj", formula: "val 16667" }]`; `50000` is pill-like max rounding.
- `shape.text.fontSize = 28`, `shape.text.bold = true`, `shape.text.color = "text1"`
- `shape.text.typeface = "Aptos"`, `shape.text.alignment = "center"`, `shape.text.verticalAlignment = "middle"`
- `shape.text.insets = { left: 12, right: 12, top: 8, bottom: 8 }`
- `shape.text.autoFit = "shrinkText"` or `"resizeShapeToFitText"` only when needed.
- `shape.text.get("literal text").bold = true`, `shape.text.get("literal text").style = "heading1"`
- `shape.text.add("New paragraph")`, `shape.text.replace("Old", "New")`

Detached rich text:
```ts
const { Text } = await import("@oai/artifact-tool");
const block = Text.create(["Quarterly Business Review", "Q2 Execution Plan"]);
block.get("Quarterly Business Review").style = "title";
block.get("Quarterly Business Review").fontSize = 38;
block.get("Q2 Execution Plan").color = "accent1";
shape.text = block;
```

Connector example:
```ts
slide.shapes.add({
  geometry: "connector",
  kind: "elbow",
  from: sourceShape,
  fromIdx: 3,
  to: targetShape,
  toIdx: 1,
  line: { style: "solid", fill: "accent1", width: 2 },
  head: { type: "arrow", width: "med", length: "med" },
});
```

### Images, tables, charts, and notes
- `slide.images.add({ blob, fit: "cover", alt })`, `slide.images.add({ dataUrl, fit: "contain", alt })`, `slide.images.add({ uri, alt })`
- `image.position = { left, top, width, height }`
- `image.replace({ blob: replacementBlob, alt: "Updated hero" })`
- `image.crop = { left: 0.05, top: 0.05, right: 0.05, bottom: 0.05 }`
- `image.geometry = "roundRect"` or pass `geometry: "roundRect"` at creation for masks
- Text-free plate pattern:
  ```ts
  const plate = slide.images.add({
    blob: await readImageBlob("tmp/slides/pro-reference-images/slide-01.png"),
    fit: "cover",
    alt: "Text-free visual plate",
  });
  plate.position = { left: 0, top: 0, width: 1280, height: 720 };
  ```
- `const table = slide.tables.add([["Metric", "North", "EMEA"], ["Bookings", 120, 94]])`
- `const table = slide.tables.add({ rows, columns, left, top, width, height, values })`
- `table.getCell(row, col).value = "APAC"`, `table.setValues(matrix)`
- `table.merge({ startRow, endRow, startColumn, endColumn })`
- `table.style = "TableStyleMedium9"`; `table.columns.get(0).width = 220`
- `table.cells.block({ row, column, rowCount, columnCount }).fill = "#0F172A"`
- Use `slide.charts.add(...)` for every data-backed chart or graph; do not implement bar/line/scatter/pie/treemap/map charts with `slide.shapes.add(...)`.
- `const chart = slide.charts.add("line" | "bar" | "scatter" | "pie" | "treemap" | "map" | "bar3D")`
- `chart.position = { left, top, width, height }`
- `chart.title = "Quarterly Revenue"`, `chart.categories = ["Q1", "Q2", "Q3", "Q4"]`
- `const series = chart.series.add("Revenue"); series.values = [120, 140, 180, 210]; series.categories = chart.categories`
- `series.fill = "accent1"`; `series.stroke = { width: 2, style: "solid", fill: "accent1" }`
- `chart.hasLegend = true`; `chart.legend.position = "bottom"`
- `chart.barOptions.direction = "column"`; `chart.barOptions.grouping = "stacked"`
- `chart.dataLabels.showValue = true`; `chart.dataLabels.position = "outEnd"`
- For line charts: `chart.lineOptions.grouping = "standard"`; `chart.lineOptions.smooth = false`
- Match chart fonts to slide fonts: `chart.titleTextStyle.typeface = FONT.title`; `chart.legend.textStyle.typeface = FONT.body`; `chart.xAxis.textStyle.typeface = FONT.body`; `chart.yAxis.textStyle.typeface = FONT.body`; `chart.dataLabels.textStyle.typeface = FONT.body`
- Tune chart aesthetics with chart APIs: `chart.titleTextStyle.fill`, `chart.titleTextStyle.fontSize`, `chart.legend.textStyle.fontSize`, `chart.xAxis.textStyle.fontSize`, `chart.yAxis.textStyle.fontSize`, `chart.yAxis.majorGridlines`, `chart.xAxis.line`, `chart.yAxis.line`, `chart.plotAreaFill`, `series.fill`, and `series.stroke`
- Prefer a separate `slide.shapes.add({ geometry: "roundRect", ... })` panel behind the chart instead of `chart.chartFill` until chart-space fill export is verified.
- `slide.speakerNotes.setText("Presenter notes")`, `slide.speakerNotes.append(["Next point", "Final point"])`

### Theme, styles, auto-layout, and placeholders
- `presentation.theme.colorScheme = { name, themeColors: { accent1, accent2, bg1, bg2, tx1, tx2, ... } }`
- `presentation.theme.hexColorMap`
- `const style = presentation.styles.add("metricLabel")`; set `style.fontSize`, `style.bold`, `style.color`, `style.typeface`
- `presentation.styles.describe()` or `presentation.styles.describe("title")`
- Import `AutoLayout`, `AutoLayoutAlign`, and `AutoLayoutDirection` only when using auto-layout.
- `slide.autoLayout(shapes, { direction: AutoLayoutDirection.horizontal, frame: "slide", align: AutoLayoutAlign.topCenter, horizontalGap: 32, verticalPadding: 48 })`
- `AutoLayout.apply(slide, shapes, options)` is the static entrypoint.
- Use direct shapes/text for non-chart one-off generated layouts. If layout reuse is useful: `const layout = presentation.layouts.add("Title Slide")`; `layout.placeholders.add({ name: "Title", type: "title", index: 1, text: "Title" })`; `slide.setLayout(layout)`; `slide.placeholders.getItem("title").text = "Kickoff"`.

## Required Workflow

1. Plan the deck:
- Define audience, narrative arc, slide list, source plan, visual system, icon/style plan, and likely hero/diagram/crop assets.
- Create or update an agent-authored `narrative_plan.md` in the final output directory during this step unless the user explicitly asks for PPTX-only output. Include audience, objective, narrative arc, slide list, source plan, visual system, imagegen plan, asset needs, and editability plan. Do not rely on the JS builder to create or overwrite this file.
- If the source plan includes a local PDF, first extract readable text locally with runtime `pypdf`. Use web/source search only if local extraction fails, source data is incomplete, or the task requires current external context.
- Separate every slide into editable text and generated visual. Editable text includes titles, subtitles, body, callouts, dates, timeline labels, captions, tables, chart labels, code/UI text, and important diagram labels.
- Use modern sans by default: `Poppins` for titles and `Lato` for body/captions. Use formal serif only when appropriate: `Caladea` headings and `Liberation Serif` body/captions.

2. Prepare native-imagegen prompts:
- Write one outline file with an intro paragraph and exactly one section per slide.
- Use this skill's `scripts/prepare_reference_prompts.js` to create one prompt per slide and a manifest with expected filenames.
- Ask for text-free art-direction plates: broad calm zones, atmosphere, texture, collage, hero imagery, motifs, unlabeled diagrams/charts, abstract UI chrome, placeholder strokes, and icon containers.
- Do not ask imagegen for real slide words, labels, dates, chart text, table text, citations, logos, code, or readable annotations.
- Store selected generated reference images in `tmp/slides/pro-reference-images/` with exact names `slide-01.png`, `slide-02.png`, and so on.
- If native imagegen saves under `$CODEX_HOME/generated_images/...`, copy or move only the selected final images into the reference directory. Do not leave project-bound reference assets only in the default generated-images location.
- Generate slide 1 first as the visual-system setter. For slides 2..N, use slide 1 as a visual style reference when the platform supports it; otherwise carry slide 1's palette, motif, density, and image treatment in the prompt.
- If a generated image is not exactly 16:9, do not preprocess it. The JS builder uses `fit: "cover"` to place it on the slide canvas.

Example:

```bash
SKILL_DIR=<path-to-installed-slides-skill>
node "$SKILL_DIR/scripts/prepare_reference_prompts.js" \
  tmp/slides/pro-outline.md \
  tmp/slides/pro-reference-images \
  --slide-count 6 \
  --deck-size 1280x720 \
  --style-guidance "text-free art-direction plates, broad calm regions for editable PowerPoint cards and title blocks, warm editorial palette, rounded hero image frames, Lucide-style icon containers, unlabeled diagrams, placeholder strokes only"
```

3. Generate the art plates:
- Use the platform-native imagegen tool once per slide prompt.
- Keep the final selected image for each slide as `slide-XX.png` in the reference directory from the manifest.
- Do not generate or keep image-rendered words, numbers, table text, labels, logos, source text, or code as part of the plate.
- If a visual crop or isolated hero image is needed, prefer regenerating a cleaner text-free plate or placing the full plate with PowerPoint geometry. Avoid raster-cropping helpers in this JS skill path.

4. Build the editable deck in JS:
- Start from `scripts/init_pro_deck_builder_js.js` or write a bespoke Node builder with the same architecture: theme constants, slide data, source notes, `addText`, `addCard`, `addTitleBlock`, `addHeader`, `addPlate`, native chart helpers wrapping `slide.charts.add(...)`, table/card helpers, icon helpers, render/export helpers, and inspect-record helpers.
- Treat generated art plates as visual direction and crop sources. The authored JS layer owns text, cards, labels, native chart objects, icons, and important shapes.
- A full-slide generated art plate may sit behind the deterministic editable layer only when it is text-free.
- For local image files, read bytes with `fs.readFile`, pass an exact `ArrayBuffer` via `{ blob }`, and retain the source path only in scratch inspect records; do not rely on `{ path }` for final `exportPptx` media embedding.
- When using `scripts/init_pro_deck_builder_js.js`, keep the generated sibling `build/node_modules/@oai/artifact-tool` package link when the script creates one. Node resolves `@oai/artifact-tool` from the builder file's directory, so this makes shell-run eval builders import the default Codex runtime package while preserving `await import("@oai/artifact-tool")` in the builder.
- Do not put editable text into generated boxes by guesswork. Author matching editable boxes yourself, or place a clean authored card/panel over a broad calm art region.

Example scaffold:

```bash
SKILL_DIR=<path-to-installed-slides-skill>
node "$SKILL_DIR/scripts/init_pro_deck_builder_js.js" \
  --deck-id demo-slides \
  --output tmp/slides/build_demo_pro_js.js \
  --slide-count 3 \
  --reference-dir tmp/slides/pro-reference-images \
  --out-dir outputs/demo-slides
```

Then edit `SLIDES`/`SOURCES` and slide-specific layout functions, render previews, verify, and export by running the generated builder in the existing Node environment.

Run local builder files from a workspace where `@oai/artifact-tool` is resolvable. Generate builders with `scripts/init_pro_deck_builder_js.js` so the builder directory has a local `build/node_modules/@oai/artifact-tool` package link to the default Codex runtime package and a `package.json` with `type: "module"`. Prefer the default Codex runtime Node binary:

```bash
~/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node <builder-file>
```

Do not use a different `node` binary for `@oai/artifact-tool` builders unless you first prove the package resolves. Do not run `pnpm exec` from the repo root, and do not run a builder without a sibling or otherwise resolvable `node_modules/@oai/artifact-tool`; Node resolves the package from the builder path.

5. Verify:
- Render final slides from JS with `presentation.export({ slide, format: "png", scale: 1 })`; save previews under scratch `tmp/slides/<deck-id>/preview/`, not in the final output folder.
- Write scratch inspect records from JS helper wrappers. Each editable textbox record must include `kind`, `slide`, `role`, `text`, `textChars`, `textLines`, and `bbox`.
- Use the scratch inspect records to confirm major planned copy exists as editable text/table/chart/notes data, not only in images.
- Verify data charts are native chart objects: the builder should contain `slide.charts.add(...)` for every chart-like visual, and the exported PPTX should contain native chart XML parts such as `ppt/slides/charts/chart*.xml` when the deck includes charts.
- When inspecting rendered previews, treat charts as first-class design objects: chart typography should match the surrounding slide font system, colors should use the deck palette, axes/gridlines should be intentional and not default-heavy, labels and legends should be readable without crowding, and the chart should look visually integrated with the panel/card it sits in.
- For every native chart, use chart APIs to improve aesthetics before accepting a render: set `titleTextStyle`, `legend.textStyle`, `xAxis.textStyle`, `yAxis.textStyle`, `dataLabels.textStyle`, axis/gridline strokes, series fill/stroke, legend position, and `plotAreaFill` as appropriate. Do not accept mismatched default chart fonts or generic Office chart styling when the rest of the slide has a designed visual system.
- Track every render/verify attempt in scratch `tmp/slides/<deck-id>/verification/render_verify_loops.ndjson`; the template appends one record each time previews and `output.pptx` are exported. Stop after 3 total render/verify/fix loops, including the initial render.
- Run `ls -1 tmp/slides/<deck-id>/preview/slide-*.png` from the workspace and inspect the exact absolute preview paths at readable size before accepting the deck.
- Check every slide for overlapping elements, text overflow or clipping, decorative rules built for single-line text after a title wraps, footer/source collisions, gaps under 0.3", edge margins under 0.5" outside intentional headers/footers, uneven spacing, inconsistent alignment, low-contrast text/icons, excessive wrapping from narrow text boxes, leftover placeholder content, and table/card/layout-box font sizes that feel unnaturally small or visually timid for their containers.
- Font size should feel natural and aesthetically balanced for the layout: not so large that it overflows, wraps excessively, or collides with boundaries, but not so small that tables, labels, callouts, or card text look like footnotes unless they are genuinely footnotes/citations. Prefer increasing container size, reducing copy, simplifying a table, or splitting dense content before shrinking important text.
- Fix every actionable visual issue in the JS builder, rerender, and repeat until the slide is clean or the 3-loop cap is reached. Loop 1 is the initial render; loops 2 and 3 are the only allowed fix/rerender passes.
- Do not run `scripts/pro_deck_quality_check.js` for normal deck creation. It is reserved for explicit debug, eval, or manual investigation requests.

## Completion Criteria

Complete only when:
- The final deck is exported as `output.pptx`; any agent-authored `narrative_plan.md` remains a support file that is not surfaced unless requested.
- Internal implementation check only: the final deck is built, rendered, verified, and exported with the required runtime. Do not mention this tooling or workflow in the final response.
- Important copy was internally verified as editable.
- Data-backed charts and graphs use native `slide.charts.add(...)`; any shape-drawn sparkline/microchart exception is recorded in scratch verification notes.
- Rendered previews were inspected, and any actionable visual issues were fixed within the 3-loop cap.
- The final response includes a short summary of the deck/slides created or edited and standalone Markdown link(s) only to final `.pptx` artifact(s), using `<deck or slide title> - <filename>` as the link label and an absolute filesystem path as the target.
- The final response does not mention implementation details such as `artifact_tool`, artifact-tool, `@oai/artifact-tool`, the Node/JS builder, scripts, package manifests, export workflow, verification workflow, or internal tooling unless explicitly requested.
- The final response does not mention or link to `narrative_plan.md`, rendered previews, verification records, scripts, package manifests, scratch files, or other support artifacts unless explicitly requested.

## References

Use the installed `@oai/artifact-tool` TypeScript presentation docs when they are available in the current environment. Do not assume local checkout-specific absolute paths exist in packaged installs.

