Six-skill presentation system: ingest talks into a rhetoric vault, run interactive clarification, generate a speaker profile, create presentations that match your documented patterns, produce the deck illustrations + thumbnail visual layer, and publish talk pages to a Jekyll shownotes site. Includes a 102-entry Presentation Patterns taxonomy (91 observable, 11 unobservable go-live items) for scoring, brainstorming, and go-live preparation.
86
92%
Does it follow best practices?
Impact
86%
1.24xAverage score across 26 eval scenarios
Advisory
Suggest reviewing before use
"""Tests for generate-qr.py — QR generation (no network calls)."""
import os
import json
from PIL import Image
from pptx import Presentation
from pptx.util import Inches
from conftest import make_deck
def _square_png(tmp_path, name="sq.png"):
"""A two-color (QR-like) PNG: quantizes to 2 colors with ~0 reconstruction
error, so the content-based detector treats it as a QR."""
path = str(tmp_path / name)
im = Image.new("RGB", (50, 50), (255, 255, 255))
px = im.load()
for x in range(50):
for y in range(50):
if (x + y) % 2 == 0:
px[x, y] = (0, 0, 0)
im.save(path)
return path
def _multicolor_png(tmp_path, name="multi.png"):
"""A many-color (photo/diagram-like) PNG: a smooth gradient that does NOT
reduce to two colors, so the detector rejects it even when square."""
path = str(tmp_path / name)
im = Image.new("RGB", (60, 60))
px = im.load()
for x in range(60):
for y in range(60):
px[x, y] = ((x * 4) % 256, (y * 4) % 256, ((x + y) * 2) % 256)
im.save(path)
return path
def _sparse_text_png(tmp_path, name="screenshot.png"):
"""A mostly-white image with sparse dark 'text' — ~2-color but heavily skewed
to the background (like a doc screenshot), so it must NOT be taken for a QR."""
path = str(tmp_path / name)
im = Image.new("RGB", (80, 80), (255, 255, 255))
px = im.load()
for y in range(0, 80, 6):
for x in range(0, 80, 3):
px[x, y] = (0, 0, 0)
im.save(path)
return path
def test_choose_fg_color_dark_bg(generate_qr):
# Dark background → white foreground
fg = generate_qr.choose_fg_color((0, 0, 0))
assert fg == (255, 255, 255)
def test_choose_fg_color_light_bg(generate_qr):
# Light background → black foreground
fg = generate_qr.choose_fg_color((255, 255, 255))
assert fg == (0, 0, 0)
def test_choose_fg_color_none(generate_qr):
# No background detected → default to black
fg = generate_qr.choose_fg_color(None)
assert fg == (0, 0, 0)
def test_choose_fg_color_mid_gray(generate_qr):
# Mid-gray — should pick based on luminance
fg = generate_qr.choose_fg_color((128, 128, 128))
# Luminance for (128,128,128) ≈ 0.502 → ≥ 0.5 → black
assert fg == (0, 0, 0)
def test_choose_fg_color_dark_blue(generate_qr):
# Dark blue → white foreground
fg = generate_qr.choose_fg_color((0, 0, 100))
assert fg == (255, 255, 255)
def test_generate_qr_png(generate_qr, tmp_path):
out = str(tmp_path / "test.png")
generate_qr.generate_qr_png("https://example.com", (0, 0, 0), (255, 255, 255), out)
assert os.path.isfile(out)
assert os.path.getsize(out) > 100 # sanity check — not empty
def test_generate_qr_png_custom_colors(generate_qr, tmp_path):
out = str(tmp_path / "custom.png")
generate_qr.generate_qr_png("https://example.com", (255, 255, 255), (128, 0, 128), out)
assert os.path.isfile(out)
def test_tracking_db_crud_insert(generate_qr):
db = {}
entry = {
"talk_slug": "test-talk",
"target_url": "https://example.com/notes",
"shortener": "none",
"short_url": "https://example.com/notes",
}
generate_qr.update_tracking_db(db, entry, "test-talk-qr.png")
assert len(db["qr_codes"]) == 1
assert db["qr_codes"][0]["talk_slug"] == "test-talk"
assert db["qr_codes"][0]["qr_png_rel_path"] == "test-talk-qr.png"
def test_tracking_db_crud_update(generate_qr):
db = {"qr_codes": [{
"talk_slug": "test-talk",
"target_url": "https://old-url.com",
"shortener": "none",
"short_url": "https://old-url.com",
"qr_png_rel_path": "old.png",
"created_at": "2024-01-01",
"updated_at": "2024-01-01",
}]}
entry = {
"talk_slug": "test-talk",
"target_url": "https://new-url.com",
"shortener": "none",
"short_url": "https://new-url.com",
}
generate_qr.update_tracking_db(db, entry, "new.png")
assert len(db["qr_codes"]) == 1
assert db["qr_codes"][0]["target_url"] == "https://new-url.com"
assert db["qr_codes"][0]["qr_png_rel_path"] == "new.png"
# created_at preserved from original
assert db["qr_codes"][0]["created_at"] == "2024-01-01"
def test_resolve_slide_bg_rgb_none_for_plain_deck(generate_qr, tmp_path):
"""A plain deck without explicit background returns None."""
prs = make_deck(1)
path = str(tmp_path / "deck.pptx")
prs.save(path)
prs2 = Presentation(path)
result = generate_qr.resolve_slide_bg_rgb(prs2.slides[0])
# May be None or a default — both are acceptable
assert result is None or isinstance(result, tuple)
def test_slide_has_existing_qr_detects_square_qr_sized_picture(generate_qr, tmp_path):
prs = make_deck(1)
prs.slides[0].shapes.add_picture(_square_png(tmp_path), Inches(8), Inches(5), Inches(2.0), Inches(2.0))
assert generate_qr.slide_has_existing_qr(prs.slides[0]) is True
def test_slide_has_existing_qr_ignores_non_square_picture(generate_qr, tmp_path):
prs = make_deck(1)
prs.slides[0].shapes.add_picture(_square_png(tmp_path), Inches(1), Inches(1), Inches(6.0), Inches(2.0))
assert generate_qr.slide_has_existing_qr(prs.slides[0]) is False
def test_slide_has_existing_qr_detects_large_square_qr(generate_qr, tmp_path):
# Size-independent: a 2.8in two-color square is still a QR (the inherited
# shownotes QR in the #56 repro deck was 2.78in — outside the old 1.5-2.5 band).
prs = make_deck(1)
prs.slides[0].shapes.add_picture(_square_png(tmp_path), Inches(1), Inches(1), Inches(2.8), Inches(2.8))
assert generate_qr.slide_has_existing_qr(prs.slides[0]) is True
def test_slide_has_existing_qr_ignores_multicolor_square(generate_qr, tmp_path):
# Square and in size range, but many colors (e.g. a Venn diagram) → not a QR.
prs = make_deck(1)
prs.slides[0].shapes.add_picture(_multicolor_png(tmp_path), Inches(2), Inches(2), Inches(2.0), Inches(2.0))
assert generate_qr.slide_has_existing_qr(prs.slides[0]) is False
def test_slide_has_existing_qr_ignores_small_two_color_icon(generate_qr, tmp_path):
# Two-color but below the 1.5in floor → a small icon, not a QR.
prs = make_deck(1)
prs.slides[0].shapes.add_picture(_square_png(tmp_path), Inches(1), Inches(1), Inches(1.0), Inches(1.0))
assert generate_qr.slide_has_existing_qr(prs.slides[0]) is False
def test_slide_has_existing_qr_ignores_text_screenshot(generate_qr, tmp_path):
# Near-square, in size range, ~2-color — but mostly-white with sparse text
# (unbalanced) → not a QR. Regression: a martinfowler.com screenshot (slide 28
# of the #56 repro deck) that the recon-error-only test wrongly accepted.
prs = make_deck(1)
prs.slides[0].shapes.add_picture(_sparse_text_png(tmp_path), Inches(2), Inches(1), Inches(4.0), Inches(4.0))
assert generate_qr.slide_has_existing_qr(prs.slides[0]) is False
def test_slide_has_existing_qr_false_for_plain_slide(generate_qr):
prs = make_deck(1)
assert generate_qr.slide_has_existing_qr(prs.slides[0]) is False
def test_two_color_metrics_separate_qr_screenshot_photo(generate_qr, tmp_path):
qr_err, qr_min = generate_qr._two_color_metrics(open(_square_png(tmp_path, "q.png"), "rb").read())
multi_err, _ = generate_qr._two_color_metrics(open(_multicolor_png(tmp_path, "m.png"), "rb").read())
_, sshot_min = generate_qr._two_color_metrics(open(_sparse_text_png(tmp_path, "s.png"), "rb").read())
assert qr_err < 5.0 and qr_min >= 0.25 # QR: two-color AND balanced
assert multi_err > 20.0 # photo/diagram: many colors
assert sshot_min < 0.25 # screenshot: mostly background
def test_find_qr_rects_returns_points_geometry(generate_qr, tmp_path):
prs = make_deck(1)
# placed at 8in,5in, 2in square → points: 576, 360, 144, 144
prs.slides[0].shapes.add_picture(_square_png(tmp_path), Inches(8), Inches(5), Inches(2.0), Inches(2.0))
rects = generate_qr.find_qr_rects(prs.slides[0])
assert len(rects) == 1
L, T, W, H = rects[0]
assert abs(L - 576) < 0.5 and abs(T - 360) < 0.5
assert abs(W - 144) < 0.5 and abs(H - 144) < 0.5
def test_resolve_target_includes_inherited_qr_slides(generate_qr, tmp_path):
"""A deck adapted from another talk: config targets only the closing slide,
but an earlier slide carries an inherited QR — it must also be targeted."""
prs = make_deck(4)
prs.slides[1].shapes.add_picture(_square_png(tmp_path), Inches(8), Inches(5), Inches(2.0), Inches(2.0))
indices = generate_qr.resolve_target_slide_indices(prs, {"slide_position": "closing"}, "https://example.com/notes")
assert 1 in indices # inherited-QR slide
assert 3 in indices # closing slide
def test_back_half_is_always_slug_ignoring_preferred_short_path(generate_qr, monkeypatch):
"""The back-half is ALWAYS the talk slug — a legacy preferred_short_path is ignored."""
captured = {}
def fake_create_bitly_link(long_url, api_token, custom_back_half=None, domain=None):
captured["back_half"] = custom_back_half
return {"short_url": "https://jbaru.ch/my-slug", "link_id": "id", "short_path": custom_back_half}
monkeypatch.setattr(generate_qr, "create_bitly_link", fake_create_bitly_link)
config = {"shortener": "bitly", "preferred_short_path": "legacy-override", "bitly_domain": "jbaru.ch"}
secrets = {"bitly": {"api_token": "tok"}}
generate_qr.resolve_short_url(
"https://jbaru.ch/my-slug", "my-slug", config, secrets, {}, dry_run=False, vault_path=None
)
assert captured["back_half"] == "my-slug"
def test_insert_qr_via_powerpoint_orchestration(generate_qr, monkeypatch):
"""The PowerPoint-write orchestration is deterministic and unit-tested with
the InsertQR wrapper (the actual VBA) mocked: one insert-qr.sh call per color
variant, the deck threaded through uniquely-named intermediates, intermediates
cleaned up, and the final result moved back onto the deck."""
calls = []
monkeypatch.setattr(generate_qr.subprocess, "run", lambda cmd, **kw: calls.append(cmd))
removed = []
monkeypatch.setattr(generate_qr.os, "remove", lambda p: removed.append(p))
moved = []
monkeypatch.setattr(generate_qr.shutil, "move", lambda a, b: moved.append((a, b)))
monkeypatch.setattr(generate_qr.os.path, "isfile", lambda p: True)
deck = "/decks/talk.pptx"
# job slides carry their existing-QR rects (points): slide 1 replaces in place,
# slide 3 is a new placement; slide 5 likewise new.
jobs = [
("/q/a.png", [(1, [(100.0, 200.0, 144.0, 144.0)]), (3, [])]),
("/q/b.png", [(5, [])]),
]
generate_qr.insert_qr_via_powerpoint(deck, jobs, "/scripts")
wrapper = "/scripts/insert-qr.sh"
# one subprocess call per job; deck threaded through intermediates; spec is
# 1-based, ";"-joined, with per-slide removal rects after ":"
assert calls == [
[wrapper, "/decks/talk.pptx", "/decks/talk.pptx.qrtmp0.pptx", "/q/a.png", "1:100.00,200.00,144.00,144.00;3"],
[wrapper, "/decks/talk.pptx.qrtmp0.pptx", "/decks/talk.pptx.qrtmp1.pptx", "/q/b.png", "5"],
]
# the prior intermediate is cleaned up; the final intermediate is moved onto the deck
assert removed == ["/decks/talk.pptx.qrtmp0.pptx"]
assert moved == [("/decks/talk.pptx.qrtmp1.pptx", "/decks/talk.pptx")]
def test_insert_qr_via_powerpoint_missing_wrapper(generate_qr, monkeypatch):
"""A missing insert-qr.sh fails fast with an actionable error, not a traceback."""
import pytest
monkeypatch.setattr(generate_qr.os.path, "isfile", lambda p: False)
with pytest.raises(SystemExit):
generate_qr.insert_qr_via_powerpoint("/decks/talk.pptx", [("/q/a.png", [(1, [])])], "/scripts")
def test_insert_qr_via_powerpoint_wrapper_failure_is_actionable(generate_qr, monkeypatch):
"""A wrapper (insert-qr.sh) failure surfaces as an actionable SystemExit
pointing at the DeckOps setup, not a raw CalledProcessError traceback."""
import pytest
def boom(cmd, **kw):
raise generate_qr.subprocess.CalledProcessError(1, cmd)
monkeypatch.setattr(generate_qr.subprocess, "run", boom)
monkeypatch.setattr(generate_qr.os.path, "isfile", lambda p: True)
with pytest.raises(SystemExit) as exc:
generate_qr.insert_qr_via_powerpoint("/decks/talk.pptx", [("/q/a.png", [(1, [])])], "/scripts")
assert "deck-editing-setup.md" in str(exc.value)
def test_format_qr_spec_new_placement_and_replace(generate_qr):
# No rects → bare slide number (new bottom-right placement)
assert generate_qr._format_qr_spec([(5, [])]) == "5"
# One rect → "num:L,T,W,H" (replace in place), 2-decimal points
assert generate_qr._format_qr_spec([(12, [(450.0, 80.0, 200.16, 200.16)])]) == \
"12:450.00,80.00,200.16,200.16"
# Multiple slides + a duplicate-QR slide (two rects) → flattened, ";"-joined
assert generate_qr._format_qr_spec([(12, [(1, 2, 3, 4), (5, 6, 7, 8)]), (38, [])]) == \
"12:1.00,2.00,3.00,4.00,5.00,6.00,7.00,8.00;38"
def test_create_bitly_link_raises_when_custom_back_half_fails(generate_qr, monkeypatch):
"""If the custom back-half can't be set, fail rather than return a random hash —
the back-half must always be the slug (rules/qr-generation-rules.md §2)."""
import pytest
def fake_http(url, data=None, headers=None, method="GET"):
if url.endswith("/v4/bitlinks"):
return {"id": "bit.ly/abc123", "link": "https://bit.ly/abc123"}
raise RuntimeError("custom back-half already taken")
monkeypatch.setattr(generate_qr, "_http_request", fake_http)
with pytest.raises(RuntimeError, match="could not set custom back-half"):
generate_qr.create_bitly_link(
"https://example.com/notes", "tok", custom_back_half="my-slug", domain="jbaru.ch"
)
def test_legacy_non_slug_cache_entry_is_recreated_with_slug(generate_qr, monkeypatch):
"""A tracked link with a legacy non-slug back-half is NOT reused/retargeted —
it is recreated with the slug, even when the cached target matches."""
created = {}
def fake_create_bitly_link(long_url, api_token, custom_back_half=None, domain=None):
created["back_half"] = custom_back_half
return {"short_url": f"https://jbaru.ch/{custom_back_half}", "link_id": "new-id", "short_path": custom_back_half}
def boom_update(*a, **k):
raise AssertionError("update_bitly_link must not be called for a legacy non-slug entry")
monkeypatch.setattr(generate_qr, "create_bitly_link", fake_create_bitly_link)
monkeypatch.setattr(generate_qr, "update_bitly_link", boom_update)
tracking_db = {"qr_codes": [{
"talk_slug": "my-slug",
"target_url": "https://jbaru.ch/my-slug", # cached target matches → would reuse without the fix
"shortener": "bitly",
"short_path": "legacy-hash", # NON-slug back-half
"short_url": "https://bit.ly/legacy-hash",
"shortener_link_id": "old-id",
}]}
short_url, meta = generate_qr.resolve_short_url(
"https://jbaru.ch/my-slug", "my-slug",
{"shortener": "bitly", "bitly_domain": "jbaru.ch"},
{"bitly": {"api_token": "tok"}}, tracking_db, dry_run=False, vault_path=None,
)
assert created["back_half"] == "my-slug"
assert meta["short_path"] == "my-slug"
assert meta["shortener_link_id"] == "new-id"
assert short_url == "https://jbaru.ch/my-slug"
def test_missing_custom_domain_decision_stops_before_first_link(generate_qr, monkeypatch):
"""First short link with NO recorded custom-domain decision (key absent) STOPS
so the agent asks the user — it must not silently default to bit.ly."""
import pytest
monkeypatch.setattr(generate_qr, "create_bitly_link",
lambda *a, **k: (_ for _ in ()).throw(AssertionError("must STOP before creating")))
with pytest.raises(SystemExit):
generate_qr.resolve_short_url(
"https://jbaru.ch/my-slug", "my-slug",
{"shortener": "bitly"}, # no bitly_domain key → decision not recorded
{"bitly": {"api_token": "tok"}}, {}, dry_run=False, vault_path=None,
)
def test_explicit_null_custom_domain_proceeds(generate_qr, monkeypatch):
"""An explicit null custom-domain decision is recorded — proceed on the default
domain, no STOP, no re-ask."""
captured = {}
def fake_create_bitly_link(long_url, api_token, custom_back_half=None, domain=None):
captured["domain"] = domain
return {"short_url": f"https://bit.ly/{custom_back_half}", "link_id": "id", "short_path": custom_back_half}
monkeypatch.setattr(generate_qr, "create_bitly_link", fake_create_bitly_link)
short_url, meta = generate_qr.resolve_short_url(
"https://jbaru.ch/my-slug", "my-slug",
{"shortener": "bitly", "bitly_domain": None}, # recorded decision: no custom domain
{"bitly": {"api_token": "tok"}}, {}, dry_run=False, vault_path=None,
)
assert captured["domain"] is None
assert meta["short_path"] == "my-slug"
def test_slug_cache_entry_is_reused(generate_qr, monkeypatch):
"""A tracked entry whose back-half is already the slug is reused from cache —
no API call — when the target matches."""
def boom_create(*a, **k):
raise AssertionError("must reuse cache, not create a new link")
monkeypatch.setattr(generate_qr, "create_bitly_link", boom_create)
tracking_db = {"qr_codes": [{
"talk_slug": "my-slug",
"target_url": "https://jbaru.ch/my-slug",
"shortener": "bitly",
"short_path": "my-slug",
"short_url": "https://jbaru.ch/my-slug",
"shortener_link_id": "id",
}]}
short_url, meta = generate_qr.resolve_short_url(
"https://jbaru.ch/my-slug", "my-slug", {"shortener": "bitly"}, {}, tracking_db, dry_run=False,
)
assert short_url == "https://jbaru.ch/my-slug"
assert meta["short_path"] == "my-slug".github
eval-resources
humor-postmortem-blind-spots
qr-bitly-slug-from-outline
qr-missing-shortener-detection
shownotes-publisher-omit-placeholder
shownotes-publisher-publish-no-date
shownotes-publisher-publish-with-date
shownotes-publisher-update-add-video
video-extraction-diagnostics
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10
scenario-11
scenario-12
scenario-13
scenario-14
scenario-15
scenario-16
scenario-17
scenario-18
scenario-19
scenario-20
scenario-21
scenario-22
scenario-23
scenario-24
scenario-25
scenario-26
rules
scripts
skills
illustrations
presentation-creator
references
patterns
build
deliver
prepare
scripts
shownotes-publisher
vault-clarification
vault-ingress
vault-profile
tests