Seattle Events Radar — Design for a Weekly What's-On System
Related: seattle-cafe-and-night-guide, adhd-family-operating-manual
Builds-on: the places guide (places/, scripts/places.sh, the places skill)
Informs: Projects/tech-blog (optional later integration, same pattern as /guide)
The Gap the Places Guide Doesn't Cover
The places guide answers "where should we go?" It works because places are durable: Canon's frontmatter from April is still true in August. Events are the opposite — they expire, and the interesting ones (ZooTunes on-sale day, a Movies at the Mural night, a one-off kids' festival) punish you for finding out late. So this can't be a corpus of markdown files that slowly grows. It has to be a pipeline that runs weekly and emits a digest, with only a thin layer of durable state (the seasonal series that recur every year).
Same architecture split as places, though: deterministic scripts for facts, Claude for curation, markdown as the artifact. That split is what made places maintainable, and it transfers cleanly.
Concrete misses this design would have caught: Movies at the Mural ran Princess Bride July 31 and Hamilton August 7 (free, Seattle Center); ZooTunes' remaining August run includes The Beths/Beach Bunny (Aug 16), Courtney Barnett with Built to Spill (Aug 18), and The Breeders (Aug 20) — most at the family-friendly 6pm slot, kids 2 and under free. And the deeper lesson from ZooTunes: tickets went on sale February 27 for a June–August season. A weekly radar has to track on-sale dates, not just event dates, or the good shows are gone before the digest ever mentions them.
Source Audit
Three tiers, by how they're consumed. The tier determines where they live in the pipeline.
Tier 1 — Scriptable (APIs and feeds, the events.sh layer)
| Source | Access | Verdict |
|---|---|---|
| Ticketmaster Discovery API | Free key, 5,000 calls/day, geo + date-range + genre search | The backbone for ticketed concerts/shows. One call covers "everything within N miles of Shoreline in the next 14 days." |
| TMDB API | Free key, now_playing + upcoming endpoints with region filter |
Movies coming up. No showtimes (those APIs are all paid/licensed), but "what's releasing and what's in theaters" is the actual question. |
| Trumba feeds | Append .ics/.rss/.json to any Trumba calendar URL |
Seattle's civic layer runs on Trumba — Seattle Center's event calendar exports ICS directly. Free, structured, no scraping. |
| KCLS BiblioCommons | kcls.bibliocommons.com/v2/events?locations=1535 (Shoreline branch) |
Storytimes and kids' programs at the neighborhood library. Structured pages, filterable by branch. |
| Venue ICS feeds | Varies per venue | A registry file (events/feeds.yaml) accumulates these over time, same way reserves.md accumulates places. |
Tier 2 — Curation layers (Claude-side, no API)
| Source | Access | Verdict |
|---|---|---|
| EverOut (The Stranger) | Blocks non-browser fetches — verified a 403 on plain fetch today. WebSearch snippets work; their weekly newsletters work better. | The best human curation in the city. Route around the bot-block via newsletter-as-feed: subscribe to Stranger Suggests / EverOut weekly emails, read them at digest time via Gmail. Zero scraping, fully in-terms. |
| ParentMap | Open calendar + monthly "Best Family Activities" roundups, fetchable | The kids-4-and-2 filter already applied by someone else. Also has a weekend newsletter. |
| Seattle's Child | Open, fetchable | Seasonal family guides (outdoor movies, festivals). |
| Seattle Times / Seattle Refined | Open, fetchable | Seasonal "guide to summer" style anchors; monthly PNW festival roundups. |
Tier 3 — Seasonal anchors (durable state, refreshed ~quarterly)
These recur annually and belong in a standing file, not the weekly pull: ZooTunes (Jun–Aug, on-sale late Feb), Movies at the Mural (Jul–Aug, free), Seattle Parks outdoor movies (22 nights across 7 parks), Bellevue Movies in the Park (Jul–Aug Tuesdays/Thursdays), Celebrate Shoreline (mid-August, Cromwell Park), SIFF (May), Chateau Ste. Michelle summer concerts, farmers markets. Each entry carries when to check for the next season's announcement — that's the field that makes February's on-sale date show up in a February digest.
Dead ends (checked so we don't re-litigate)
- Eventbrite — public event search API killed February 2020. Org-level endpoints only. Out.
- Songkick — API closed to new keys for years. Out.
- Bandsintown — artist-lookup only (give it an artist, get their dates). Useless for "what's happening near me," useful later if we add a "followed artists" feature.
- SeatGeek — usable but near-total overlap with Ticketmaster for discovery. Backup, not primary.
Architecture
Mirror of places, with the digest replacing the per-place file as the artifact:
flowchart TB
A[events.sh pull<br/>Ticketmaster + TMDB + ICS feeds] --> C[normalized JSON cache<br/>events/.cache/]
B[Curation pass<br/>EverOut newsletter via Gmail,<br/>ParentMap, WebSearch] --> D
C --> D[Claude: filter + rank<br/>distance, kid-fit, taste]
E[events/seasonal.md<br/>anchor series + on-sale dates] --> D
D --> F[Weekly digest<br/>events/weekly/YYYY-MM-DD.md]
F -.optional later.-> G[Blog: events.json<br/>same pattern as places.json]
The pieces:
scripts/events.sh— deterministic, likeplaces.sh. Subcommands:pull(hit Ticketmaster geo-search + TMDB + every feed in the registry, write normalized JSON),feeds(list/validate the registry). Keys in.env:TICKETMASTER_API_KEY,TMDB_API_KEY.events/feeds.yaml— the ICS/RSS registry. Grows over time likereserves.mddid.events/seasonal.md— the anchor series table with next-announcement-check dates. Refreshed when a digest run notices a stale entry.events/weekly/YYYY-MM-DD.md— the digest itself, one per run, dated. Old ones are a record of what was considered, not maintained.- An
eventsskill (sibling ofplaces) that wraps the whole run: script pull → newsletter read → curation → digest write.
Digest shape (sections, in priority order):
- This weekend — the actual decision the digest serves. Each entry: what, when, where, drive time from home, kid-fit (both kids / Niko-only / date-night / solo), price, link.
- Next two weeks — same format, lighter.
- On the radar — on-sale dates and just-announced series. This is the section that earns the system its keep.
- Movies — in theaters now + releasing soon, flagged for kid-viability.
The family filter is the point. Generic event lists exist everywhere; the digest is worth running because it applies the constraints no aggregator knows: Shoreline drive radius, a 4-year-old and a 2-year-old (6pm shows good, late shows are date-night-with-sitter territory), Costco-runs-on-weekends rhythm, and taste (neo-soul/electronic bias for grown-up shows — the Ticketmaster genre filter can carry a taste profile).
Cadence and Trigger
Run Wednesday or Thursday — late enough to catch weekend announcements, early enough to actually plan (and book a sitter if it's a date-night pick).
Phase it: start as a manual /events skill invocation, weekly. Once the source mix stabilizes (2–3 runs to learn which sources actually produce the picks), promote it to a scheduled routine. One real constraint to know upfront: a scheduled cloud run can't use this machine's Chrome or local gws Gmail auth, so the newsletter-reading step ties the workflow to a local run unless it's re-plumbed. The Ticketmaster/TMDB/ICS layer is headless-safe either way. Manual-first is not a compromise — it's how places worked too, and the skill makes each run a one-liner.
Build Plan
Human blockers (yours, ~10 min each, one-time):
- Ticketmaster developer key (developer.ticketmaster.com)
- TMDB API key (themoviedb.org)
- Newsletter signups: EverOut weekly + ParentMap weekend, to the household Gmail
Claude work (one session, well under an hour):
scripts/events.shwithpull+ normalizationevents/feeds.yamlseeded with Seattle Center Trumba ICS + KCLS Shorelineevents/seasonal.mdseeded from the Tier 3 list above- The
eventsskill file, modeled onplaces/SKILL.md - First digest run as the shakedown
Open Questions
- ZooTunes ticketing coverage — the zoo sells through its own platform, so Ticketmaster may not carry it. If not, it lives in
seasonal.mdwith a direct check of zoo.org/zootunes. Verify on first pull. - EverOut long-term — newsletter-as-feed is robust, but if their curation is essential enough, the browser-session route (Claude in Chrome, logged in as you) is the fallback. Decide after seeing how much the newsletter alone yields.
- Blog integration — the
/guidepattern (markdown →places.json→ React page) would transfer, but events expire, so a public page is a freshness commitment the guide never had. Defer until the weekly digest has proven the pipeline; a "what's on in Seattle" page fed by an already-running weekly job is cheap, but only then. - Polly's view — the digest is vault-readable by design. Whether it should also land somewhere she already looks (shared calendar entries for picks? the Gmail thread?) is a workflow question to answer after a few runs, not a design decision now.
Revision — CI-First Architecture (2026-08-07, same day)
Discussion immediately after the first draft pivoted the architecture. Decisions, in order:
- CI-only, scheduled. Everything runs in GitHub Actions on cron; anything that can't run headless is cut from phase 1. The local
/eventsskill and the private family digest move to "later, maybe." - No separate events workflow. The cron trigger goes on the blog's existing
deploy.yml; events are fetched at build time andevents.jsonis never committed. If the build fails, Pages keeps serving the last deploy — graceful staleness for free. - Claude runs in the pipe after all — as a fail-soft curation stage via
claude -p, authenticated with the Max subscription (not API billing).
What the mechanical layer keeps without any curation: Ticketmaster's Family classification segment (kid-friendly as a filter, not a judgment), sales.public.startDateTime (the on-sale radar is structured data), TMDB certifications (G/PG flags). What only Claude adds: editorial picks and blurbs, plus EverOut/ParentMap coverage via WebSearch (EverOut 403s direct fetches, and datacenter IPs fare worse — search snippets route around it).
Auth (verified against docs, Aug 2026)
- Max bundles no API credits — the remembered "$100/month" is the Max 5x price itself. Optional "usage credits" add-on (Settings → Usage) allows overflow at API rates; leave it off.
claude setup-token→ one-year OAuth token → repo secretCLAUDE_CODE_OAUTH_TOKEN. Works withclaude -pin CI and withanthropics/claude-code-action. Draws on Max plan windows (5-hour + weekly, shared with interactive use); a weekly curation run is negligible against either.- Token is not read in
--baremode — run full mode (needed for WebSearch anyway). Calendar note: regenerate the token annually. - Rate-limit hit mid-run = step failure = mechanical page ships. No billing surprise possible with usage credits off.
- The remembered "$100/month for API use" was real but never shipped: on May 14, 2026 Anthropic announced an "Agent SDK credit" that would have moved
claude -p/ GitHub Actions / Agent SDK usage out of subscription pools into standalone monthly credits ($100/mo on Max 5x, billed at API rates, ~250 tasks). Paused June 15, 2026 before taking effect — those surfaces still draw from normal Max limits. Anthropic says a reworked version will come with advance notice. Watch item: if a revived version ships, the CI curation stage's economics change (from "free within Max limits" to "consumes a metered credit") — the fail-soft design absorbs any outcome, but re-check this doc's auth section when that announcement lands.
Pipeline spec (deploy.yml)
Triggers: push to main, workflow_dispatch, schedule: cron "0 14 * * 4" (Thu ~7am PT; GH cron is UTC and can slip minutes — irrelevant here).
1. node scripts/build-events.cjs # deterministic pull → src/data/events.json
2. claude -p (curation) # fail-soft → src/data/events-curated.json
3. node scripts/validate-curated.cjs # zod gate; on failure, delete curated file
4. astro build → deploy Pages # page works with or without curated layer
Stage 1 — build-events.cjs. Ticketmaster Discovery (geo radius around Seattle, next ~21 days, keep classification + onsale fields), TMDB now_playing + upcoming (region US, certifications), every feed in the committed ICS registry. Fail-soft per source — one feed being down must never block a blog-post deploy. Dedupe, sort, strip nothing-burger fields. Secrets: TICKETMASTER_API_KEY, TMDB_API_KEY.
Stage 2 — Claude curation. npx @anthropic-ai/claude-code -p with:
- Prompt in a committed file (
scripts/curate-events.md) so edits are versioned, not YAML archaeology. Input:events.json. Tasks: WebSearch the week's EverOut/ParentMap/Seattle's Child coverage; pick 5–10 highlights; one-sentence blurbs; surface on-sale alerts; tag family picks. - Output: strict JSON to
src/data/events-curated.jsononly. Guardrails: pinned model (Sonnet is plenty),--max-turnscap,timeout-minuteson the step,--allowedToolsscoped to WebSearch, WebFetch, Read, and Write restricted to the output path.continue-on-error: true. - Prompt explicitly forbids home-relative framing (drive times, neighborhood inference) — public page, public data only.
Stage 3 — validation gate. Zod schema at the boundary (the LLM step is untrusted input to the build). Invalid → delete the file, log loudly, build proceeds. The Astro page renders the curated layer only when the file exists and filters already-past events client-side, so stale data never shows expired listings.
Secrets total: TICKETMASTER_API_KEY, TMDB_API_KEY, CLAUDE_CODE_OAUTH_TOKEN — all free-tier or already-paid. Marginal cost of the whole system: $0/month.
Superseded from the original design
Newsletter-as-feed via Gmail, the Chrome fallback, the local weekly skill, and the events/weekly/ digest directory are all out of phase 1. events/seasonal.md survives but moves into the blog repo as a committed file the build merges in (free outdoor movie series and civic one-offs aren't in Ticketmaster). The private curated digest remains possible later as a thin local consumer of the same build-events.cjs output — nothing in the CI design blocks it.
Sources
- Ticketmaster Discovery API — getting started (free tier: 5,000 calls/day, 5 req/s)
- TMDB API —
now_playing/upcomingendpoints - Trumba calendar feed formats — append
.ics/.rss/.json - Seattle Center event calendar (Trumba-backed)
- KCLS events — Shoreline branch
- EverOut Seattle + newsletters
- ParentMap calendar + monthly family roundups
- Woodland Park Zoo ZooTunes — 2026 lineup coverage (Seattle Refined)
- Outdoor movies around Seattle 2026 (ParentMap) · Seattle's Child version
- Eventbrite search API deprecation (2020)