CtrlK
BlogDocsLog inGet started
Tessl Logo

jbaruch/speaker-toolkit

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.

Quality

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Low

Low-risk findings worth noting

Overview
Quality
Evals
Security
Files

phase6-publishing.mdskills/presentation-creator/references/

Phase 6: Publishing — Detail

Pre-Flight Checklist

Before ANY Phase 6 action, load these files. If any is missing, STOP and ask.

  1. speaker-profile.json — publishing config, shortener, URL patterns, QR settings
  2. secrets.json — API keys (bitly, rebrandly, gemini). Missing key = stop, not fallback.
  3. 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.

Step 6.0: Resources Gathering

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.

  1. Run the extraction script against outline.yaml:

    "{python_path}" "{speaker_toolkit_root}/skills/presentation-creator/scripts/extract-resources.py" outline.yaml
  2. The 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.

  3. 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.

  4. 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.

  5. 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.

Step 6.1: Shownotes

Read publishing_process.shownotes. If enabled:

  • Generate the talk page at {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).
  • Follow the publishing_method description (git push, CMS, manual) to make it live.
  • If 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.
  • Include: title, abstract, slide embed/download link, speaker bio.
  • If 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 name

Examples:

shownotes.url.baseshownotes.url.templateslugResulting URL
https://speaking.example.com/{slug}/arc-of-aihttps://speaking.example.com/arc-of-ai/
https://speaker.dev/talks/{slug}/arc-of-aihttps://speaker.dev/talks/arc-of-ai/
https://example.com/{yyyy}-{mm}-{dd}-{slug}/jfokus-robocodershttps://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.

Step 6.2: QR Code

Read publishing_process.qr_code. If enabled:

  1. Determine the URL to encode:

    • If target is shownotes_url, use the shownotes URL from Step 6.1
    • If target is custom_url, use the custom_url field
  2. Resolve URL shortening using one of these paths:

PathShort URL resolutionQR image
MCP (preferred when configured)Agent calls Bitly/Rebrandly MCP tool, passes --short-url and --shownotes-url to scriptScript generates locally from the resolved URL
Direct APIScript calls bit.ly/rebrand.ly REST API via secrets.jsonScript generates locally
None (explicit "shortener": "none" only)Script uses the raw shownotes URLScript generates locally

MCP path (preferred when Bitly or Rebrandly MCP server is installed):

  • Bitly MCP: npx @bitly/mcp — covers link creation, update, QR, and analytics
  • Rebrandly MCP: see rebrandly.com MCP documentation
  • Agent creates or updates the short link via MCP tools, then passes the resolved URL to generate-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
  • Pass --short-provider and --short-link-id so the catalog keeps the real provider identity instead of the generic mcp_preresolved marker

Direct API path (when MCP is not available):

  • API keys must be stored in {vault_root}/secrets.json with chmod 600:
    {
      "gemini": {"api_key": "..."},
      "bitly": {"api_token": "..."},
      "rebrandly": {"api_key": "..."}
    }
  • Script reads secrets.json and calls the shortener's REST API directly
  • The shortener field in the profile controls which service to use

None path (no shortening):

  • Requires an explicit "shortener": "none" in the profile
  • Script encodes the raw shownotes URL directly into the QR code
  • Any other resolution failure exits non-zero without generating a QR (rules/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.

  1. 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,128

    The 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.

  2. The script will:

    • Match the QR background color to the target slide (walks slide → layout → master for solid fill; falls back to white with a warning for theme-colored fills)
    • Auto-select foreground color (black on light backgrounds, white on dark) using WCAG relative luminance
    • Insert the QR as a 2" square in the bottom-right corner
    • Serialize concurrent publication of the same talk and hold that serialization across the external effects — see skills/presentation-creator/scripts/generate-qr.py (qr_publication_lock docstring) for the lock's scope and placement
    • Commit the QR record against the current database generation — see the commit_qr_record docstring in the same script for what it rebases and what it refuses
    • On a rejected commit: exit non-zero having written one JSON document to stderr — {"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 case
    • Persist schema-v2 QR metadata in qr_codes[] — including one artifacts[] receipt per generated PNG — through the shared tracking-database transaction used by generate-qr.py
    • Refuse to write when the database moved underneath the run — the exact generation and same-talk conditions live in commit_qr_record
  3. Re-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).

Step 6.3: Export

Read publishing_process.export_format and publishing_process.export_method.

  • If export_script is provided, run it (substituting the deck path)
  • If export_method is a description, follow its instructions
  • Common pattern: PowerPoint AppleScript for PDF (see phase5-slides.md)
  • If no export info, ask: "How do you want to export? PDF, keep .pptx only, or both?"

Step 6.4: Talk Timer Artifact

Optional 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.txt

Format: 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:

  • 25-min talks: 8-13 chapters
  • 45-60 min talks: 10-15 chapters
  • Subdivide chapters exceeding ~5 min into multiple chapters in outline.yaml

Q&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.

Step 6.5: Additional Steps

Read publishing_process.additional_steps[]. For each entry:

  • If automated is true and script is provided, run it
  • If automated is false, present the step to the author as a manual TODO
  • Report completion status for each step

Step 6.6: Go-Live Preparation Checklist

Before delivery, surface a delivery-readiness checklist composed of two strands:

  1. Unobservable patterns from patterns/_index.md (the "Unobservable Patterns — Go-Live Checklist" section). These are patterns the vault cannot score retroactively because they involve pre-event logistics, hidden authoring/provenance processes, physical stage behaviors, post-event follow-up, or external systems the current artifacts cannot prove.
  2. Observable delivery reminders that the speaker can decide to apply on the day — venue setup choices (lights, lectern, mic), opening discipline, time discipline, and screen-blackout tactics. These are observable post-talk, but the speaker benefits from a pre-talk reminder regardless of the vault's ability to score them later.

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
==================================

Step 6.7: Publishing Report

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}
==================================

skills

presentation-creator

references

alternate-entry-flows.md

deck-editing-setup.md

deckops-spec.md

idea-sourcing-wizard.md

pattern-history-authorization.md

phase0-intake.md

phase1-intent.md

phase2-architecture.md

phase3-content.md

phase4-guardrails.md

phase5-slides.md

phase6-publishing.md

phase7-post-event.md

SKILL.md

README.md

tile.json