Imported from SanAntonio021/agents-skills (
pptx/SKILL.md). Install upstream withnpx skills add SanAntonio021/agents-skills --skill pptx. Copyright stays with the author.
PPTX creation, editing, and analysis
文件存放与交付
- 文件归属、正式位置和清理时机遵循已加载的共享全局规则。候选、临时脚本、预览和临时核验记录放在本任务过程目录;必要编辑源和资源依赖作为正式内容保留。
- 沿用项目命名习惯;没有约定时用
内容主题_v01.扩展名,同名递增版本。生成并通过必要检查后,由智能体在最终回复前自动交付正式成果到约定的正式位置,复核交付文件的哈希、可打开性及必要依赖,并给出正式路径链接。需要用户挑选时,选定后再交付;不另设确认环节。 - 使用工具的显式输出参数或将工作目录设到任务过程目录,保留工具所需内部结构;正文命令中的相对输出路径均以该目录为基准,技能脚本及输入路径使用绝对路径。不修改上游插件缓存。可编辑源、正式工程及原始数据保留其用途,不一律当作临时文件。
- 普通任务结束后保留过程材料,用户显式触发 ChatNote(
chat-notes)或明确要求清理后才进入可恢复清理;不自动清空过程目录。工具用于进程隔离、安全回滚的内部暂存清理不等于任务清场,仍遵守原有保护门。
Choose one authoring route
For both new decks and changes to an existing deck, first read the collaboration section in references/presentation-workflow.md. The main conversation discusses content; one user-visible production conversation per deck executes authorized page work. Content approval authorizes the next page of a new deck, but does not by itself authorize editing an existing deck. Honor explicit edit scope and the host's requirements for creating a new conversation. Read-only inspection does not require a production conversation.
Keep this skill as the local PowerPoint entry, routing and acceptance owner. For creation,
redesign, template reuse, reconstruction or multi-agent slide work, first read
references/presentation-workflow.md. Its ordered routing
rules honor explicit tools and existing authoring projects, retain the simple lab-report-slides
route, use PPT Master for its specialist workflows, and otherwise prefer available official
Presentations for new authoring. This is an operating default, not a design-quality ranking.
Keep reading, extraction, inspection, validation, combining/splitting and small deterministic edits here. Announce the chosen authoring route once, then let it own generation; do not run competing generators or silently switch after a failure. Asset preparation and local merge/QA are supporting stages, not a second authoring route.
For a multi-page technical deck, define the independent question each page answers before authoring it. Reuse shared architecture only when it establishes context; subsequent pages should carry their own distinct function (for example, link capability versus installation and operations) rather than redrawing the same topology. Confirm page text, metrics, footnotes, and comparison conclusions before assembly, and treat confirmation as content approval only unless the user explicitly authorizes editing an existing deck.
Effect previews, material assets, and editable pages
Keep these three deliverables distinct in names, records, and review:
- an effect preview is a page-level composition image used to confirm hierarchy, technical relationships, scale, and visual direction;
- a material asset is a reusable background, device cutout, icon, or local illustration that may be placed on several pages;
- an editable page is the assembled PPTX whose text, arrows, key frames, and change-prone indicators remain native objects where practical.
Use an effect preview to settle the page's intended result before spending effort on the editable assembly when the layout or system relationship is still uncertain. Do not present the preview as the final PPT, an editable source, or evidence that every object can be independently changed. Conversely, do not flatten a finished page into one screenshot merely to match the preview: retain independent assets and native objects at the layer where later edits are expected.
When a visual or architecture component is later found to be wrong, remove or replace only the identified component and preserve the validated background, topology, and labels. Leave a clear insertion area for the user or a later pass when the replacement asset is not yet confirmed; do not redraw the whole page or silently substitute a different platform, payload, or relay node.
Only when choosing ppt-master, read
references/ppt-master-integration.md and run its external
pin verification against the actual installed root before reading or executing the upstream skill.
Require status=PASS; --pin-only does not prove an installation. Preserve the version, integrity
guard and failure protections in that reference. Follow the selected skill's own workflow without
copying it here. After any authoring route returns a candidate, apply this skill's editability,
rendering and formal-release checks; generation success alone is not acceptance.
The supplied template and approved sample govern this deck's design. Generic design suggestions later in this file apply only where those sources leave a choice open; they do not override existing fonts, colors, separators, image repetition or required engineering/UI layouts. Preserve visual quality through layered assets instead of simplifying a complex scene merely to use native shapes.
Manual text paste into PowerPoint
For PowerPoint desktop questions about pasting text from Codex, a browser, or another rich-text
source into an existing text box, read
references/powerpoint-text-paste.md. Confirm that the text
insertion caret is inside the destination box and use the Paste Options actually shown as evidence.
Do not transfer Word's default-paste settings to PowerPoint or claim that Ctrl+V can be configured
to always keep text only. Paste Options are context-dependent; when a plain-text choice is absent,
use the native Paste Special or Quick Access Toolbar routes in the reference.
A .pptx is a ZIP archive of XML files. After that routing decision, choose your local approach by
task:
OfficeCLI route
For ordinary .pptx/.potx inspection, text extraction, element queries, validation, and small
structural edits, use this skill's bridge so Codex and Claude call the same pinned OfficeCLI:
python <skill-root>\scripts\officecli_bridge.py view input.pptx text
python <skill-root>\scripts\officecli_bridge.py query input.pptx '*' --compact
python <skill-root>\scripts\officecli_bridge.py validate input.pptx
python <skill-root>\scripts\officecli_bridge.py mutate input.pptx draft.pptx batch --input commands.json
The bridge pins OfficeCLI 1.0.151 and verifies its existence, SHA-256, and reported version before
every invocation. Normal presentation work never downloads or repairs it. To repair the default local
binary, run the following only when current authorization covers repair or an upgrade:
python <skill-root>\scripts\repair_officecli.py --repair.
An OFFICECLI_EXE override is subject to the same checks and must be fixed or unset directly; the
repair script only repairs the default path.
The bridge copies input.pptx to new draft.pptx before mutation and never overwrites an existing
output. It is not a fidelity renderer. OfficeCLI --render native --allow-native is retained only
as an explicit diagnostic probe; its success or failure is never release evidence, and its generic
native error must not be interpreted as "PowerPoint is not installed". Use the independent native
gate below for PowerPoint acceptance. --render html --non-fidelity-preview is also diagnostics only.
HTML/SVG previews must not be used for final images, layout PDF, print/page QA, or publication
graphics. OfficeCLI PDF export is disabled because the pinned installation has no exporter plugin.
The bridge never quits or terminates Office.
Acceptance layers
Safe text replacement in an existing PPTX
For python-pptx edits, reuse scripts/safe_text_replace.py instead of copying paragraph/run
property nodes with ad hoc XML insertion. Its API and style policy are documented in the module.
Replace existing properties rather than appending a second a:pPr or a:rPr. For a local edit
that must retain mixed styles, edit the intended existing run's text instead of rebuilding the
whole paragraph. Do not silently flatten mixed formatting or automatically repair malformed input.
Save to a new path and validate the final saved package after every modification pass.
Import replace_text from that module and call replace_text(shape_or_text_frame, text).
The function changes only the target in memory; it does not save files. Whole-text replacement
uses each corresponding paragraph's first-run style (new paragraphs reuse the last original
paragraph), replacing mixed runs and fields intentionally. Use it only when this style policy
matches the edit; for mixed-style local changes, retain the existing runs. Malformed duplicate
format nodes raise UnsafeTextStructureError without changing the target.
The PPTX validator checks duplicate DrawingML text-format properties separately from XSD and
original-template comparisons. A duplicate-format failure must identify the part and node and
must block delivery even when --original is supplied or compatibility rendering succeeds.
Keep these records separate:
STATIC_PASS:validate.py, OOXML/package checks, typography and source-hash checks.LO_RENDER_PASS: an optional LibreOffice compatibility render and visual inspection, required only when that compatibility check or a specific rendering-difference diagnosis is in scope. Keep its actual status separate; an unrun check is not a pass.NATIVE_OPEN_PASS: the independent gate opened an isolated copy with PowerPoint and readSlides.Count.NATIVE_RENDER_PASS: the same gate exported every slide to a new non-empty PNG.
OfficeCLI validate passing proves only STATIC_PASS; it does not prove that PowerPoint can
open the file. A failed OfficeCLI native probe is reported as
officecli_native_diagnostic_failed with the original stderr and exit code, never as
APP_UNAVAILABLE.
For native evidence, run the gate explicitly for the current task:
python <skill-root>\scripts\office_native_gate.py check input.pptx `
--format pptx --json --allow-office-com
python <skill-root>\scripts\office_native_gate.py check input.pptx `
--format pptx --json --allow-office-com --require-render `
--render-output-dir <new-task-owned-png-directory>
The gate returns PASS, FAIL_OPEN, FAIL_RENDER, APP_UNAVAILABLE, UNVERIFIED, or
UNSAFE_PROCESS, and records the actual phase and exception. It refuses to run without
--allow-office-com. If POWERPNT.EXE already exists, the PPTX gate does not call COM, returns
UNVERIFIED in preflight with reason powerpoint_already_running, and its non-JSON output is
exactly 请关闭 PowerPoint 后重试。 It never attaches to, closes, or kills that process. Otherwise
it defaults to DispatchEx plus an isolated copy and checks the source SHA-256 before and after.
When WPS or another installation interferes with that activation route, use the installed Microsoft
PowerPoint executable explicitly for the current PPTX check:
python <skill-root>\scripts\office_native_gate.py check input.pptx `
--format pptx --json --allow-office-com --require-render `
--render-output-dir <new-task-owned-png-directory> `
--powerpoint-exe "<verified absolute path to Microsoft POWERPNT.EXE>"
Discover the actual local installation and verify its Microsoft signature before choosing that path;
do not hard-code another machine's path or change system registration. The explicit route requires
matching application path, the task-created process as the sole live PowerPoint process, and an
initially hidden, empty instance. The gate never saves the source or force-terminates an application.
It quits only its own empty instance. If document open, requested export and document Close have
all completed, a disconnected COM object during application cleanup is accepted only when the
original verified child process exits normally within three seconds. Unknown ownership, a running
or abnormally exited child, and any earlier failure remain unverified. The receipt records this as
application_lifecycle.cleanup=self_exited; it does not establish that default COM activation is repaired. For a
PowerPoint release, require STATIC_PASS, NATIVE_OPEN_PASS, NATIVE_RENDER_PASS, and inspection
of the actual exported pages. LibreOffice is not a default additional gate. A blocked native check
is not a completed delivery: finish independent file checks and state the specific unverified item
without taking over a user window or promoting another renderer's output to native acceptance.
Python runtime preflight
Before running scripts/office/validate.py or scripts/typography_audit.py, run
scripts/python_runtime_preflight.py with the interpreter currently selected for QA. The
preflight is standard-library-only and probes each candidate in its own process by importing
defusedxml, lxml, and pptx (the python-pptx distribution). It checks the current
sys.executable first, then py -3, PATH Python, and any explicitly supplied system-Python
candidate. Use only the first candidate for which all three imports pass; do not install or repair
packages automatically.
Keep the complete JSON report with the QA evidence. It must record every candidate, its source,
actual interpreter path, Python version, each dependency's ok/version/error result, and the
final selected_executable. If no candidate passes, stop before file validation. The report path
must be an existing QA/evidence directory; the preflight refuses .codex, .cc-switch, .claude,
and bundled runtime paths. The selected interpreter is the only interpreter allowed for the two
static validators, and its path plus the three dependency results belong in the STATIC_PASS
record.
Keep the existing OOXML/pptxgenjs paths for template-sensitive work, unsupported PowerPoint features, and any operation where preserving package parts is the acceptance criterion. OfficeCLI is an interface and does not replace the visual QA or source-hash checks below.
| Task | Approach |
|---|---|
| Create using an explicitly selected local implementation or continue its existing project | Use the owning source; for pptxgenjs, see gotchas below. This is not an automatic fallback |
| Edit deterministic elements of an existing deck or template | unzip → edit ppt/slides/slideN.xml → zip; substantial authoring follows the routing rules above |
| Read content | markitdown deck.pptx (one block per slide under <!-- Slide number: N --> markers); reuse existing current PowerPoint PNG previews for visual inspection |
Scripts
Paths are relative to this skill's directory. Everything else is plain Python, node, or shell.
| Script | What it does |
|---|---|
scripts/thumbnail.py deck.pptx [prefix] |
Optional LibreOffice-dependent template grid. Reuse existing PowerPoint PNGs first; this script accepts .pptx, not a PNG directory. Pass a unique prefix because the default thumbnails can overwrite another deck's grids |
scripts/add_slide.py unpacked/ slide2.xml [--after slideN.xml] |
Duplicate a slide (or a slideLayoutN.xml) with all the package bookkeeping. Also takes a .pptx directly with -o out.pptx |
scripts/clean.py unpacked/ |
Delete slides, media, and rels no longer referenced. Run after <p:sldIdLst> is final |
scripts/release_bundle.py |
Create non-overwriting formal release directories, canonical external-output snapshots, revision slide-difference proofs, and final artifact manifests |
scripts/pptx_editability_audit.py deck.pptx [--json-out PATH] [--fail-on-flattened] |
Report native objects, pictures/SVG media, likely flattened slides, and centered-but-top-anchored text that needs rendered review |
scripts/verify_ppt_master_pin.py --skill-root <path> [--json-out PATH] |
Verify the active CC Switch-installed ppt-master tree against the external bootstrap/transition/stable pin before handoff |
scripts/python_runtime_preflight.py [--candidate PATH] [--json-out PATH] |
Select a verified Python runtime for static PPTX QA; import-checks defusedxml, lxml, and python-pptx, and never installs packages |
scripts/office/validate.py deck.pptx [--original src.pptx] |
Schema, relationship, content-type, chart and slide checks; each failure names its fix. Pass --original for any template-derived deck — it baselines the schema checks against the template, so the template's own XSD errors don't read as yours |
scripts/office/soffice.py --headless --convert-to pdf deck.pptx |
Optional LibreOffice compatibility conversion through the protected wrapper |
On Windows, scripts/office/soffice.py is a thin compatibility adapter. It accepts the limited
conversion command above and delegates all LibreOffice launch, queue, profile, and process management
to the public libreoffice-runner; do not call soffice directly.
Creating with pptxgenjs — gotchas
pptxgenjs is preinstalled — do not run npm install first; write the script and require('pptxgenjs') directly. Only if that require fails: npm install pptxgenjs. The model knows the API; these are the footguns:
- Set
pres.layoutbefore adding slides. For the standard PowerPoint Widescreen preset, useLAYOUT_WIDEbefore adding any slide: it is 13.333" × 7.5" and must serialize asp:sldSz cx="12192000" cy="6858000".LAYOUT_16x9is a different 10" × 5.625" canvas; the aspect ratio matches, but its physical coordinate system does not. Coordinates past the edge are written, not clamped — the shape just isn't on the slide. Theppt-masterlogicalppt169canvas (1280 × 720) maps to the standard wide physical size at export; do not treat pixel dimensions as inches. - Hex colors: never
#, never 8 digits.color: "FF0000". Both"#FF0000"and alpha baked into the hex ("00000020") corrupt the file. For translucency:transparency: 0-100on fills and images,opacity: 0.0-1.0on shadows — each is silently ignored on the other. - pptxgenjs mutates option objects in place (converts values to EMU on first use). Never share one
shadow/options object across twoadd*calls — build a fresh object each time. - Shadow
offsetmust be ≥ 0 — a negative offset corrupts the file. To cast a shadow upward, useangle: 270with a positive offset. - All shape extents must be non-negative.
pptxgenjswrites a negativea:ext cx/cywhen a line or shape is given reversed endpoints (for example,h: y2 - y1withy2 < y1); PowerPoint may report the file as corrupt even when LibreOffice renders it. Normalize line endpoints or use positivew/hplus the appropriate direction before writing. letterSpacingis silently ignored — the real option ischarSpacing.- Lists:
bullet: trueon each item, never a literal•(renders double bullets). SetbreakLine: trueon every array item except the last. Space bulleted paragraphs withparaSpaceAfter, notlineSpacing(huge gaps). - One
new pptxgen()per output file — never reuse an instance. rectRadiusonly works onROUNDED_RECTANGLE, notRECTANGLE.- Gradient fills aren't supported — use a gradient image as the background instead.
- Text boxes have built-in internal padding — set
margin: 0whenever text must align with a shape, line, or icon at the same x. - Speaker notes go in
slide.addNotes("...")(plain text, once per slide), never in a text box on the slide. - Keep charts native. Use
addChart()for everything PowerPoint can chart (pass an array of{type, data, options}for combos). For PowerPoint-native features the library doesn't expose (trendlines, error bars), compute the extra series yourself or post-process the generated OOXML — do not fall back to a rendered image. Only chart types PowerPoint has no native form for (Sankey, network, chord) go in as images. - Default charts render bare — no title, no data labels, dated palette. Set
showTitle+title,showValue: true+dataLabelPosition,chartColors: [...]from your palette, and quiet the frame (catAxisLabelColor/valAxisLabelColor,valGridLine: { color, size },catGridLine: { style: "none" },showLegend: falsefor a single series). - On a stacked bar or column chart,
dataLabelPositionmust bectr,inEnd, orinBase.outEndcorrupts the file. - A combo series using
secondaryValAxis/secondaryCatAxisneeds bothvalAxesandcatAxeson the chart options, two entries each. Without them pptxgenjs writes axis ids it never declares, and PowerPoint discards that chart and reports the file as corrupt. Supplying onlyvalAxesis not enough. - Tables need a post-write shape-ID check. PptxGenJS 4.0.1 can give a table's
p:cNvPr@idthe same value as another object on that slide.objectNamechangesp:cNvPr@name; it does not reserve or replace the numeric ID. Rebuild the deck with an unused per-slide ID rather than relying on the name. - After
writeFile(), runpython scripts/office/validate.py deck.pptx. It reports the two chart faults above, duplicate per-slidep:cNvPr@idvalues (including tables, charts, and grouped shapes), negative DrawingML extents, and the other slide-XML defects PowerPoint refuses. The diagnostics name the slide and the offending object/attribute where available. Fix them in your generator, not by hand-editing the packed XML. - After
writeFile(), runpython scripts/slide_size_audit.py deck.pptx --expected wide16x9. This checks the presentation-levelp:sldSzagainst the standard PowerPoint Widescreen dimensions (12192000 × 6858000EMU). Run it before visual QA; do not accept a same-ratio10" × 5.625"deck as interchangeable with a standard wide deck. - When a deck is derived from a real template, run
python scripts/slide_size_audit.py output.pptx --reference template.pptxinstead of relying on a generic 16:9 preset. The reference gate compares the physical EMU width and height exactly. - Never reorder the children of
<p:presentation>. pptxgenjs writes<p:notesMasterIdLst>right after<p:sldIdLst>and points both masters at one theme part. PowerPoint reads that happily — move the element and the same deck becomes unopenable. - Icons: render
react-iconsto SVG (ReactDOMServer.renderToStaticMarkup), rasterize withsharpat ≥256px, and insert viaaddImage({ data: "image/png;base64," + buf.toString("base64") })— theimage/png;base64,prefix is required (react-icons,react,react-dom, andsharpare preinstalled —npm install react-icons react react-dom sharponly if a require fails).
Editing existing decks and templates
Pick layouts first using existing PowerPoint page images that match the current template. Inspect them
directly or assemble those images into a contact sheet without rerendering the deck. Pair them with
markitdown to map each content section onto a template slide, and vary the layouts — don't put every
section on the same title-and-bullets slide. If no current render exists, use the protected native
export described in Converting to Images.
The retained optional python scripts/thumbnail.py template.pptx template-thumbs uses LibreOffice
to produce a labeled grid (template-thumbs.jpg, split into template-thumbs-N.jpg past 12 slides).
Use it only when LibreOffice rendering is in scope, not merely to obtain another preview. Always pass
a unique deck-specific prefix; the default thumbnails can overwrite other grids. It accepts .pptx
only, so a .potx requires a separate .pptx copy. Grids support layout selection; inspect full-size
pages for visual QA.
python3 -c "import sys,zipfile; zipfile.ZipFile(sys.argv[1]).extractall('unpacked')" deck.pptx
python scripts/add_slide.py unpacked/ slide2.xml --after slide2.xml # duplicate a slide (or slideLayoutN.xml); prints the new slide's path
# reorder / delete slides = edit <p:sldIdLst> in ppt/presentation.xml
python scripts/clean.py unpacked/ # after deletions: removes orphaned slides, media, rels
# edit slide content in ppt/slides/slideN.xml
(cd unpacked && rm -f ../out.pptx && zip -Xr ../out.pptx .) # zip from INSIDE the dir; rm first or deleted parts survive
python scripts/office/validate.py out.pptx --original deck.pptx
- Do all structural work — add, delete, reorder — before editing any slide's content.
add_slide.pycopies a slide file verbatim, so duplicating after you edit clones the edited content; andclean.pydeletes any slide missing from<p:sldIdLst>, including one you just wrote. - Never copy a slide file by hand —
add_slide.pydoes every registration a new slide needs and reports what it made (Created ppt/slides/slide17.xml from slide2.xml). It also works directly on a file:add_slide.py deck.pptx slide2.xml -o out.pptx— pass-o, or it rewrites the input deck in place. A duplicated slide still references its source's chart/SmartArt/embedded-object parts rather than cloning them, so editing one slide's chart changes the other's. - If you use
python-pptx, three things it won't do: duplicate a slide (its only entry point isadd_slide(layout)), preserve formatting throughtext_frame.text = "..."(that collapses the paragraph to a single unstyled run — assignrun.textinstead), or read the SVG/EMF most template art uses (add_pictureraisesUnidentifiedImageError). - Legacy
.pptmust be converted first:python scripts/office/soffice.py --headless --convert-to pptx file.ppt..potxtemplates unpack and pack identically — keep the.potxextension on the output. - To reuse a template icon or image, duplicate a slide or layout that already contains it.
Formal release and revision workflow
Use this workflow only when the task explicitly calls the output a formal deliverable, final candidate, or release. Temporary drafts may continue to use ordinary new-output filenames.
-
Start a formal release inside the task process directory. Before generating the PPTX, run:
python scripts/release_bundle.py init --output-root <project>/过程文件/<task-topic> --topic <topic> ` --pptx-name final.pptx --pdf-name final.pdf --png-dir png --evidence-dir evidenceThe command creates
<topic>_YYYYMMDD_vNNand refuses to reuse an existing directory. Keep the manifest, working PPTX/PDF, PNGs, and QA evidence inside that process directory. The release bundle is internal working structure, not an extra project-root deliverable folder. Do not appendfixed,final2, or similar parallel names after rendering has started. Only when the user explicitly requests a personal visual sign-off, append--require-design-acceptance. Otherwise inspect rendered pages yourself and deliver the checked result. New bundles do not require LibreOffice by default. Add--require-lo-renderwhen compatibility checking or a specific LibreOffice rendering diagnosis is required. Existing manifests retain their recorded requirements; legacy manifests withoutrequire_lo_renderstill require it. New unrequested LibreOffice checks areNOT_REQUIRED, notPASS. Do not rewrite old acceptance history to remove a gate. -
Snapshot the external output root consistently. After the fresh bundle exists, create a before snapshot using the same canonical root and exclusion for the after snapshot:
python scripts/release_bundle.py snapshot --root <project> ` --exclude <project>/过程文件 --output <release-dir>/evidence/integrity-before.json python scripts/release_bundle.py snapshot --root <project> ` --exclude <project>/过程文件 --output <release-dir>/evidence/integrity-after.json python scripts/release_bundle.py compare-snapshots ` <release-dir>/evidence/integrity-before.json <release-dir>/evidence/integrity-after.jsonSnapshots store POSIX paths relative to the same resolved root, byte counts, and SHA-256 values. A root or exclusion mismatch is
scope_mismatchand is not a valid change report. Files inside the current release directory are excluded by design; never repair unrelated changes automatically. -
Revise a formal release in a new version directory. Use the prior release as
--parentand declare the pages intentionally changed. Regenerate all formal artifacts in the new directory, then rerun content QA, structural validation, typography, canvas, and PowerPoint opening and full-deck export. Run the LibreOffice compatibility render only when required for this release.scripts/release_bundle.py compare-slidescompares slide XML, relationship closures, shared parts, and global package parts. Only when it returnsUNCHANGED_SLIDES_PROVEN, the parent manifest records a full-deck visual pass, and all currently required static/native/compatibility gates pass may visual inspection be limited to the declared changed pages. Any undeclared page, relationship, shared-resource, global-part, page count, or parent-evidence difference returnsFULL_VISUAL_QA_REQUIREDand requires every page to be inspected. A partial or unverified native Office gate never becomes a complete release claim. -
Inspect and finalize. Agent visual inspection is the default completion check. Only for an explicitly requested user sign-off, record their actual verdict against the PPTX and PNG bytes:
python scripts/release_bundle.py record-design-acceptance --manifest <release-dir>/release_manifest.json ` --status PASS --statement <user-verdict> python scripts/release_bundle.py finalize ` --manifest <release-dir>/release_manifest.json --status-json <release-dir>/evidence/gates.jsonUse
REJECTEDwhen that is the verdict; never infer approval. Any later PPTX or PNG change makes the receiptSTALE. The manifest records hashes, acceptance layers, visual scope, andCOMPLETE,PARTIAL_ACCEPTANCE, orINCOMPLETE. The helper never starts PowerPoint or LibreOffice. -
Deliver automatically to the designated formal location. After the required gates pass, copy the selected PPTX (and PDF only when requested) to non-existing, clearly versioned filenames. Recheck hashes and opening from those paths, then link them. Keep temporary bundle materials in the task process directory until explicit cleanup. Before cleanup, place any necessary editable source and dependencies in their formal location, update references and verify the deck; do not retain the whole bundle solely for possible future use. Take the after snapshot before this intended publication; do not classify the new deliverables as unrelated changes.
When filling in a template:
- If you script an XML transform, parse with
defusedxml.minidom— round-tripping OOXML throughxml.etree.ElementTreerewrites namespace prefixes and corrupts the deck. - Template slots ≠ source items. If the template shows 4 team members and you have 3, delete the 4th member's entire group (image + text boxes), not just its text — then check for orphaned visuals in QA.
- One
<a:p>per list item — never concatenate items into a single paragraph. Copy the sibling<a:pPr>to preserve spacing, and putb="1"on the<a:rPr>of titles, section headers, and inline labels (Status:,Owner:). - Let bullets inherit from the layout; only add
<a:buChar>,<a:buAutoNum>(numbered), or<a:buNone>to override — never a literal•in the text. - Text with leading or trailing spaces needs
xml:space="preserve"on its<a:t>.
When copying slides between decks:
- Audit both files first:
python scripts/slide_size_audit.py source.pptx --jsonand the same command for the target. When reusing a real template, runpython scripts/slide_size_audit.py target.pptx --reference source.pptx; the physical EMU width and height must match exactly. - If the target is not that exact size, set PowerPoint's Design → Slide Size → Widescreen (16:9) before copying. The label
16:9alone is insufficient because10" × 5.625"and13.333" × 7.5"have the same ratio but different coordinates. Re-run the audit after changing the target and choose Ensure Fit when PowerPoint asks how to scale existing content.
Design Ideas
Don't create boring slides. Plain bullets on a white background won't impress anyone. Consider ideas from this list for each slide.
Before Starting
- Pick a bold, content-informed color palette: The palette should feel designed for THIS topic. If swapping your colors into a completely different presentation would still "work," you haven't made specific enough choices.
- Dominance over equality: One color should dominate (60-70% visual weight), with 1-2 supporting tones and one sharp accent. Never give all colors equal weight.
- Dark/light contrast: Dark backgrounds for title + conclusion slides, light for content ("sandwich" structure). Or commit to dark throughout for a premium feel.
- Commit to a visual motif: Pick ONE distinctive element and repeat it — rounded image frames, icons in colored circles. Carry it across every slide. Do not use a color bar or accent stripe as your motif (see Avoid list).
Color Palettes
Choose colors that match your topic — don't default to generic blue. Use these palettes as inspiration:
| Theme | Primary | Secondary | Accent |
|---|---|---|---|
| Midnight Executive | 1E2761 (navy) |
CADCFC (ice blue) |
FFFFFF (white) |
| Forest & Moss | 2C5F2D (forest) |
97BC62 (moss) |
F5F5F5 (cream) |
| Coral Energy | F96167 (coral) |
F9E795 (gold) |
2F3C7E (navy) |
| Warm Terracotta | B85042 (terracotta) |
E7E8D1 (sand) |
A7BEAE (sage) |
| Ocean Gradient | 065A82 (deep blue) |
1C7293 (teal) |
21295C (midnight) |
| Charcoal Minimal | 36454F (charcoal) |
F2F2F2 (off-white) |
212121 (black) |
| Teal Trust | 028090 (teal) |
00A896 (seafoam) |
02C39A (mint) |
| Berry & Cream | 6D2E46 (berry) |
A26769 (dusty rose) |
ECE2D0 (cream) |
| Sage Calm | 84B59F (sage) |
69A297 (eucalyptus) |
50808E (slate) |
| Cherry Bold | 990011 (cherry) |
FCF6F5 (off-white) |
2F3C7E (navy) |
For Each Slide
Every slide needs a visual element — image, chart, icon, or shape. Text-only slides are forgettable.
Layout options:
- Two-column (text left, illustration on right)
- Icon + text rows (icon in colored circle, bold header, description below)
- 2x2 or 2x3 grid (image on one side, grid of content blocks on other)
- Half-bleed image (full left or right side) with content overlay
Data display:
- Large stat callouts (big numbers 60-72pt with small labels below)
- Comparison columns (before/after, pros/cons, side-by-side options)
- Timeline or process flow (numbered steps, arrows)
Visual polish:
- Icons in small colored circles next to section headers
- Italic accent text for key stats or taglines
Typography
Check typography in the actual PowerPoint export. Fonts missing from the rendering machine may be substituted, so verify the chosen fonts and the rendered text fit. If an optional LibreOffice compatibility check is requested, its substitutions can have different widths: report those differences separately from PowerPoint results. The substitution cautions below concern that compatibility route; they do not require running it or treating an installed font's native preview as approximate.
- Use native PowerPoint font-size stops for every newly authored or model-resized text run. Choose a point size exposed by PowerPoint's standard font-size control and reachable through its built-in Increase/Decrease Font Size commands (for example,
14 pt, not a conversion residue such as13.9 pt). If an existing user-supplied template intentionally uses a nonstandard size, preserve it unless the user asks to normalize it; do normalize fractional residue introduced by our own authoring, scaling, import, or export path. - Safe fonts (render true-to-width in QA and ship with Office): Arial, Calibri, Cambria, Times New Roman, Courier New, Bookman Old Style, Century Schoolbook. Use these for body text and anything where fit matters.
- Headers with personality at zero QA risk: pair a safe-list serif header (Cambria, Bookman Old Style, Century Schoolbook) with a safe-list sans body (Calibri or Arial). You get visual contrast without giving up reliable overflow checks.
- If the user asks for a font outside the safe list (e.g. Georgia or Trebuchet MS): use it where the user asked, but size those containers with extra slack (~10%) and don't trust QA text-fit on those elements — the preview of that font is approximate. If the user hasn't specified, prefer safe-list fonts for body text.
- QA-unreliable fonts (substitute has different widths — overflow checks can be wrong): Georgia, Trebuchet MS, Impact, Arial Black, Garamond, Consolas, Palatino Linotype. Calibri Light substitution varies by environment; treat as QA-unreliable. Fine for titles/accents with slack; don't trust QA text-fit on these.
- Never default to Aptos — Office's post-2023 default has no metric-compatible substitute here and is missing from older Office installs, so it's unreliable on both ends.
| Element | Size |
|---|---|
| Slide title | 36-44pt bold |
| Section header | 20-24pt bold |
| Other non-table visible text | 12pt minimum |
| Table cells and chart data tables, except sources/links | 12pt minimum |
| Sources, links, and footnotes | 12pt minimum, muted |
These are delivery gates, not suggestions. Table cells and native chart data tables use the 12pt minimum. Charts, axes, legends, timelines, Gantt labels, and other reader-facing content also use the 12pt minimum. Twelve points is a floor for collision avoidance, not the default: use larger text when the layout has room, and reduce content or reflow it before accepting overlaps. Titles and section headers retain their larger gates.
Give generated title, section-header, source, and footnote shapes meaningful
objectName values (Title, SectionHeader, Source, Citation, or Footnote) when
the creation library supports it. This makes the typography audit deterministic.
Spacing
- 0.5" minimum margins
- 0.3-0.5" between content blocks
- Leave breathing room—don't fill every inch
Avoid (Common Mistakes)
- Don't repeat the same layout — vary columns, cards, and callouts across slides
- Don't center body text — left-align paragraphs and lists; center only titles
- Don't skimp on size contrast — titles need 36pt+ to stand out from body text
- Don't default to blue — pick colors that reflect the specific topic
- Don't mix spacing randomly — choose 0.3" or 0.5" gaps and use consistently
- Don't style one slide and leave the rest plain — commit fully or keep it simple throughout
- Don't create text-only slides — add images, icons, charts, or visual elements; avoid plain title + bullets
- Don't forget text box padding — when aligning lines or shapes with text edges, set
margin: 0on the text box or offset the shape to account for padding - Don't use low-contrast elements — icons AND text need strong contrast against the background; avoid light text on light backgrounds or dark text on dark backgrounds
- NEVER use accent lines under titles — these are a hallmark of AI-generated slides; use whitespace or background color instead
- NEVER add decorative color bars or accent stripes — this includes: header/footer bars spanning the slide width, vertical sidebar stripes down one edge of the slide, thin accent stripes along one edge of a card or content block, and "single-side borders" on rectangles. These read as AI-generated filler. If you want to set a card apart, use a subtle background tint, a drop shadow, or an icon — not an edge stripe.
- Don't default to cream/beige backgrounds — when no background is specified, use white (
FFFFFF) or the user's brand palette; avoid warm-neutral defaults likeF5F5DC,FAF0E6,FAEBD7,FFF8E1 - Don't ship text that overflows its shape — if text doesn't fit, simplify, split across slides, or enlarge the container; never reduce visible text below the typography gates or leave content cut off or spilling past bounds
QA (Required)
Your first render usually has a few real issues — overlaps, overflow, misalignment. Find and fix those, re-render only the slides you changed, and stop.
Content QA
markitdown output.pptx
Check for missing content, typos, wrong order.
When using templates, check for leftover placeholder text:
markitdown output.pptx | grep -iE "\bx{3,}\b|lorem|ipsum|\bTODO|\[insert|this.*(page|slide).*layout"
If grep returns results, fix them before declaring success.
File QA (required)
$preflight = Join-Path "<skill-root>" "scripts/python_runtime_preflight.py"
$report = Join-Path "<qa-evidence-dir>" "python-runtime-preflight.json"
& "<current-python>" $preflight --json-out $report
if ($LASTEXITCODE -ne 0) { throw "No verified Python runtime for PPTX static QA" }
$qa_python = (Get-Content -LiteralPath $report -Raw -Encoding UTF8 | ConvertFrom-Json).selected_executable
if ([string]::IsNullOrWhiteSpace($qa_python)) { throw "Preflight did not select an interpreter" }
& $qa_python scripts/office/validate.py output.pptx # built from scratch
& $qa_python scripts/office/validate.py output.pptx --original src.pptx # built from a template
& $qa_python scripts/typography_audit.py output.pptx
& $qa_python scripts/slide_size_audit.py output.pptx --expected wide16x9
The preflight command is mandatory even when the invoking Python is expected to be complete:
record its JSON before recording STATIC_PASS. If the current runtime is missing a dependency,
the only permitted recovery is to rerun the validators with a candidate that the preflight itself
verified, such as py -3 or an installed system Python. Do not run pip install and do not edit
runtime directories.
If the deck came from a template, always pass --original. A template may itself
contain parts the XSD rejects, so a bare run can report failures you never caused — and
a genuine regression can hide among them. --original baselines
the schema and slide checks against the template, suppressing errors it already had.
The structural checks — relationships, content types, charts — ignore --original and
report template-inherited problems either way, so read those on their own merits.
Per-slide p:cNvPr@id uniqueness is also structural and is never suppressed by
--original; PowerPoint requires those object IDs to be unique within each slide.
The typography audit is read-only and checks the entire output deck, including text in
tables, grouped shapes, and native chart text. It fails when any non-title visible text
is below 12pt, automatic shrink-to-fit reduces the effective size below its gate, or the
effective size cannot be resolved. spAutoFit may expand a shape but does not waive an
unresolved base font size. Fix the layout and rerun the audit; the script does not
mechanically enlarge text.
The minimum-size audit does not by itself prove that model-authored text uses native
PowerPoint font-size stops. For every newly authored or resized text run, inspect the
exported run-level OOXML and confirm that a:rPr@sz / a:defRPr@sz equals the exact
intended hundredths of a point (14 pt -> 1400), with no conversion residue such as
1390. When an authoring API uses CSS pixels, start from the intended standard point
size and use px = pt * 96 / 72; do not round the pixel value first. Imported rich text
can keep explicit run sizes even after a whole-text setter, so update and verify every
affected run or range rather than trusting the text-box default.
For a user-authorized exception to the 12pt visible-text gate, pass
--exceptions exceptions.json. Each entry must identify one slide and one shape and
include the user's reason; blanket slide/deck exceptions are rejected, and 12pt remains
the absolute floor. Do not create or use an exception file without explicit user
authorization. Use --json when a machine-readable audit record is useful.
pptxgenjs emits chart XML PowerPoint refuses to open, and every other tool accepts: python-pptx opens those decks, LibreOffice renders them, the XSD passes them. Every failure names its fix. Fix it in the generator and rebuild.
Visual QA
Convert the slides to images (see Converting to Images) and inspect every one as a complete 16:9 page. Text that becomes readable only after cropping or zooming in does not pass. After staring at the generating code you tend to see what you expect rather than what rendered, so look at the images fresh (a subagent works well for this if you have one). User-visible defects to look for:
- Text overflow or text cut off at a box or slide boundary — check this first. It is the most common defect and always user-visible. For optional compatibility previews with font substitution, inspect the native PowerPoint page as well; extra container slack does not prove correct rendering.
- Overlapping elements (text through shapes, lines through words, stacked elements)
- Source citations or footers colliding with content above
- Elements too close (< 0.3" gaps) or cards/sections nearly touching
- Uneven gaps (large empty area in one place, cramped in another)
- Insufficient margin from slide edges (< 0.5")
- Columns or similar elements not aligned consistently
- Low-contrast text (e.g., light gray text on cream-colored background)
- Template decoration mispositioned after text replacement — e.g., a title underline positioned for one line, but the replaced title wrapped to two
- Low-contrast icons (e.g., dark icons on dark backgrounds without a contrasting circle)
- Text boxes too narrow causing excessive wrapping
- Leftover placeholder content
If the user manually edits a temporary deck after an audit, treat the edited file as a new output: rerun content QA, file validation, typography audit, rendering, and full-page visual inspection on that latest version. If the edited file is a formal release, create a new release-bundle version first and follow the revision workflow above; never overwrite the prior formal artifacts.
Converting to Images
Convert presentations to individual slide images for visual inspection:
Do not use OfficeCLI as a visual export path. Its HTML/SVG output is available only as an explicit
non-fidelity diagnostic preview (--render html --non-fidelity-preview for screenshots) and cannot
serve as final images or visual QA. Run office_native_gate.py check <deck.pptx> --format pptx --json --allow-office-com --require-render --render-output-dir <new-task-owned-png-directory> under the
ownership and source-protection checks above. The gate
owns the isolated PowerPoint export and records NATIVE_RENDER_PASS. Inspect every actual exported
PNG at full-page size; successful export alone is not visual inspection. The optional output directory
must not already exist and is valid only with --require-render; the receipt lists the retained images.
Omitting it keeps the existing transient-export behavior. Reuse the retained PNGs for previews
and contact sheets, verifying that they correspond to the current PPTX, rather than rerendering via
LibreOffice. After a file changes, export the current version again.
Only when LibreOffice compatibility or a specific rendering difference is being checked, use the existing protected adapter and inspect its output separately:
python scripts/office/soffice.py --headless --convert-to pdf output.pptx
rm -f slide-*.jpg
pdftoppm -jpeg -r 150 output.pdf slide
ls -1 "$PWD"/slide-*.jpg
Pass the absolute image paths directly to the view tool. In the optional commands above, use a
dedicated task-owned output directory; rm clears only its stale images. pdftoppm zero-pads based
on page count: slide-1.jpg for decks under 10 pages, slide-01.jpg for 10-99, slide-001.jpg for 100+.
After fixes, repeat that optional conversion only when it remains required. Its PDF must come from
the current PPTX before pdftoppm can reflect the changes. It does not replace native acceptance.
Dependencies
Use dependencies for the selected work: pptxgenjs (npm, install only if the chosen route needs it
and require('pptxgenjs') fails); markitdown[pptx], Pillow, defusedxml, lxml, python-pptx
(the runtime preflight checks validation imports). Native acceptance needs Microsoft PowerPoint and
the protected gate's dependencies. LibreOffice is conditional for thumbnail.py, legacy conversion,
and requested compatibility rendering through scripts/office/soffice.py; Poppler is needed when
rasterizing PDFs with pdftoppm. Do not probe or install LibreOffice for an ordinary native-only check.
当前用户请求已覆盖本次 PowerPoint 原生检查、且现有守护程序能证明隔离时,可传入 --allow-office-com 并检查实际输出,不另设用户逐页签字。按本技能的原生 gate 要求核对进程归属和源文件,只清理本任务的空实例。工具身份校验失败时停用该工具,选择可信且可满足目标的现有文件级或渲染路径;如实说明未验证项。