pptx-structural-asserts

star 2

Programmatic python-pptx structural assertions for deck QA. Runs fifteen checks: text-frame overflow, contrast violations (luminance ratio), alignment-grid drift, repeated-layout-hash, title-underline spam, dark/light run length, missing speaker notes, duplicate titles, body-word density, font render-presence (portability), safe-area breathing-room, plus three archetype-spec checks (waterfall zero-baseline algebra, decision_options column-width sum, risk_heatmap WCAG-AA contrast). Single entry point: check_pptx.py emits a merged JSON report consumed by @deck-critic. Keywords: structural QA, contrast, overflow, alignment grid, layout hash, archetype asserts, font portability, safe area, python-pptx assertions.

srulyt By srulyt schedule Updated 6/8/2026

name: pptx-structural-asserts description: "Programmatic python-pptx structural assertions for deck QA. Runs fifteen checks: text-frame overflow, contrast violations (luminance ratio), alignment-grid drift, repeated-layout-hash, title-underline spam, dark/light run length, missing speaker notes, duplicate titles, body-word density, font render-presence (portability), safe-area breathing-room, plus three archetype-spec checks (waterfall zero-baseline algebra, decision_options column-width sum, risk_heatmap WCAG-AA contrast). Single entry point: check_pptx.py emits a merged JSON report consumed by @deck-critic. Keywords: structural QA, contrast, overflow, alignment grid, layout hash, archetype asserts, font portability, safe area, python-pptx assertions."

PPTX Structural Asserts

Deterministic, fast, no-render-required structural QA. Loads the deck via python-pptx and runs fifteen checks against shape geometry, text frames, runs, notes, and (when a --spec is supplied) deck-spec archetype invariants. Output is a single JSON report consumed by @deck-critic.

Pipeline / How to Invoke

check_pptx.py is the only entry point the production QA pipeline (@deck-critic Step 2) calls. When invoked with --spec it imports check_archetypes.run() from this same skill and merges the archetype findings into the same report under archetype_violations. Archetype fail statuses surface as blocking findings (tagged archetype.<check-id>); warn statuses surface as warning findings. Calling check_archetypes.py directly is still supported for local debugging but is NOT part of the production pipeline (no separate report file).

When to Use This Skill

Load this skill when you are @deck-critic and need:

  • A deterministic verdict on the deck's structure (independent of any render engine being installed)
  • Pass/fail signals for: aspect-ratio, overflow, contrast, alignment, layout repetition, underline spam, dark/light rhythm, speaker notes, duplicate titles

Files in This Skill

File Purpose
SKILL.md This file
scripts/check_pptx.py The check runner — loads pptx + deck-spec, emits JSON report
scripts/check_archetypes.py Cheap spec-level invariants for the 7 archetype recipes added in session 2026-05-04-c8d3b2a1 (waterfall zero-baseline algebra, decision_options column-width sum, risk_heatmap WCAG-AA contrast). Operates on deck-spec.json only — no .pptx parsing required. Imported and run by check_pptx.py when --spec is supplied (wired in fix iteration 1 of the same session).

Quick Use

python .github/skills/pptx-structural-asserts/scripts/check_pptx.py \
  --pptx <session>/agents/deck-builder/output.pptx \
  --spec <session>/agents/deck-builder/deck-spec.json \
  --out  <session>/agents/deck-critic/structural-report.json

Exit codes: 0 on script success (regardless of pass/fail of checks); 1 on script error (file not found, malformed pptx, etc.).

Checks (Severities)

