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 111-entry Presentation Patterns taxonomy (81 observable: 62 patterns + 19 antipatterns; 30 unobservable: 21 patterns + 9 antipatterns) for scoring, brainstorming, and go-live preparation.
—
—
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
Before ANY Phase 6 action, load these files. If any is missing, STOP and ask.
speaker-profile.json — publishing config, shortener, URL patterns, QR settingssecrets.json — API keys (bitly, rebrandly, gemini). Missing key = stop, not fallback.outline.yaml — source of truth for talk slug, metadata, slides, and shownotes URL.
Load via scripts/outline_schema.py (pydantic model exposes talk.slug, talk.title,
talk.shownotes_url_base, slides, etc.) — never re-parse the YAML by hand.Do not guess values that should come from these files. Do not proceed with partial context — every silent assumption becomes a wrong default downstream.
The publishing workflow is speaker-specific. Read publishing_process from
speaker-profile.json. Read the talk slug and metadata from outline.yaml
(talk: block, authored in Phase 1). If the section is missing or empty,
fall back to asking the author interactively and document their answers for
next time.
Extract and curate resource links from the finalized outline before any publishing step. Resources scattered across speaker notes, visual descriptions, and Coda slides are easy to miss — this step catches them systematically.
Run the extraction script against outline.yaml:
"{python_path}" "{speaker_toolkit_root}/skills/presentation-creator/scripts/extract-resources.py" outline.yamlThe script produces resources.json in the talk working directory with
categorized entries: URLs, repos, books/papers, RFCs, and tool mentions.
Each entry includes slide references and context.
Present the extracted resources to the speaker as a formatted review list, grouped by type. Coda section items are flagged and listed first — the speaker deliberately chose to surface them.
The speaker reviews, approves, removes false positives, adds missing items,
and edits entries. Save the approved list back to resources.json with
approved: true on accepted items.
Persist a complete resources[] entry with schema_version: 1, the talk slug,
item count, and category breakdown through the ingress owner's upsert_resource mutation.
Expect the exact existing record for the slug, or {"$missing": true} for a
first entry. Dry-run, review, apply against the reported input SHA, and re-read
as specified by the
owner mutation contract.
If the speaker declines resource gathering, skip this step — Step 6.1 will omit the resource links section from shownotes.
Read publishing_process.shownotes. If enabled:
{shownotes.source.path_or_url}/{shownotes.source.talks_subdir}/{slug}.md
(adapt the extension for Hugo/Astro content collections if the SSG uses a
different convention — check shownotes.source.type and existing entries).publishing_method description (git push, CMS, manual) to make
it live.shownotes.shownotes_template is provided, use it as the frontmatter /
layout skeleton for the new page. Otherwise follow the convention observed
in existing entries under the talks subdir.resources.json exists and has approved: true items, include a
"Resources" section with those links. Read from resources.json in the
talk working directory (produced by Step 6.0) — do not re-scan the outline.Construct the live shownotes URL by composing shownotes.url.base + the
result of substituting the Presentation Spec's slug (and other variables, if
the template uses them) into shownotes.url.template. The slug was agreed
with the author in Phase 1 — NEVER invent or rephrase it.
Template variables supported:
{slug} — talk slug from Presentation Spec{yyyy}, {mm}, {dd} — 4- and 2-digit date components from the talk
frontmatter date field{yy} — 2-digit year{venue} — slugified venue nameExamples:
shownotes.url.base | shownotes.url.template | slug | Resulting URL |
|---|---|---|---|
https://speaking.example.com | /{slug}/ | arc-of-ai | https://speaking.example.com/arc-of-ai/ |
https://speaker.dev | /talks/{slug}/ | arc-of-ai | https://speaker.dev/talks/arc-of-ai/ |
https://example.com | /{yyyy}-{mm}-{dd}-{slug}/ | jfokus-robocoders | https://example.com/2026-04-16-jfokus-robocoders/ |
If the SSG uses per-file permalink: frontmatter (common in Eleventy), read
the permalink from the newly-generated page's frontmatter rather than
synthesizing from url.template. In that case shownotes.url.template may
be null.
Before passing the URL downstream (QR code, bit.ly target, post-event video description), verify the URL is reachable — a 404 on the deployed site breaks printed QR codes with no recovery path.
After the page is live and verified, persist shownotes_url and
shownotes_published: true on the matching talk with one
update_talk_publishing mutation. Its expect object must cover those same
fields with their exact values from the latest strict read. Apply it through the
owner mutation contract,
never by rewriting the database.
If not enabled, skip.
Read publishing_process.qr_code. If enabled:
Determine the URL to encode:
target is shownotes_url, use the shownotes URL from Step 6.1target is custom_url, use the custom_url fieldResolve URL shortening using one of these paths:
| Path | Short URL resolution | QR image |
|---|---|---|
| MCP (preferred when configured) | Agent calls Bitly/Rebrandly MCP tool, passes --short-url and --shownotes-url to script | Script generates locally from the resolved URL |
| Direct API | Script calls bit.ly/rebrand.ly REST API via secrets.json | Script generates locally |
None (explicit "shortener": "none" only) | Script uses the raw shownotes URL | Script generates locally |
MCP path (preferred when Bitly or Rebrandly MCP server is installed):
npx @bitly/mcp — covers link creation, update, QR, and analyticsgenerate-qr.py via --short-url--shownotes-url is ALWAYS required — it is the canonical redirect target the
catalog records. Passing only the short URL would make the record claim the
short link redirects to itself--short-provider and --short-link-id so the catalog keeps the real
provider identity instead of the generic mcp_preresolved markerDirect API path (when MCP is not available):
{vault_root}/secrets.json with chmod 600:
{
"gemini": {"api_key": "..."},
"bitly": {"api_token": "..."},
"rebrandly": {"api_key": "..."}
}secrets.json and calls the shortener's REST API directlyshortener field in the profile controls which service to useNone path (no shortening):
"shortener": "none" in the profilerules/qr-generation-rules.md §2, §6, §7)First short link — confirm the custom domain: before a short link is created
for the first time, ensure publishing_process.qr_code.{shortener}_domain is
recorded in the profile. If the key is absent, ASK the user whether they have a
custom domain (e.g. jbaru.ch) and SAVE the answer — the domain, or null for
"no custom domain" — then proceed. See rules/qr-generation-rules.md §8. The
Direct API path enforces this (the script STOPS if the key is absent); on the MCP
path make the check before resolving the link.
Run the QR generation script:
# MCP-preresolved mode (--shownotes-url is the canonical redirect target):
"{python_path}" "{speaker_toolkit_root}/skills/presentation-creator/scripts/generate-qr.py" deck.pptx \
--talk-slug SLUG --shownotes-url SHOWNOTES_URL --short-url SHORT_URL \
--short-provider bitly --short-link-id LINK_ID
# Direct API mode:
"{python_path}" "{speaker_toolkit_root}/skills/presentation-creator/scripts/generate-qr.py" deck.pptx \
--talk-slug SLUG --shownotes-url SHOWNOTES_URL \
--vault /path/to/vault
# No shortening:
"{python_path}" "{speaker_toolkit_root}/skills/presentation-creator/scripts/generate-qr.py" deck.pptx \
--talk-slug SLUG --shownotes-url SHOWNOTES_URL
# PNG-only (no deck — for presenterm, PDF, or standalone use):
"{python_path}" "{speaker_toolkit_root}/skills/presentation-creator/scripts/generate-qr.py" --png-only \
--talk-slug SLUG --shownotes-url SHOWNOTES_URL \
--output /path/to/qr.png --bg-color 128,0,128The script is a non-owner dual reader and a current-schema writer. Dry-run accepts legacy database schema 0 or current schema 1 without changing either. A real run requires database schema 1 before URL-shortener calls, deck edits, or tracking persistence. Route a legacy database through vault-ingress Step 1. An unsupported future database or record version is no usable prior state.
The script will:
skills/presentation-creator/scripts/generate-qr.py (qr_publication_lock
docstring) for the lock's scope and placementcommit_qr_record docstring in the same script for what it rebases and
what it refuses{"error": "qr_publication_unfinalized", ...} with retry,
atomic_rollback, and an effects[] entry per landed effect, each
carrying its own rollback action. Render it for the operator; do not
restate its fields here. See unfinalized_effects_payload in
skills/presentation-creator/scripts/generate-qr.py. The run makes no
tracking-database change in that caseqr_codes[] — including one
artifacts[] receipt per generated PNG — through the shared
tracking-database transaction used by generate-qr.pycommit_qr_recordRe-running for the same talk_slug with a different target URL is safe:
printed QR codes stay valid. Which link a run reuses, retargets, or creates is
decided by resolve_short_url in
skills/presentation-creator/scripts/generate-qr.py — see its docstring.
No raw-dogging: NEVER bypass generate-qr.py with hand-rolled python-pptx or
direct qrcode library calls. If the script targets the wrong slides, uses the wrong
shortener, or produces the wrong colors — fix the inputs (profile config, secrets,
arguments), don't patch the outputs with ad-hoc code. The script is the single source
of truth for QR generation; working around it silently drops shortening, tracking,
and color matching.
Dependencies: pip install qrcode (Pillow is already a transitive dep of python-pptx).
Read publishing_process.export_format and publishing_process.export_method.
export_script is provided, run it (substituting the deck path)export_method is a description, follow its instructionsOptional step: generate a plain-text timing file from outline.yaml's
chapters[] (each chapter has target_min). Run unless the author opts out.
Source: chapters[].title and chapters[].target_min in outline.yaml.
Generate a plain-text timing file for timemytalk.app by running:
"{python_path}" "{speaker_toolkit_root}/skills/presentation-creator/scripts/generate-talk-timings.py" \
outline.yaml --output talk-timings.txt
# If the talk slot includes Q&A time:
"{python_path}" "{speaker_toolkit_root}/skills/presentation-creator/scripts/generate-talk-timings.py" \
outline.yaml --qa 5 --output talk-timings.txtFormat: one line per chapter, MM:SS Label, using cumulative start times.
The final line is always MM:SS FINISH where the timestamp equals the total
talk duration (including Q&A if applicable).
Granularity guidelines:
outline.yamlQ&A: if the talk slot includes Q&A time, pass --qa MINUTES to append a
Q&A chapter before FINISH.
The speaker uploads the resulting .txt file to timemytalk.app before delivery.
Read publishing_process.additional_steps[]. For each entry:
automated is true and script is provided, run itautomated is false, present the step to the author as a manual TODOBefore delivery, surface a delivery-readiness checklist composed of two strands:
Both strands matter for delivery quality and belong in the same checklist.
GO-LIVE CHECKLIST — {talk title}
==================================
VENUE SETUP:
[ ] Lights ON — keep light on the speaker (Reynolds: audience must see your face;
they cannot read facial expressions in the dark; dim only the screen-area lights
if the projector requires it)
[ ] Lectern moved aside — step out from behind any podium or table; place computer
on the floor in front of the stage so you don't have to turn back to advance slides
[ ] Mic test — wireless lavalier or headset preferred over handheld; do not rely on
shouting
PRE-EVENT:
[ ] Know Your Audience — audience/organizer research captured; concrete adaptations noted
[ ] Required — mandatory/assigned context confirmed and used deliberately as practice
[ ] Fourthought — ideation/capture/organization completed before slide authoring
[ ] Proposed — accepted CFP artifact retained; final scope checked against its promises
[ ] Concurrent Creation — one Slide Wrangler named; collaboration history retained
[ ] Peer Review — colleague/editor review completed; comments retained
[ ] Social Media Advertising — dated promotional posts published and retained, if relevant
[ ] Preparation — backups, cables, hydration, room layout check
[ ] Carnegie Hall — completed 4 rehearsals (pace, delivery, fixes, groove)
[ ] The Stakeout — staging area identified near venue
[ ] Posse — supporter(s) confirmed for front row
[ ] Seeding Satisfaction — plan to arrive early and mingle
[ ] First Impression — pre-event communications (invitation tone, agenda
framing, bio wording) have already shaped the audience's view of you
before you walk in; the room you walk into builds on that. Do NOT
arrive heads-down at the laptop fixing slides. Engage warmly with
early-arrivers, shake hands, ask questions. The mood you project
before the talk starts is part of the talk.
[ ] Shoeless — comfort ritual ready
DURING DELIVERY:
[ ] Honeymoon-window discipline — first 1–2 minutes earn the talk; do NOT spend
them on agenda slides, "let me introduce myself," or "thanks for the invitation"
filler. Open with the planned PUNCH hook (see outline)
[ ] Never apologize, never confess nerves — both are self-focused at a moment that
should be audience-focused. Acknowledge nerves to yourself; do not share them
with the audience
[ ] Hara hachi bu — finish at 90–95% of the allotted time, never run over;
"leave them slightly hungry, not stuffed"
[ ] Screen Blackout — use the B key (or planned black slides) at section boundaries,
personal stories, and audience-question moments to redirect attention
[ ] Lightsaber — if laser pointer needed, max 2-3 steady moments
[ ] Red/Yellow/Green — exit feedback cards set up (if venue supports)
AVOID:
[ ] Abstract Attorney — compare the final talk directly with the accepted abstract
[ ] Borrowed Shoes — confirm authorship and substantially adapt borrowed material
[ ] Laser Weapons — don't wave the pointer; use built-in highlights
[ ] Bunker — step out from behind the podium
[ ] Backchannel — don't monitor social media during the talk
POST-EVENT:
[ ] Crucible — record feedback and the concrete revisions made before the next delivery
[ ] Spaced Follow-Up — after 1–2 weeks, send opt-in attendees 2–3 recall questions
==================================PUBLISHING REPORT — {talk title}
==================================
[DONE/SKIP] Resources: {N approved items from resources.json, or "skipped"}
[DONE/SKIP] Shownotes: {url or "not configured"}
[DONE/SKIP] QR code: {inserted at slide N, encoded URL, shortener used}
[DONE/SKIP] Export: {format} → {output path}
[DONE/SKIP] Talk timer: {output path, or "no pacing summary in outline"}
[DONE/SKIP/TODO] {additional step name}: {status}
[INFO] Go-live checklist: {presented above}
==================================.tessl-plugin
rules
skills
illustrations
presentation-creator
references
patterns
build
deliver
prepare
scripts
shownotes-publisher
vault-clarification
vault-ingress
references
scripts
vault-profile