254 lines
10 KiB
Markdown
254 lines
10 KiB
Markdown
---
|
|
phase: 06-sql-consolidation-code-quality
|
|
plan: 02
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- backend/events/events.go
|
|
- backend/events/cmd/genevents/main.go
|
|
- frontend/src/events.ts
|
|
- lefthook.yml
|
|
autonomous: true
|
|
requirements: [QUAL-02]
|
|
|
|
must_haves:
|
|
truths:
|
|
- "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"
|
|
artifacts:
|
|
- path: "backend/events/cmd/genevents/main.go"
|
|
provides: "Go→TypeScript event constant generator"
|
|
contains: "go/ast"
|
|
- path: "backend/events/events.go"
|
|
provides: "go:generate directive for event codegen"
|
|
contains: "go:generate"
|
|
- path: "frontend/src/events.ts"
|
|
provides: "Generated TypeScript event constants"
|
|
contains: "LibraryConfigChanged"
|
|
key_links:
|
|
- from: "backend/events/events.go"
|
|
to: "frontend/src/events.ts"
|
|
via: "go:generate directive running genevents"
|
|
pattern: "go:generate go run"
|
|
- from: "lefthook.yml"
|
|
to: "go generate"
|
|
via: "codegen-check pre-commit hook"
|
|
pattern: "go generate"
|
|
---
|
|
|
|
<objective>
|
|
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.
|
|
</objective>
|
|
|
|
<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>
|
|
|
|
<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
|
|
|
|
<interfaces>
|
|
<!-- Current event constants for reference -->
|
|
|
|
From backend/events/events.go (21 constants in 5 groups):
|
|
```go
|
|
// 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`
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Create event codegen tool</name>
|
|
<files>
|
|
backend/events/cmd/genevents/main.go
|
|
backend/events/events.go
|
|
</files>
|
|
<action>
|
|
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.
|
|
</action>
|
|
<verify>
|
|
<automated>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"</automated>
|
|
</verify>
|
|
<done>
|
|
- 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
|
|
</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Wire codegen-check pre-commit hook</name>
|
|
<files>
|
|
lefthook.yml
|
|
</files>
|
|
<action>
|
|
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.
|
|
</action>
|
|
<verify>
|
|
<automated>go generate ./... && test -z "$(git diff --name-only)" && echo "codegen-check would pass"</automated>
|
|
</verify>
|
|
<done>
|
|
- `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
|
|
</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
```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>
|