docs: mark spec 001 implemented, remove transient plan

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Lucas Winther
2026-10-23 19:14:49 +02:00
co-authored by Claude Opus 5.5
parent 2ff58ace46
commit 9d5f2ef647
2 changed files with 1 additions and 127 deletions
-126
View File
@@ -1,126 +0,0 @@
---
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 <[email protected]>`.
## 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):**
```ts
// 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`.
+1 -1
View File
@@ -1,5 +1,5 @@
---
status: approved
status: implemented
---
# EPIC: The Musical Timeline — Repo bootstrap and 7 design-direction mockups