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.
89
94%
Does it follow best practices?
Impact
89%
1.30xAverage score across 25 eval scenarios
Advisory
Suggest reviewing before use
Process steps in order. Do not skip ahead. This skill writes a
markdown file into the Jekyll shownotes site's _talks/ collection
where a custom parser plugin (_plugins/markdown_parser.rb in the
target site) extracts structured fields by pattern matching on the
body. The format is strict — small mistakes silently flatten
content or break conditional rendering.
Reference files in this skill:
references/parser-contract.md —
extraction grammar per extracted_* field;
references/template-conditionals.md —
how talk.html renders each field;
references/common-mistakes.md —
13 failure modes with the right way.
Default target: ~/Projects/shownotes (deployed at
https://speaking.jbaru.ch).
Everything the shownotes page needs is already in the talk's artifacts by the time this skill runs. Do not ask the user to re-supply anything that is derivable. Read automatically:
From outline.yaml — the talk's presentation spec. Load via
the schema script's JSON emit mode:
python3 skills/presentation-creator/scripts/outline_schema.py \
--emit-json <path-to-outline.yaml>Script contract:
outline.yaml as a single positional argumentOutline modelFAIL: ... — abort and surface the failureParse the JSON, never re-parse YAML by hand. Map fields directly:
| Shownotes field | Source in outline.yaml |
|---|---|
| Title (body H1) | talk.title |
| Filename slug | talk.slug — the only source. Never invent, derive from venue, or rephrase |
| Conference | talk.venue |
| Date | talk.delivery_date (ISO YYYY-MM-DD) |
| Speakers | talk.speakers[] — single → `"by {{ site.speaker.display_name |
| Abstract seed | talk.thesis |
From the talk directory:
resources.json (produced by extract-resources.py in Phase 6
Step 6.0) — becomes the ## Resources section. If the file is
missing OR has no approved: true items, omit the section_talks/{filename_stem}.md — if this is an update
(not a first publish), read it first so Step 7 preserves hand-edits.
{filename_stem} is defined in Step 2Ask the user EXACTLY one question — the slides PDF embed URL
(Google Drive https://drive.google.com/file/d/.../preview form).
That's the only value not in any file — the speaker just uploaded
the deck. Don't ask about Video; URLs arrive post-recording, and
omitting the line is what fires the "Video Coming Soon" badge
(Step 5). If the user volunteers a video URL in the same turn,
capture it.
Ask follow-ups ONLY on ambiguity:
Skill(skill: "presentation-creator") to repair, then resumetalk.thesis empty → ask the speaker for a one-paragraph
abstract (Step 4 rules apply)talk.delivery_date missing → confirm whether the talk has
happened (pre-talk publish is fine; Step 2 picks the filename
convention by the field's presence)_talks/{filename_stem}.md exists AND speaker didn't
flag this as an update → ask before overwriting (Step 7)Proceed immediately to Step 2.
The talk slug {talk_slug} is outline.yaml::talk.slug — the spec
is the only source. Already kebab-case validated by outline_schema.py.
Never invent it, never derive it from the venue or title, never
rephrase.
Filename convention in _talks/, picked by rule:
talk.delivery_date is set → {YYYY-MM-DD}-{talk_slug}.md
(e.g., 2026-05-07-devoxx-uk-2026-300-tokens.md)talk.delivery_date is not set (pre-talk publish) →
{talk_slug}.md (e.g., geecon-2026-absolutely-right.md)Don't ask the user which convention to use. The presence of
delivery_date is the signal.
The chosen filename's stem (everything before .md) is the
{filename_stem} used by Steps 6, 8, and 9 for the thumbnail path
and the live URL. Examples:
talk_slug | delivery_date | filename | filename_stem |
|---|---|---|---|
devoxx-uk-2026-300-tokens | 2026-05-07 | 2026-05-07-devoxx-uk-2026-300-tokens.md | 2026-05-07-devoxx-uk-2026-300-tokens |
geecon-2026-absolutely-right | (absent) | geecon-2026-absolutely-right.md | geecon-2026-absolutely-right |
If an existing file at the conventional path was created in the
OTHER convention (e.g., the file is {talk_slug}.md but
talk.delivery_date is now set), keep the existing filename
unchanged. Renaming a published talk breaks the live URL and any
QR codes printed against it.
Full path: ~/Projects/shownotes/_talks/{filename_stem}.md.
Proceed immediately to Step 3.
Minimum (and maximum) frontmatter:
---
layout: talk
---The Jekyll plugin auto-inserts this if absent; write it explicitly
for portability. Do NOT add title:, video:, slides:,
conference:, date:, description:, abstract:, or
thumbnail_url: — every one of these is either extracted from the
body, ignored by the templates, or duplicates content (see
references/common-mistakes.md
entries 1c and 5 for the failure modes).
Proceed immediately to Step 4.
Body structure is a strict sequence the parser expects. Every value
in {braces} below is derived from outline.yaml or
resources.json per Step 1 — not asked from the user (except the
slides URL, which is the one question from Step 1).
# {talk.title}
**Conference:** {talk.venue}
**Date:** {talk.delivery_date}
**Slides:** [View Slides]({slides_url_from_step_1})
**Video:** [View Video]({video_url}) # ← omit this line entirely if no video yet
A presentation at {talk.venue} in
{Month YYYY} in
{City, Country} by
{{ site.speaker.display_name | default: site.speaker.name }}
## Abstract
{talk.thesis, lightly adapted into one flowing paragraph. No
sub-headings, no lists, no code blocks. See
[references/parser-contract.md](references/parser-contract.md) for
the exact capture-and-flatten mechanic.}
## Resources
- [{title}]({url}) # one bullet per approved resources.json entry
- [{title}]({url})
### Optional Sub-Sections
Sub-sections under Resources DO render correctly — Resources
captures everything after `## Resources` until end-of-file.Authoring rules (full grammar in references/parser-contract.md):
**FieldName:** value, one per line. URLs
wrapped as [text](url) — bare URLs don't populate the
extracted_* fields. Date in ISO YYYY-MM-DD**Field:** TBD fires the wrong state
(covered in Step 5)A presentation at
(the parser anchor) and runs to the next ## headingmarkdownify, so
any sub-headings / lists / code blocks inside flatten to literal
textmarkdownify — sub-headings,
lists, and formatting all renderProceed immediately to Step 5.
talk.html's conditional rendering keys off truthiness of
extracted_slides and extracted_video. Any non-empty value flips
the badge to "Available" and the embed include tries to load the
value as a URL — so placeholder strings always produce the wrong
badge plus a broken iframe. The only correct way to express "not
yet available" is to omit the field line entirely.
Common placeholder shapes and what they break are enumerated in references/common-mistakes.md entries 1 (Video) and 1b (Slides). The fix in every case: omit the line; add it when the real URL lands.
Updating a published file when the URL lands:
_talks/{filename_stem}.md (read-then-edit
per Step 7's preservation rule)**Slides:** or **Video:** line in real markdown-link
form, placed inside the field block (between **Date:** and the
blank line before the A presentation at... paragraph)The badge and embed automatically fill in on the next build.
Never commit <!-- TODO --> HTML comments — inline comments after
a link line are captured by the parser's value group and pollute
extracted_* values. Tracking for incomplete work belongs in a PR
description or an issue, not the committed file.
Proceed immediately to Step 6.
Thumbnails are resolved by filename-stem naming convention —
the site's templates compute the path from the .md filename:
/assets/images/thumbnails/{filename_stem}-thumbnail.png({filename_stem} as defined in Step 2.) Examples:
| Talk file | Expected thumbnail file |
|---|---|
2026-05-07-devoxx-uk-2026-300-tokens.md | assets/images/thumbnails/2026-05-07-devoxx-uk-2026-300-tokens-thumbnail.png |
geecon-2026-absolutely-right.md | assets/images/thumbnails/geecon-2026-absolutely-right-thumbnail.png |
Drop the thumbnail file (4:3 PNG; the site resizes to 400×300) at
that path. Do NOT set thumbnail_url: in frontmatter — it's
checked only for truthiness as a featured-talks signal and never
read as a URL; see
references/common-mistakes.md
entry 1c and
references/template-conditionals.md
for the three template locations that hard-code the slug-derived
path. Missing thumbnails fall back via onerror to the placeholder
SVG — page renders fine without one.
If a thumbnail needs to be produced, hand off to
Skill(skill: "illustrations") — this skill places files, doesn't
generate images.
Proceed immediately to Step 7.
Compose the full file content per Steps 3 + 4 + 5 + 6 and write it
to ~/Projects/shownotes/_talks/{filename_stem}.md.
If a file at that path already exists, this is an UPDATE — typically the video-add case from Step 5, or a resource refresh. In the update case:
**Video:** line, append a new resource)NEVER overwrite an existing file with a fresh composition unless the user explicitly requests a full rewrite — speakers often hand-edit shownotes post-publish (typo fixes, resource additions) and a re-author wipes those edits.
Proceed immediately to Step 8.
Before pushing, validate the file parses cleanly. The subshell +
pipefail is required — without it, tail's successful exit masks
a failing jekyll build:
cd ~/Projects/shownotes
( set -o pipefail && bundle exec jekyll build 2>&1 | tail -20 ) \
|| { echo "Build failed — fix per Step 4 and re-run"; exit 1; }The || { ...; exit 1; } guard makes the build a hard gate: a
non-zero exit aborts before any of the visual-check commands run.
Only after the build is green, open the rendered page locally:
bundle exec jekyll serve --port 4000 2>&1 &
open "http://localhost:4000/talks/{filename_stem}/"Visually confirm: title, conference + date + correct video badge (Available vs Coming Soon), slides embed, single-paragraph abstract, resources list. If a field doesn't render, the parser didn't match — re-check Step 4 rules.
Proceed immediately to Step 9.
Default flow — branch + PR. Required under
jbaruch/coding-policy: ci-safety unless the target repo has the
Content-Only Direct-Push Carve-Out wired (authority-of-record rule
_talks/** /
assets/images/thumbnails/** — see that rule for full
preconditions).cd ~/Projects/shownotes
git checkout -b shownotes/{filename_stem}
git add _talks/{filename_stem}.md [assets/images/thumbnails/{filename_stem}-thumbnail.png]
git commit -m "Add shownotes: {Talk Title} at {Conference}"
git push -u origin shownotes/{filename_stem}
gh pr create --fill
gh pr checks --watch --fail-fast
# After merge, watch the Pages deployment and confirm 200:
gh run watch --exit-status $(gh run list --workflow=pages-build-deployment --branch=main --limit=1 --json databaseId --jq '.[0].databaseId')
curl -fsI "{site.url}/talks/{filename_stem}/" | head -1 # expect: HTTP/2 200Direct-push (carve-out wired AND changes touch only carve-out paths):
cd ~/Projects/shownotes
git add _talks/{filename_stem}.md [assets/images/thumbnails/{filename_stem}-thumbnail.png]
git commit -m "Add shownotes: {Talk Title} at {Conference}"
git push
gh run watch --exit-status $(gh run list --workflow=pages-build-deployment --branch=main --limit=1 --json databaseId --jq '.[0].databaseId')
curl -fsI "{site.url}/talks/{filename_stem}/" | head -1 # expect: HTTP/2 200The carve-out bypasses the PR cycle but NOT the CI/deploy watch —
ci-safety requires both the run conclusion and the 200 to confirm
the publish landed. If the carve-out status is unknown, default to
the branch + PR flow.
If a QR code is needed for the live URL, hand off via
Skill(skill: "presentation-creator") — the QR generation flow
lives in that skill's Phase 6 Step 6.2.
Finish here — the skill is complete.
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
rules
skills
illustrations
presentation-creator
references
patterns
build
deliver
prepare
scripts
shownotes-publisher
vault-clarification
vault-ingress
vault-profile