| name | classic-cinema |
| description | Browse movies and generate ticket-style confirmation emails for Classic Cinemas Elsternwick. Use when Nathan asks what's on at the cinema, wants movie details, or wants to 'book tickets'. Generates a personal reminder email — does NOT purchase tickets. |
| role | tool-workflow |
| argument-hint | [movie] [time] [tickets] [zone] |
| allowed-tools | Bash, Read, AskUserQuestion, Write |
Classic Cinema
Personal reminder-email generator for Classic Cinemas Elsternwick. Walks a
conversational booking flow, generates a ticket-style HTML email, and sends it
via gog. Does NOT purchase tickets or reserve seats — Nathan buys at the box
office.
Run Card
- Scope: browse movies, pick session, choose seats, generate + send reminder email.
- Defaults: 1 adult ticket, zone picker for seats, Elsternwick venue.
- First safe action: classify intent, then fetch listing or parse args.
- Visible state: availability emoji on every session, seat count, email preview before send.
- Verify:
heal-skill check after any src/ change.
- Publish: confirmation email sent via
gog, booking-log entry appended.
- Fallback: API down → report and stop. Email send fails → show the HTML and stop.
Intent Classification
Classify and proceed. Do NOT show a menu unless intent is genuinely ambiguous.
| Signal | Route | Action |
|---|
| Args with movie name + time | Express | Parse args (arg-parsing.md), proceed to booking flow |
| Movie name only, no time | Express | Show that movie's sessions, ask which |
| No args / "what's on" / "what's showing" | Browse | Fetch listing immediately and show the table |
| Ambiguous | Fallback | Show the menu below |
Ambiguous-only menu
Present only when intent classification returns Fallback:
- What's on — fetch listing, pick from the table.
- Quick book —
/classic-cinema <movie> <time> [tickets] [zone].
- Movie details — look up a specific movie.
- Health check —
bun run skills/classic-cinema/src/heal-skill.ts check.
Owner
- Runtime: Bun. Commands live at
skills/classic-cinema/src/*.ts; run with bun run skills/classic-cinema/src/<command>.ts. Each command's --help is the source of truth for flags, stdout/stderr, temp files, and exit codes — do not copy them here.
- Shared API client + types (base URL, fetch+cache, AEST time, seatmap shapes):
skills/classic-cinema/src/cinema-api.ts.
- Booking-log model + validation:
skills/classic-cinema/src/booking-log.ts.
- Booking choreography and API details:
skills/classic-cinema/references/booking-flow.md.
- Argument parsing:
skills/classic-cinema/references/arg-parsing.md.
- Email template fill:
skills/classic-cinema/references/template-fill.md.
- Email sending:
skills/classic-cinema/references/email-send.md.
- Booking log shape:
skills/classic-cinema/references/booking-log.md.
- Skill health doctor:
skills/classic-cinema/src/heal-skill.ts (run heal-skill check when a booking fails or output looks wrong).
- Legacy Python scripts under
scripts/*.py are superseded by src/*.ts; retirement criteria: skills/classic-cinema/references/retirement-criteria.md.
Express Mode (3 questions max)
Parse args right-to-left: zone → tickets → time → movie remainder. See arg-parsing.md.
- Movie + session — fuzzy match, show sessions with availability emoji
- Tickets — "1+1" = 1 adult + 1 child. Default: 1 adult. ⚠️ Some sessions (arthouse, festival, late-evening) have no Child tier — fallback to "2 adults" with Nathan's confirmation, never silently. See booking-flow.md.
- Seats — zone picker or full map (see Availability UX below)
Best case: /classic-cinema faraway 10am 1+1 middle → zero questions → confirm → send.
Full choreography in booking-flow.md.
Browse Mode
- Fetch movie listing via API (instant)
- Show the listing table, then present Next Safe Actions (post-listing)
- Movie details — when Nathan asks about a movie, use the API data first (
summary, trailer URL). Supplement with WebSearch only if Nathan wants more (reviews, cast, etc).
- Nathan picks a movie → show sessions with availability emoji, then present Next Safe Actions (post-sessions)
- Nathan picks a session → converge with Express at Q2 (Tickets)
Full choreography in booking-flow.md.
Next Safe Actions
DX lens: present choices as a short numbered list so the user can reply by
number. Bold the recommended default. Never present more than 4 options.
Post-listing (after showing tonight's movies)
- Pick a movie (reply by number or name) — see sessions + availability.
- Movie details — trailer, synopsis, or reviews for a specific title.
- Quick book —
/classic-cinema <movie> <time> [tickets] [zone].
- Nothing tonight — done.
Post-sessions (after showing a movie's sessions with availability)
- Pick a session (reply by number or time) — check tickets + seats.
- Back to listing — see all movies again.
- Movie details — trailer, synopsis, or reviews.
Post-booking (after email sent)
- Done — booking logged.
- Book another — back to listing.
Availability UX
| % Available | Emoji | Label | Seat behavior |
|---|
| 51-100% | 🟢 | plenty available | Zone picker |
| 21-50% | 🟡 | filling up | Zone picker |
| 1-20% | 🔴 | almost full! | Auto-show full seat map |
| 0% | 🚨 | SOLD OUT | Block, suggest alternatives |
Always show raw numbers: 🟢 94% available (141/150 seats)
≤20% available rule: skip zone picker, render full seat map. If Express provided a zone arg, override it — tell Nathan why: "Only N seats left — showing the full map."
Commands
Run from the repo root. Each --help owns its flags; inspect it rather than guessing.
| Step | Command |
|---|
| Listing / details | bun run skills/classic-cinema/src/list-movies.ts [--movie QUERY] |
| Availability | bun run skills/classic-cinema/src/check-availability.ts --session-ids ID[,ID] |
| Tickets + pricing | bun run skills/classic-cinema/src/parse-tickets.ts --session-id ID --spec "1+1" |
| Seat pick | bun run skills/classic-cinema/src/pick-seats.ts --seatmap-file PATH --zone ZONE --count N |
| Fill email | bun run skills/classic-cinema/src/fill-ticket.ts … (then send via gog, see email-send.md) |
| Health doctor | bun run skills/classic-cinema/src/heal-skill.ts check |
- Pass the API
headerImage value to fill-ticket.ts; do not guess a Classic Cinemas URL or use posterImage.
- Do not copy command flags, temp-file names, JSON shapes, or stdout/stderr contracts into this file.
Gotchas
- Always emit booking-log entries with
jq -cn (compact), never bare jq -n. jq -n pretty-prints multi-line by default, so one entry becomes many lines and corrupts the one-line-per-entry JSONL. (Verified 2026-06-11: the cause is the -n-pretty default, not a git-safety hook — that hook is retired.) parse-tickets.ts and the send flow write through Bun.write; if you hand-append, build with jq -cn and >> it. Recover with heal-skill repair --only booking-log-valid --execute.
fill-ticket.ts uses replaceAll, not replace. {{MOVIE_TITLE}} appears 3× and {{WEB_VIEW_URL}} 2× in the frozen template; a single-occurrence replace would ship literal {{…}} tokens in the email.
Verification
- After any
src/ change: skills/test-runner/src/test-runner.sh run --cwd skills/classic-cinema -- src/cinema-api.test.ts src/pick-seats.test.ts src/fill-ticket.test.ts src/booking-log.test.ts and cd skills/classic-cinema && bunx tsc --noEmit -p tsconfig.json.
- After any change:
bunx biome check --diagnostic-level=error skills/classic-cinema/src/.
- Whole-skill health (scripts, frozen template, booking log, owner paths):
bun run skills/classic-cinema/src/heal-skill.ts check.
- Use live API checks only when listing, availability, or booking choreography changed.
Safety Invariants
- NEVER click CHECKOUT on the Classic Cinemas site (triggers real payment — G7/G10)
- Always confirm before sending email (AskUserQuestion)
- Never hard-code the Gmail account — read from
.productivity.yml (fall back to ~/code/my-second-brain/.productivity.yml)
- Validate seats against regex
^[A-Z]\d{1,2}(, [A-Z]\d{1,2})*$ before template fill
References