Files
Epic-The-Musical-Timeline/docs/plans/001-timeline-mockups.md
T

13 KiB
Raw Blame History

status
status
in-progress

EPIC Timeline — Repo bootstrap + 7 mockups Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: A Bun app, shipped as a Docker image, that serves an index page and 7 design-direction mockups. All of them share one sample dataset and carry fan-project credits. The repo is prepared for agents following the playbook.

Architecture: Bun.serve with HTML-import routes. bun build --target=bun bundles the server and every page into dist/, and the Docker image runs bun dist/server.js. Each mockup is a self-contained folder under src/mockups/NN-slug/ that imports the shared data, credits and audio modules.

Tech Stack: Bun 1.3.x (runtime, bundler, test runner), TypeScript, three.js (mockups 06/07 only), @fontsource/* for self-hosted fonts, Docker oven/bun:1.3.

Spec: docs/specs/001-timeline-mockups.md

Global Constraints

  • Every page (the index and all 7 mockups) shows the fan notice and the credit to Jorge Rivera-Herrans, both from src/shared/credits.ts.
  • Mockups, not full pages. Target a desktop viewport of 1440×900. No routing, persistence, or mobile polish.
  • Self-contained at runtime: no CDN scripts or fonts. Video slots are YouTube link cards. Songs may have an optional direct MP3.
  • three.js only in src/mockups/06-constellation/ and src/mockups/07-sea-voyage/.
  • No framework (Vite, React, Next…). Use plain TypeScript and the DOM plus Bun's bundler.
  • Sample data: Troy Saga (5 songs), Cyclops Saga (4 songs), and 1–2 later callback songs. Lyrics are placeholder lines. Facts that haven't been checked are marked // TODO(verify).
  • A mockup agent edits only its own folder src/mockups/NN-slug/. Shared files are fixed after Task 1.
  • Commits use Conventional Commits and end with Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>.

Review Focus

  1. Song with no audio (every sample song): the audio slot shows an empty state ("No audio yet"), never a broken <audio>. Task 1, test createAudioSlot empty state.
  2. Mockup URL without a trailing slash (/mockups/01-editorial) should still open the mockup. Task 1, smoke test.
  3. A motif that appears in only one song: selecting it highlights that song and draws no thread to nowhere. Task 1 data test: songsWithMotif returns ≥1 for every motif. Each mockup's own review checks it visually.
  4. WebGL unavailable in the 3D mockups: show a readable fallback message instead of a blank canvas. Tasks 5a and 5b, per their review checklist.
  5. The container is started with a custom PORT: the server respects process.env.PORT (default 3000). Task 6, docker run check.

Task 1: Foundation — Bun app, shared modules, sample data, mockup stubs

Files:

  • Create: package.json, tsconfig.json, bun.lock (via install), .gitignore, .dockerignore, Dockerfile
  • Create: src/server.ts, src/index.html, src/index.ts, src/index.css
  • Create: src/data/types.ts, src/data/sample.ts, src/data/query.ts
  • Create: src/shared/credits.ts, src/shared/credits.css, src/shared/audio-slot.ts, src/shared/video-cards.ts
  • Create: src/mockups/registry.ts and 7 stubs src/mockups/{01-editorial,02-vertical-story,03-transit-map,04-greek-frieze,05-score,06-constellation,07-sea-voyage}/{index.html,main.ts,style.css}
  • Test: tests/data.test.ts, tests/shared.test.ts, tests/server.test.ts

Interfaces (Produces; mockup agents rely on these exactly):

// src/data/types.ts
export type MotifKind = "motif" | "character";
export interface Motif { id: string; name: string; kind: MotifKind; color: string; description: string }
export interface Saga { id: string; name: string; order: number }
export interface OdysseyRef { book: number; lines: string; url: string }
export interface StoryTime { yearsAfterTroy: number; label: string }
export interface Part { name: string; singers: string[]; lines: string[]; motifIds: string[]; startSec?: number }
export interface Video { title: string; url: string }
export interface Song {
  id: string; title: string; sagaId: string; track: number;
  storyTime: StoryTime; odyssey: OdysseyRef | null;   // null = no Homer counterpart
  parts: Part[]; facts: string[]; videos: Video[]; audio?: string;
}
// src/data/sample.ts
export const sagas: Saga[]; export const motifs: Motif[]; export const songs: Song[]; // songs sorted by track
// src/data/query.ts
export function songsWithMotif(motifId: string): Song[];
export function motifsInSong(song: Song): Motif[];        // unique, in first-appearance order
export function gapYears(a: Song, b: Song): number;       // b.storyTime.yearsAfterTroy - a.storyTime.yearsAfterTroy
export function sagaOf(song: Song): Saga;
// src/shared/credits.ts
export const FAN_NOTICE: string;  // must contain "unofficial", "not affiliated", "Jorge Rivera-Herrans"
export function mountCredits(target?: Element): HTMLElement; // fills [data-fan-credits] or appends a <footer> to body
// src/shared/audio-slot.ts
export function createAudioSlot(song: Song): HTMLElement; // .audio-slot; empty state .audio-slot--empty; parts with startSec get seek buttons
// src/shared/video-cards.ts
export function createVideoCards(song: Song): HTMLElement; // .video-cards > a.video-card[target=_blank][rel=noopener]
// src/mockups/registry.ts
export interface MockupInfo { slug: string; number: number; name: string; pitch: string; tryThis: string }
export const mockups: MockupInfo[]; // 7 entries, slugs = folder names above

Shared modules use plain class names and minimal neutral CSS (credits.css), so each mockup restyles them.

  • Step 1: Scaffold. bun init -y, then delete the generated CLAUDE.md, README and index.ts. bun add three @fontsource/cormorant-garamond @fontsource/inter, bun add -d @types/three @types/bun typescript @happy-dom/global-registrator. Scripts: "dev": "bun --hot src/server.ts", "start": "bun dist/server.js", "build": "bun build src/server.ts --target=bun --production --outdir dist", "test": "bun test", "typecheck": "tsc --noEmit". .gitignore: node_modules/ dist/ .env* CLAUDE.local.md .worktrees/ screenshots/.
  • Step 2: Write the failing tests.
    • tests/data.test.ts: every song.sagaId is in sagas; every part.motifIds entry is in motifs; every motif has songsWithMotif(id).length >= 1; the songs include 5 with sagaId==="troy", 4 with "cyclops", and ≥1 other; every song has ≥1 part, ≥1 fact, ≥1 video; gapYears(songs[0], songs[1]) >= 0; ids are unique.
    • tests/shared.test.ts (register GlobalRegistrator from @happy-dom/global-registrator in the test file): FAN_NOTICE contains the three required phrases; mountCredits() puts the notice into document.body; createAudioSlot empty state: a song without audio → the element has the class audio-slot--empty and no audio element; a song with audio and one part with startSec: 30 → one <audio src> and one button[data-start="30"]; createVideoCards → one a.video-card per video with rel containing noopener.
    • tests/server.test.ts: start the server via startServer(0) (export startServer(port: number): Server from src/server.ts; the module calls it with Number(process.env.PORT ?? 3000) only when import.meta.main). Assert GET / → 200, and the HTML contains every registry slug link; for each slug, GET /mockups/<slug>/ → 200 with data-fan-credits, and GET /mockups/<slug> → 200 or a redirect to the slash form.
  • Step 3: Run bun test. Expected: FAIL (modules missing).
  • Step 4: Implement. Write the types, query functions, credits, audio slot and video cards. server.ts uses routes with HTML imports for / and each /mockups/<slug>/, and a redirect for the no-slash form. Index page: a title, the 7 cards from registry (number, name, pitch, "try this"), and the credits. Each stub: <footer data-fan-credits>, main.ts calls mountCredits() and renders the mockup's name and "stub". Write the sample data (see the spec's data section). Odyssey URLs point at a public-domain translation (e.g. Butler on Project Gutenberg / Wikisource). Story times and Odyssey refs that haven't been checked carry // TODO(verify). Songs: Troy — The Horse and the Infant, Just a Man, Full Speed Ahead, Open Arms, Warrior of the Mind. Cyclops — Polyphemus, Survive, Remember Them, My Goodbye. Callbacks — at least one later song that reuses a Troy or Cyclops motif. Motifs: at least 4 musical motifs and 3 character themes, each with a distinct colour.
  • Step 5: Run bun test && bun run typecheck. Expected: all pass, 0 type errors.
  • Step 6: Verify the build. bun run build && PORT=3999 bun dist/server.js &, then curl -s -o /dev/null -w '%{http_code}' localhost:3999/mockups/01-editorial/ → 200. Kill the server.
  • Step 7: Commit. git add -A && git commit -m "feat: bun app foundation, sample data, shared credits/audio, mockup stubs"

Task 2: Agent-ready repo docs (playbook)

Files: Create AGENTS.md, CLAUDE.md, GEMINI.md, README.md, PRODUCT.md, DESIGN.md, docs/{ARCHITECTURE,DECISIONS,TODO,PERMISSIONS}.md, docs/specs/README.md, docs/plans/README.md, .claude/settings.json, .codex/config.toml, .codex/rules/project.rules, .gemini/settings.json.

  • Step 1: Follow /home/user/preparing-repos-for-agents/skill/preparing-repos-for-agents/SKILL.md §2–§8. Re-run each candidate command in a fresh shell (bun install --frozen-lockfile, bun test, bun run typecheck, bun run build, bun run dev, docker build -t epic-timeline .) and record each one in the Verified table.
  • Step 2: Deviations to encode. Mockups live in src/mockups/, are committed and served (user decision, overriding the template's gitignored design/mockups/). Mockup agents edit only their own folder. The spec uses NNN-slug naming. DESIGN.md says the visual direction is open until a mockup is chosen. PRODUCT.md covers the fan project, the credit, the audience, lyrics + MP3 + YouTube. There is no CI yet, so delete the CI line.
  • Step 3: Deletion pass (§8). wc -l AGENTS.md < 150.
  • Step 4: Commit. git commit -m "chore: bootstrap agent-ready docs and rules"

Tasks 3–5: Mockups in batches (subagents in parallel, one per mockup)

Batch A (Task 3): 01 Editorial, 02 Vertical story, 03 Transit map. Batch B (Task 4): 04 Greek frieze, 05 Score/waveform. Batch C (Task 5): 06 Constellation, 07 Sea voyage.

Per-mockup brief (each agent gets this plus its row from the spec's directions table):

  • Files: only src/mockups/<slug>/ (any number of files inside it).

  • Consumes: Task 1 interfaces (songs, sagas, motifs, songsWithMotif, motifsInSong, gapYears, sagaOf, mountCredits, createAudioSlot, createVideoCards).

  • Must show (from the spec): the direction's "Must show" column; a song detail state (Odyssey ref with link, story time, parts with singers and placeholder lines, motif chips per part, a fact, video cards, audio slot); motif selection highlighting every song and part where the motif appears; visible credits.

  • Gap spacing between songs is proportional to gapYears (or visibly labelled with it) in mockups 01, 02, 03 and 07.

  • Use the frontend-design skill. Commit fully to the direction's aesthetic, with bundled fonts only.

  • 06/07: three.js. If WebGL is unavailable, show a styled fallback message (Review Focus #4).

  • Verify: bun test && bun run typecheck && bun run build pass. Start bun dist/server.js, take headless screenshots (chromium --headless --screenshot or Playwright, 1440×900) of the default state and the song-open state into the gitignored screenshots/, and look at them before reporting.

  • Don't commit. The controller reviews and commits each batch.

  • Task 3: dispatch batch A in parallel → review each against the brief (spec compliance + visual check of the screenshots) → fix → git commit -m "feat(mockups): 01 editorial, 02 vertical story, 03 transit map"

  • Task 4: batch B, same loop → git commit -m "feat(mockups): 04 greek frieze, 05 score"

  • Task 5: batch C, same loop → git commit -m "feat(mockups): 06 constellation, 07 sea voyage (three.js)"

Task 6: Docker verification, final review, push

  • Step 1: docker build -t epic-timeline . → succeeds.
  • Step 2: docker run -d --rm -e PORT=8080 -p 8080:8080 --name epic-tl epic-timeline. curl / and all 7 /mockups/<slug>/ → 200. docker stop epic-tl.
  • Step 3: Run a whole-branch review (superpowers:requesting-code-review). Fix findings. Update docs/TODO.md with the TODO(verify) data follow-ups.
  • Step 4: git rm docs/plans/001-timeline-mockups.md, commit, set the spec's status: implemented, then git push -u origin main.