Check Severity Detection
aspect_ratio_pass blocking prs.slide_width / prs.slide_height ≈ 16:9 (±0.5%)
overflow_violations blocking Body text-frame text-length × est-line-height > frame height
contrast_violations blocking WCAG luminance ratio of run color vs slide bg <4.5:1
alignment_violations warn Shape left/top not on 0.05" snap grid
repeated_layout_hash blocking Hash of (sorted shape positions, text-frame counts) repeats on consecutive slides
title_underline_count blocking if >2 Count of slides with a thin (≤0.05" tall) rectangle directly below title
max_same_bg_run blocking if ≥3 Longest run of consecutive slides with same fill color
speaker_notes_missing blocking Slides with empty notes_text_frame.text
duplicate_titles blocking Slides whose first text-frame matches another's
body_word_max_violations warn Slides with body text >30 words total (F11 — Reynolds)
font_not_render_present warn Run fonts not in the design system's render_safe allowlist or declared *_fallback (C1 — font portability). Requires --spec with design_system_tokens.fonts.
safe_area_violations warn Content text shapes intruding into the slide's grid.safe_area_inches margin ("breathing room"; full-bleed/background shapes exempt) (C4). Requires --spec.
archetype.waterfall.zero_baseline blocking Spec-level: waterfall start + Σdeltas == end (±0.5%). Requires --spec.
archetype.decision_options.columns_sum_to_slide_width warn Spec-level: derived option-column widths sum to slide-content width within 0.05". Requires --spec.
archetype.risk_heatmap.contrast_aa blocking Spec-level: WHITE labels on green/amber/red heatmap cells clear WCAG AA normal-text 4.5:1. Requires --spec.

Report Schema

structural-report.json produced by check_pptx.py:

{
  "session_id": null,
  "pptx_path": "...",
  "spec_path": "...",
  "slide_count": 12,
  "aspect_ratio": 1.7777,
  "aspect_ratio_pass": true,
  "overflow_violations": [{"slide": 7, "estimated_overflow_em": 1.4}],
  "contrast_violations": [{"slide": 4, "fg": "#A0A4B0", "bg": "#F4F5F7", "ratio": 2.1}],
  "alignment_violations": [{"slide": 3, "shape": "Body", "off_snap_axis": "left", "off_by_emu": 4571}],
  "repeated_layout_hash": [{"slides": [4, 5], "hash": "h:abc123"}],
  "title_underline_count": 1,
  "underline_slides": [2],
  "max_same_bg_run": 2,
  "background_sequence": ["dark", "light", "light", "dark", "light", "..."],
  "speaker_notes_missing": [],
  "duplicate_titles": [],
  "body_word_max_violations": [],
  "fonts_used": ["Calibri", "Calibri Light"],
  "font_not_render_present": [],
  "render_safe_fonts_known": ["Carlito", "DejaVu Sans", "..."],
  "safe_area_violations": [{"slide": 6, "shape": "Body", "edges": ["left"], "safe_area_emu": 457200}],
  "archetype_violations": [
    {"id": "waterfall.zero_baseline", "slide_index": 5,
     "status": "fail",
     "message": "waterfall algebra broken: start(100) + Σdeltas(+8) = 108, but end=120 (tol=0.6)..."}
  ],
  "archetype_runner_available": true,
  "blocking_findings": ["archetype.waterfall.zero_baseline"],
  "warning_findings": []
}

Implementation Notes

  • The script imports python-pptx. If unavailable, it auto-installs via pip install python-pptx (matching the deck-builder's behavior) before failing.
  • Color comparisons traverse run → font → color → rgb. When color is inherited from the master/layout, the script falls back to the slide background and assumes default theme (note: this is a known false- negative; recorded in report.notes).
  • Alignment snap is 0.05" = 45720 EMU (914400 EMU per inch × 0.05).
  • Layout-hash combines: shape count, sorted (left, top, width, height) tuples rounded to 0.1", and text-frame count per slide.
  • Background detection: read slide background fill if solid; else derive from the largest filled rectangle covering ≥90% of slide area (heuristic for blank-layout decks where background is implemented via an underlying full-bleed shape).

Why Both Visual + Structural?

Visual QA catches "feels wrong" issues (focal point, density, decorative shapes). Structural QA catches "is wrong" issues (overflow, contrast, missing notes). Either alone is insufficient:

  • Visual-only fails when render engine is missing.
  • Structural-only misses purely visual antipatterns (5, 6).

@deck-critic runs both passes and combines findings.

Rendering Subsystem Rebuild (2026-05-04)

scripts/check_pptx.py was rewritten in session 2026-05-04-7d3f9a2b. New / changed report fields:

  • overflow_violations (F1) — now uses real Pillow text metrics per shape, with no slack tolerance, honouring margin_left/right/top/bottom, space_before/after, and paragraph level indentation. The font is resolved via the canonical render-visual/assets/font_locator.find_dejavu_sans() helper (C3).
  • contrast_violations + contrast_unresolved (F4) — contrast pairs are now theme-resolved (background fill walks shape ↔ slide-background ↔ theme; text resolves through a:solidFill then theme schemeClr). Pairs that cannot resolve to a concrete RGB land in contrast_unresolved (separate from contrast_violations). The deck-critic treats contrast_unresolved >= 5 as a BLOCKING revise.
  • g_label_threshold (F7) — saturation-aware. Hard-coded thresholds: sat > 0.45 → accent regardless of luminance; lum < 0.18 → dark; lum > 0.72 → light; else accent (C4). The previous luminance-only heuristic mis-classified saturated brand backgrounds as light / dark.
  • dark_light_run (F8) — thresholds: >= 3 warn; >= 7 BLOCKING; >= 5 with no accent break BLOCKING.
  • ody_word_max (F11) — canonical limit lowered from 70 to 30 words per body. Flagged in body_density_violations.
  • ** ont_fallback** — when the canonical DejaVu Sans bundle is unavailable and PIL bitmap-default is used as a last resort, this field surfaces so the critic can warn that overflow metrics are approximate.
Install via CLI
npx skills add https://github.com/srulyt/srulys-agent-packs --skill pptx-structural-asserts
Repository Details
star Stars 2
call_split Forks 2
navigation Branch main
article Path SKILL.md
More from Creator