Files
yellowjacket/.planning/phases/06-sql-consolidation-code-quality/06-02-PLAN.md
T

10 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
06-sql-consolidation-code-quality 02 execute 1
backend/events/events.go
backend/events/cmd/genevents/main.go
frontend/src/events.ts
lefthook.yml
true
QUAL-02
truths artifacts key_links
Running `go generate ./backend/events/...` produces frontend/src/events.ts that exactly matches the Go constants
The generated events.ts includes LibraryConfigChanged (currently missing from hand-maintained TS file)
The codegen-check pre-commit hook detects stale events.ts and fails
Output is deterministic — running the generator twice produces identical output
path provides contains
backend/events/cmd/genevents/main.go Go→TypeScript event constant generator go/ast
path provides contains
backend/events/events.go go:generate directive for event codegen go:generate
path provides contains
frontend/src/events.ts Generated TypeScript event constants LibraryConfigChanged
from to via pattern
backend/events/events.go frontend/src/events.ts go:generate directive running genevents go:generate go run
from to via pattern
lefthook.yml go generate codegen-check pre-commit hook go generate
Build a Go code generator that reads event constants from `backend/events/events.go` using `go/ast` and produces `frontend/src/events.ts`, then wire it into `go generate` and the pre-commit hook.

Purpose: Eliminate manual synchronization of event names between Go and TypeScript. The generator automatically catches drift (like the missing LibraryConfigChanged) and the pre-commit hook prevents stale files from being committed.

Output: Generator tool, //go:generate directive, updated events.ts with missing constant, working codegen-check hook.

<execution_context> @/home/caleb/.config/opencode/get-shit-done/workflows/execute-plan.md @/home/caleb/.config/opencode/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/06-sql-consolidation-code-quality/06-RESEARCH.md

@backend/events/events.go @frontend/src/events.ts @lefthook.yml

From backend/events/events.go (21 constants in 5 groups):

// Playback events (backend → frontend push).
const (
    PlaybackStateChanged = "PlaybackStateChanged"
    PlaybackFinished     = "PlaybackFinished"
    TrackChanged         = "TrackChanged"
    SeekFailed           = "SeekFailed"
    VolumeChanged        = "VolumeChanged"
)
// Queue events (backend → frontend push).
const (
    QueueChanged        = "QueueChanged"
    QueueIndexChanged   = "QueueIndexChanged"
    QueueModeChanged    = "QueueModeChanged"
    QueueTracksModified = "QueueTracksModified"
)
// Config events.
const (
    LibraryConfigChanged   = "LibraryConfigChanged"   // <-- MISSING from TS
    ThemeConfigChanged     = "ThemeConfigChanged"
    TrackListConfigChanged = "TrackListConfigChanged"
    FavoritesConfigChanged = "FavoritesConfigChanged"
)
// Playlist events.
const (
    PlaylistCreated        = "PlaylistCreated"
    PlaylistDeleted        = "PlaylistDeleted"
    PlaylistRenamed        = "PlaylistRenamed"
    PlaylistTracksChanged  = "PlaylistTracksChanged"
    PlaylistsRestored      = "PlaylistsRestored"
    DefaultPlaylistChanged = "DefaultPlaylistChanged"
)
// Library events.
const (
    LibraryScanStarted  = "LibraryScanStarted"
    LibraryScanComplete = "LibraryScanComplete"
)

From frontend/src/events.ts (20 constants — missing LibraryConfigChanged):

  • Format: export const Events = { ... } as const;
  • Followed by: export type EventName = (typeof Events)[keyof typeof Events];
  • Comment groups match Go groups (Playback, Queue, Playlist, Config, Library)

From lefthook.yml:

  • codegen-check hook runs go generate ./... then checks git diff --name-only
  • Hook currently hangs per STATE.md but research shows go generate ./... now completes in <1s

Existing go:generate directives:

  • backend/app.go:4//go:generate go tool templ generate
  • backend/database/database.go:21//go:generate go tool sqlc generate
