docs(06): create phase plan — 3 plans for SQL consolidation, event codegen, and SAFETY comments
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
---
|
||||
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>
|
||||
Reference in New Issue
Block a user