Files
acdl/tests/test_slides_pipeline.py
T
Jon Chery 301aa2c8d8
Nova Slides Render / render (push) Failing after 58s
docs(P3): marp deck + talking points + README + theme CSS + tests (REQ-245,251,252)
Marp deck (nova-autonomous-cloud-delivery-marp.md): synthesize from updated
source-of-truth; 18 main + 1 appendix slides; frontmatter — title 'Nova —
The Autonomous Cloud Delivery Platform', footer without version + without
'Act %{page}/5', title-slide subtitle 'Product Development & Citizen
Developer Overview'; no badges; embedded PNGs.

Talking points (nova-autonomous-cloud-delivery-talking-points.md):
re-distilled to 18-slide + A1 structure.

README.md: update deck title, audience, slide count (18 main + 1 appendix),
directory layout, remove badge docs, update deck table + render commands +
filenames. Document the v1.21 rename + restructure.

Theme CSS (nova-sp-theme.css): fix Appendix A1 table readability — tables
now have explicit white body + black text on any slide background
(including dark/title slides). Item 32.

Tests (test_slides_pipeline.py): add v1.21 assertions — no badges; no
version in footer/title slide; 18 main + 1 appendix slides; no D-###/REQ-
###/.py paths in audience-facing Marp deck or source slide body; old deck
files removed; render script default renamed; README references new deck
name. Update deck path in test_regression_cap023_024.py +
core/regression_verify.py CAP-024 (filename + 18-19 slide range, drop 'Arc
Preview' check per item 3).

attach_release_asset.py: usage example filename updated.

---ci---
project: acdl
phase: 3
milestone: v1.21
status: execute
phase_role: execution
---/ci---
2026-08-11 14:03:06 +00:00

268 lines
11 KiB
Python

"""REQ-239..243 (v1.20) + REQ-245,251,252 (v1.21): S&P theme + slide render
pipeline + deck-refinement tests.
v1.20 validates:
- The Marp deck frontmatter references nova-sp-theme.css
- The CSS file contains the S&P colors (#D6002A, #1B1B1B)
- The mermaid theme JSON contains the S&P colors
- Every .mmd has a corresponding .png
- The render_slides.sh script exists and is executable
- The CI workflow file exists
v1.21 adds (REQ-245,251,252):
- Deck renamed to nova-autonomous-cloud-delivery*
- No maturity badges in the Marp deck
- No version in the Marp footer/title slide
- 18 main + 1 appendix slides
- No D-###/REQ-###/internal .py paths in audience-facing slides
- Title is "Nova — The Autonomous Cloud Delivery Platform"
"""
import re
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parent.parent
PRESENTATIONS = ROOT / "docs" / "presentations"
ASSETS = PRESENTATIONS / "assets"
THEME_CSS = ASSETS / "nova-sp-theme.css"
THEME_JSON = ASSETS / "mmd" / "sp-theme.json"
MARP_DECK = PRESENTATIONS / "nova-autonomous-cloud-delivery-marp.md"
SOURCE_MD = PRESENTATIONS / "nova-autonomous-cloud-delivery.md"
RENDER_SCRIPT = ROOT / "scripts" / "render_slides.sh"
SLIDES_WORKFLOW = ROOT / ".github" / "workflows" / "slides.yml"
def test_sp_theme_css_exists():
"""REQ-239: nova-sp-theme.css exists."""
assert THEME_CSS.is_file(), f"theme CSS not found: {THEME_CSS}"
def test_sp_theme_css_has_snp_colors():
"""REQ-239: CSS contains S&P Red and Black."""
css = THEME_CSS.read_text()
assert "#D6002A" in css, "S&P Red (#D6002A) missing from theme CSS"
assert "#1B1B1B" in css, "S&P Black (#1B1B1B) missing from theme CSS"
def test_sp_theme_json_has_snp_colors():
"""The mermaid theme JSON also has S&P colors."""
json_text = THEME_JSON.read_text()
assert "#D6002A" in json_text, "S&P Red missing from mermaid theme"
assert "#1B1B1B" in json_text, "S&P Black missing from mermaid theme"
def test_marp_deck_uses_sp_theme():
"""REQ-239: Marp deck frontmatter references nova-sp-theme.css."""
text = MARP_DECK.read_text()
# The frontmatter is between the first two ---
fm_match = re.match(r'^---\n(.*?)\n---', text, re.DOTALL)
assert fm_match, "Marp frontmatter not found"
frontmatter = fm_match.group(1)
assert "nova-sp" in frontmatter, \
"Marp deck does not reference nova-sp theme"
def test_marp_deck_not_using_default_theme():
"""The Marp deck must not use 'theme: default'."""
text = MARP_DECK.read_text()
fm_match = re.match(r'^---\n(.*?)\n---', text, re.DOTALL)
assert fm_match, "Marp frontmatter not found"
frontmatter = fm_match.group(1)
assert "theme: default" not in frontmatter, \
"Marp deck still uses 'theme: default' — should use nova-sp-theme.css"
def test_render_slides_script_exists():
"""REQ-240: render_slides.sh exists and is executable."""
assert RENDER_SCRIPT.is_file(), "render_slides.sh not found"
import os
assert os.access(RENDER_SCRIPT, os.X_OK), "render_slides.sh not executable"
def test_render_slides_script_renders_mermaid():
"""REQ-240: render_slides.sh renders mermaid diagrams."""
text = RENDER_SCRIPT.read_text()
assert "mermaid-cli" in text or "mmdc" in text, \
"render_slides.sh does not invoke mermaid-cli"
assert "sp-theme.json" in text, \
"render_slides.sh does not reference sp-theme.json"
def test_render_slides_script_renders_marp():
"""REQ-240: render_slides.sh renders Marp HTML + PPTX."""
text = RENDER_SCRIPT.read_text()
assert "marp-cli" in text, "render_slides.sh does not invoke marp-cli"
assert ".html" in text, "render_slides.sh does not produce HTML"
assert ".pptx" in text, "render_slides.sh does not produce PPTX"
def test_render_slides_default_deck_renamed():
"""REQ-245: render_slides.sh default deck is nova-autonomous-cloud-delivery."""
text = RENDER_SCRIPT.read_text()
assert "nova-autonomous-cloud-delivery" in text, \
"render_slides.sh does not default to nova-autonomous-cloud-delivery"
def test_slides_ci_workflow_exists():
"""REQ-241: CI workflow for slides exists."""
assert SLIDES_WORKFLOW.is_file(), "slides.yml workflow not found"
def test_slides_ci_workflow_triggers_on_presentations():
"""REQ-241: CI workflow triggers on docs/presentations/ changes."""
text = SLIDES_WORKFLOW.read_text()
assert "docs/presentations" in text, \
"slides.yml does not trigger on docs/presentations/"
assert "render_slides.sh" in text, \
"slides.yml does not invoke render_slides.sh"
def test_every_mmd_has_png():
"""REQ-240: every .mmd file has a corresponding .png."""
mmd_dir = ASSETS / "mmd"
png_dir = ASSETS / "png"
if not mmd_dir.is_dir():
pytest.skip("no .mmd directory")
mmd_files = sorted(mmd_dir.glob("*.mmd"))
assert len(mmd_files) > 0, "no .mmd files found"
missing = []
for mmd in mmd_files:
png = png_dir / f"{mmd.stem}.png"
if not png.is_file():
missing.append(mmd.name)
assert not missing, f"PNGs missing for: {missing}"
def test_readme_no_retired_decks():
"""REQ-243: presentations README does not list retired decks."""
readme = (PRESENTATIONS / "README.md").read_text()
assert "how-the-platform-works" not in readme, \
"README still references retired 'how-the-platform-works' deck"
assert "the-developer-experience" not in readme, \
"README still references retired 'the-developer-experience' deck"
def test_readme_no_old_deck_name():
"""REQ-245: README references the new deck name, not the old one."""
readme = (PRESENTATIONS / "README.md").read_text()
assert "nova-autonomous-cloud-delivery" in readme, \
"README does not reference nova-autonomous-cloud-delivery"
def test_old_deck_files_removed():
"""REQ-245: the old nova-no-humans-platform* files are gone."""
old_files = sorted(PRESENTATIONS.glob("nova-no-humans-platform*"))
assert not old_files, f"old deck files still present: {old_files}"
def test_marp_deck_no_badges():
"""REQ-252: no maturity badges in the Marp deck."""
text = MARP_DECK.read_text()
assert "badge" not in text, "Marp deck still contains badge spans"
def test_marp_deck_no_version_in_footer():
"""REQ-251: no version (v1.x) in the Marp frontmatter footer/header."""
text = MARP_DECK.read_text()
fm_match = re.match(r'^---\n(.*?)\n---', text, re.DOTALL)
assert fm_match, "Marp frontmatter not found"
frontmatter = fm_match.group(1)
# No v1.x version string in the footer or header lines
assert not re.search(r"v1\.\d+", frontmatter), \
f"Marp frontmatter still contains a version: {frontmatter}"
# No "Act" pagination artifact
assert "Act %" not in frontmatter, \
"Marp frontmatter still contains 'Act %{page}' artifact"
def test_marp_deck_title_slide_no_version_subtitle():
"""REQ-251: the title slide does not carry a version subtitle."""
text = MARP_DECK.read_text()
# The title slide is the first slide after the frontmatter
# Find the title block (between the frontmatter and the first --- separator)
after_fm = text.split("---\n", 2)[2] if text.startswith("---") else text
first_slide = after_fm.split("\n---\n")[0]
# The old subtitle was "v1.18 — Citizen Developer & Production-Grade Guidance"
assert "v1.18" not in first_slide, \
"Title slide still contains 'v1.18' subtitle"
assert "Citizen Developer & Production-Grade Guidance" not in first_slide, \
"Title slide still contains the old version subtitle"
def test_marp_deck_title_is_autonomous_cloud_delivery():
"""REQ-245: the deck title is 'Nova — The Autonomous Cloud Delivery Platform'."""
text = MARP_DECK.read_text()
assert "Autonomous Cloud Delivery Platform" in text, \
"Deck title is not 'Autonomous Cloud Delivery Platform'"
# The old title should not appear in the audience-facing deck
# (speaker notes are not in the marp deck, so this is safe)
assert "No-Humans Infrastructure Platform" not in text, \
"Deck still carries the old 'No-Humans Infrastructure Platform' title"
def test_marp_deck_slide_count():
"""REQ-245: 18 main slides + 1 appendix = 19 slides total."""
text = MARP_DECK.read_text()
# Count slide separators: each slide ends with --- (except the last)
# The frontmatter is one --- ... --- block, then each slide is separated by ---
# Count "## Slide" and "## Appendix" headings
slide_headings = re.findall(r"^## (?:Slide|Appendix) ", text, re.MULTILINE)
main_slides = re.findall(r"^## Slide ", text, re.MULTILINE)
appendix_slides = re.findall(r"^## Appendix ", text, re.MULTILINE)
assert len(main_slides) == 18, \
f"expected 18 main slides, found {len(main_slides)}: {slide_headings}"
assert len(appendix_slides) == 1, \
f"expected 1 appendix slide, found {len(appendix_slides)}"
def test_marp_deck_no_internal_citations():
"""REQ-252: no D-### decision IDs, REQ-### requirement IDs, or internal
.py file paths in the audience-facing Marp deck."""
text = MARP_DECK.read_text()
# Decision IDs like D-121, D-083
assert not re.search(r"\bD-\d{3}\b", text), \
"Marp deck contains D-### decision IDs"
# Requirement IDs like REQ-245
assert not re.search(r"\bREQ-\d{3}\b", text), \
"Marp deck contains REQ-### requirement IDs"
# Internal python file paths like outbox_writer.py, confidence_signal.py
# (allow .py only inside code blocks for the ROI formula? No — the deck
# should not cite internal file paths at all)
assert not re.search(r"\b(outbox_writer|confidence_signal|hitl_gates|"
r"attestation_matrix|checkov_adapter|infracost_adapter|"
r"contract_resolver|run_platform)\.py\b", text), \
"Marp deck contains internal .py file paths"
def test_source_md_no_internal_citations_in_slides():
"""REQ-252: the source-of-truth markdown keeps internal citations only
in speaker notes, not in the audience-facing slide body. Speaker notes
are blockquoted (> ) — we check non-blockquote lines for D-###/REQ-###."""
text = SOURCE_MD.read_text()
# Split into lines; exclude blockquote lines (speaker notes) and the
# header frontmatter (> ... at the top)
in_note = False
body_lines = []
for line in text.splitlines():
if line.lstrip().startswith(">"):
in_note = True
continue
if in_note and line.strip() == "":
in_note = False
continue
if not in_note:
body_lines.append(line)
body = "\n".join(body_lines)
# Decision IDs and REQ IDs should not appear in the slide body
assert not re.search(r"\bD-\d{3}\b", body), \
"Source markdown slide body contains D-### decision IDs"
assert not re.search(r"\bREQ-\d{3}\b", body), \
"Source markdown slide body contains REQ-### requirement IDs"
def test_source_md_no_badges():
"""REQ-252: no maturity badges in the source-of-truth markdown."""
text = SOURCE_MD.read_text()
assert "badge" not in text.lower(), \
"Source markdown still contains badge spans"