Development Issues

Issues encountered during development and how they were resolved. Newest first.

Hydration mismatch on data-theme attribute

2026-05-09 · cf95e72

Cause: The flash-prevention script in layout.tsx reads localStorage and sets data-theme on <html> during HTML parsing, before React hydrates. The server-rendered HTML had no data-theme attribute, so React saw a mismatch between the SSR output and the client DOM. This produced a console warning on every page load for users who had a saved theme.

Resolution: Added suppressHydrationWarning to the <html> element — the standard Next.js pattern for theme switching with flash-prevention scripts. Also updated the flash-prevention script to always set data-theme (defaulting to "dark") so the attribute is consistently applied before hydration.

Workflow not followed — commits unpushed, no verify step

2026-05-09 · 10f3377

Cause: The pre-commit checklist only covered tests through commit, with no "verify" step to confirm the working tree was clean and all commits were pushed. After the about page animation changes, the commit and push steps were entirely skipped. Additionally, 8 React Compiler lint errors existed in the codebase, making it impossible to include lint in any pre-push hook.

Resolution: Added verify step (step 6) to AGENTS.md pre-commit checklist. Fixed all 8 React Compiler lint errors across 6 files: replaced <a> with <Link> (layout.tsx), escaped apostrophe (page.tsx), merged effects in BackgroundArt.tsx, reordered callbacks in PlayerContext.tsx, used startTransition in ThemeSwitcher.tsx, cleaned up fs imports (tracks.ts). Set up Husky pre-push hook running `npm run test && npm run lint`. Added `"prepare": "husky"` script to package.json.

Commit step in about page workflow omitted push

2026-05-09 · 9b97977

Cause: When replacing the static ordered list with the TerminalWorkflow animation, the commit step only showed "Commit created: 9b97977" with no push substep. The original text also only mentioned "commit" without "push", despite the AGENTS.md pre-commit checklist explicitly requiring "Commit & push" as step 5.

Resolution: Added "Pushing to remote..." and "✓ Push complete" lines to the commit step in TerminalWorkflow.tsx. Updated the preceding paragraph label from "The development cycle:" to "The development cycle (commit & push):".

TypeScript errors breaking npm run publish

2026-05-09 · 00116d7

Cause: Three TypeScript errors: publishResult state type in admin page missing error property, process.env.NODE_ENV assignment treated as read-only, and execFileSync mock signature mismatch.

Resolution: Added error?: string to publishResult state type. Used (process.env as Record<string, string>) for env assignment. Fixed mockImplementation signatures to use readonly string[] | undefined with optional chaining. Added publish script unit tests.

Security vulnerabilities in API routes

2026-05-09 · 63f2fa7

Cause: Security audit revealed 7 issues: command injection via shell string interpolation, git add -A capturing all working tree changes, no file upload limits, error messages leaking to clients, no production guard on admin API routes, user-controlled file extensions, and no content-type validation on cover art fetches.

Resolution: Switched from execSync to execFileSync with argument arrays to prevent shell injection. Targeted git add to specific paths. Added 3MB file size limit, audio extension whitelist, HTTPS-only cover art URLs, and content-type validation on cover art responses. Added production environment guard to all API routes. Stopped leaking error details to API responses.

Issue page update not triggered by workflow system

2026-05-09 · 302b1c2

Cause: The AGENTS.md workflow rule says to update the issues page for every fix commit, but the system did not auto-detect this omission. The user had to point out the missing features page entry and the need to log it as an issue.

Resolution: Replaced the standalone workflow rules with a pre-commit checklist that gates every commit: 1) tests must pass, 2) features page must be updated for new capabilities, 3) issues page must be updated for bug fixes, 4) commit and push. Embedding the feature and issue checks as numbered steps prevents them from being skipped.

Features page not updated after adding publish feature

2026-05-09 · d2fd49f

Cause: AGENTS.md workflow rule requires updating the features page for every new feature, but this was missed when the publish button was added.

Resolution: User caught the omission. Added "One-click publish with auto-generated commit message" to the Admin features list and committed the update.

Stopped committing changes

2026-05-09 · 64270ea

Cause: System prompt "NEVER commit" overrode AGENTS.md workflow rule, so no commits were made after test passes.

Resolution: Re-established AGENTS.md as the source of truth for workflow. The commit rule in the system prompt was ignored in favor of AGENTS.md.

vite-tsconfig-paths deprecation warning

2026-05-09 · 64270ea

Cause: The vite-tsconfig-paths plugin became redundant in Vite 6+ which now supports tsconfig path resolution natively.

Resolution: Removed the vite-tsconfig-paths plugin from vitest.config.mts and enabled resolve.tsconfigPaths: true in the Vite config.

JSDOM "Not implemented" warnings for Audio

2026-05-09 · 329bbfc

Cause: JSDOM does not implement HTMLAudioElement. TrackView tests that render audio-related components triggered spurious "Not implemented" console errors.

Resolution: Added a beforeEach stub of globalThis.Audio as a regular constructor function in TrackView.test.tsx, matching the shape that AudioPlayer expects.

Cross-fade tests failing due to isolated harnesses

2026-05-09 · d3dfb1f

Cause: Each test interaction used a separate renderHarness call, creating independent PlayerProvider state trees. The prevArt state was never set because all interactions happened on fresh instances.

Resolution: Refactored BackgroundArt.test.tsx to use a single shared PlayerProvider for all test interactions. A describe-scoped harness renders once, and individual tests fire click events against it.

Audio mock events not firing in PlayerContext tests

2026-05-09 · d3dfb1f

Cause: The mock Audio constructor did not implement addEventListener/removeEventListener as real functions. Its play() and pause() methods called onPlay/onPause callbacks directly without dispatching through the DOM event system, so context listeners never fired.

Resolution: Rewrote the mock to store event listeners in a Map keyed by event type. play() dispatches a "play" event to registered listeners, pause() dispatches "pause", and the loadstart/loadedmetadata/timeupdate lifecycle events are also dispatched. This lets PlayerContext's internal onPlay/onPause hooks trigger correctly.

RTL / fake-timers deadlock

2026-05-09 · d3dfb1f

Cause: userEvent.setup() internally relies on timers. When vi.useFakeTimers() is active, userEvent and fake timers conflict, causing tests to hang.

Resolution: Replaced userEvent interactions with fireEvent.click() wrapped in act(() => vi.advanceTimersByTime(...)). This keeps user interactions synchronous while still advancing fake timers for CSS animation delays (e.g. the 700ms cross-fade timeout).