Task 1: Create event codegen tool backend/events/cmd/genevents/main.go backend/events/events.go 1. Create `backend/events/cmd/genevents/main.go` — a standalone Go program (package main) that: - Uses `go/ast`, `go/parser`, `go/token` to parse `events.go` in the same directory as the source - Accepts a `-source` flag (path to events.go, default: the events.go file relative to the generator location) and an `-output` flag (path to output .ts file) - Walks the AST in declaration order (NOT map iteration — deterministic output is critical) - For each `const` block: extracts the doc comment above the block (e.g., "// Playback events (backend → frontend push).") and each constant name + string value - Generates TypeScript output matching the current `events.ts` format exactly: ```typescript // Code generated by genevents from backend/events/events.go. DO NOT EDIT.
     export const Events = {
         // Playback events (backend → frontend push)
         PlaybackStateChanged: "PlaybackStateChanged",
         ...
     } as const;

     export type EventName = (typeof Events)[keyof typeof Events];
     ```
   - Preserves comment group separation with blank lines between groups
   - Strips the trailing period from Go doc comments (Go convention) for TypeScript comments
   - Writes output atomically (write to temp file, then rename)

2. Add `//go:generate` directive to `backend/events/events.go`:
   ```go
   //go:generate go run ./cmd/genevents -source events.go -output ../../frontend/src/events.ts
   ```
   Place it after the package doc comment and before the first const block. Use a relative path from the events package directory to the frontend output.

3. Run `go generate ./backend/events/...` and verify the output matches the expected format.

4. Verify the generated events.ts now includes `LibraryConfigChanged` (the constant missing from the hand-maintained file).

**Key constraint:** AST iteration must be in source declaration order (iterate `f.Decls` directly, NOT collect into a map). This ensures deterministic output so the codegen-check hook doesn't produce false diffs.
go generate ./backend/events/... && diff <(cat frontend/src/events.ts) <(go run ./backend/events/cmd/genevents -source backend/events/events.go -output /dev/stdout) && echo "Deterministic OK" && grep -q "LibraryConfigChanged" frontend/src/events.ts && echo "Missing constant fixed" - Generator exists at backend/events/cmd/genevents/main.go - `//go:generate` directive added to events.go - Running `go generate ./backend/events/...` produces valid events.ts - Output includes all 21 constants (including LibraryConfigChanged) - Output is deterministic (running twice produces identical files) - Comment groups match Go source ordering Task 2: Wire codegen-check pre-commit hook lefthook.yml 1. The existing `codegen-check` hook in `lefthook.yml` already runs `go generate ./...` and diffs. Per research, `go generate ./...` now completes in <1 second (previous hanging appears resolved). The hook structure should work as-is with the new event generator wired in.
2. Test the hook end-to-end:
   - Run `go generate ./...` and verify it completes quickly (<5 seconds)
   - Verify no unstaged changes exist after generation (all generated code is up-to-date)
   - Manually introduce a drift: add a test constant to events.go, verify `go generate` updates events.ts, then verify the hook would detect the diff

3. If the hook still hangs (unlikely per research): narrow the `codegen-check` glob to only trigger on event-related files, or split into a separate event-specific check. Update lefthook.yml accordingly.

4. Run the full pre-commit hook to verify all hooks pass:
   ```bash
   LEFTHOOK=1 lefthook run pre-commit
   ```
   Note: If the hook takes >10 seconds, investigate and optimize. Expected: <5s total.

5. Clean up any test changes (remove test constant if added).

**Important:** The hook runs `go generate ./...` which triggers ALL generators (templ, sqlc, events). This is the correct behavior — it ensures all generated code is fresh. The <1s completion time makes this acceptable.
go generate ./... && test -z "$(git diff --name-only)" && echo "codegen-check would pass" - `go generate ./...` completes in <5 seconds - codegen-check hook detects stale events.ts (adding Go constant without regenerating TS fails the hook) - All existing pre-commit hooks still pass - No leftover test changes in the working tree ```bash # 1. Generator produces valid output go generate ./backend/events/...

2. Output includes all 21 constants

grep -c ":" frontend/src/events.ts # Should be 21+ (constants + type line)

3. LibraryConfigChanged is present

grep "LibraryConfigChanged" frontend/src/events.ts

4. Deterministic output

go generate ./backend/events/... git diff --name-only # Should be empty (no changes on second run)

5. Full generate works

go generate ./...

6. Frontend typecheck passes with new events.ts

cd frontend && ./node_modules/.bin/tsc --noEmit

7. Full build

go build -tags webkit2_41 ./...

</verification>

<success_criteria>
- Event codegen tool parses Go constants and generates matching TypeScript
- LibraryConfigChanged gap is automatically fixed
- `go generate` directive wired into events.go
- codegen-check hook works end-to-end (detects drift, passes when clean)
- Frontend TypeScript compiles with generated events.ts
- Output is deterministic across runs
</success_criteria>

<output>
After completion, create `.planning/phases/06-sql-consolidation-code-quality/06-02-SUMMARY.md`
</output>