--- status: in-progress --- # Plan 003 — content mount Spec: `docs/specs/003-content-mount.md`. TDD: write the test, see it fail, implement, see it pass. - [x] 1. `src/shared/lrc.ts` — `parseLrc(text): { time: number; text: string }[]`. Tests in `tests/content.test.ts`: basic lines, `[mm:ss]` and `[mm:ss.xx]`/`[mm:ss.xxx]`, multiple stamps on one line, `[ar:…]`-style metadata skipped, CRLF, sorted output. - [x] 2. `src/content/library.ts` — `scanLibrary(dir)`: saga folders `EPIC - ()`, song files `… - NN - .flac`, sagas in story order (Troy, Cyclops, Ocean, Circe, Underworld, Thunder, Wisdom, Vengeance, Ithaca; unknown names after, by year then name), lyrics `.lrc` preferred else `.txt`, `folder.jpg` cover. URLs under `/content/files/` with each path segment URI-encoded. Fixture folder built in a temp dir by the test. - [x] 3. `src/content/homer.ts` — `splitBooks(text)`: between the Gutenberg START/END markers, headings `BOOK <roman>` (optional trailing dot), CRLF-safe; returns `{ book, heading, summary, paragraphs }[]`. Test with a small fixture string. - [x] 4. `src/content/routes.ts` — `contentResponse(req, dir | undefined): Response | null` handling the three routes, traversal guard, range requests (206 with Content-Range). Wire into `src/server.ts` `fetch()`. Route tests via `startServer` with `CONTENT_DIR` pointing at the fixture, and without it. - [x] 5. `.gitignore` / `.dockerignore`: ignore `epic-the-musical-content/`, `content/`, `*.flac`, `*.lrc`. - [x] 6. Docs: AGENTS.md (lyrics rule, content line in Architecture/Commands), `docs/ARCHITECTURE.md`, `docs/DECISIONS.md`, `docs/design/002-direction-round-notes.md` §7 run command. - [x] 7. Verify: `bun test`, `bun run typecheck`, `bun run build`; rebuild the preview container with the mount; curl library.json (9 sagas, 40 songs), a range request on a FLAC, an Odyssey book.