--- phase: 11-per-library-scan-pipeline plan: 03 type: execute wave: 2 depends_on: ["11-01"] files_modified: - backend/app.go - backend/library/library.go autonomous: true requirements: [LSCAN-01, LSCAN-02] must_haves: truths: - "App auto-scans all libraries on launch using ScanAllLibraries" - "Legacy LibraryConfigChanged event handler is removed or updated for multi-library" - "Library struct no longer requires Config.DirectoryPath to function" artifacts: - path: "backend/app.go" provides: "Updated OnDomReady or OnStartup to trigger ScanAllLibraries on launch" - path: "backend/library/library.go" provides: "Updated NewLibrary constructor — Config no longer required" key_links: - from: "backend/app.go" to: "backend/library/scan_queue.go" via: "ScanAllLibraries call on startup" pattern: "library\\.ScanAllLibraries" --- Wire the per-library scan pipeline into app startup and clean up legacy single-directory scanning paths. Purpose: Ensure auto-scan on launch uses `ScanAllLibraries()` (same codepath as the UI button per CONTEXT.md), and remove/update legacy `LibraryConfigChanged` handler that assumed a single directory. Output: Updated app.go startup wiring, cleaned-up Library constructor. @/home/caleb/.config/opencode/get-shit-done/workflows/execute-plan.md @/home/caleb/.config/opencode/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/11-per-library-scan-pipeline/11-CONTEXT.md @.planning/phases/11-per-library-scan-pipeline/11-01-SUMMARY.md From backend/library/scan_queue.go (created in Plan 01): ```go func (l *Library) ScanLibrary(id int64) error func (l *Library) ScanAllLibraries() error func (l *Library) CancelCurrentScan() func (l *Library) CancelAllScans() ``` From backend/app.go (current): ```go func (yj *YellowJacketApp) OnStartup(ctx context.Context) // Currently: yj.library.SetContext(ctx) // Currently: library is created with appConfig.Library (Config with DirectoryPath) func NewYellowJacketApp(...) { lib, err := library.NewLibrary( yjApp.appContext, yjApp.logger, yjApp.appConfig.Library, // Config with DirectoryPath yjApp.database, ) } ``` From backend/library/library.go (current event handler): ```go func (l *Library) registerEventHandlers() { runtime.EventsOn(l.ctx, events.LibraryConfigChanged, func(data ...any) { // Parses DirectoryPath from event data, calls l.handleConfigUpdate }) } ``` Task 1: Wire auto-scan on startup and clean up legacy single-directory code backend/app.go backend/library/library.go 1. **Update `NewLibrary` constructor** in `backend/library/library.go`: - Make `*Config` parameter optional/removable. The Library no longer needs a pre-configured DirectoryPath because scan paths come from the database. - Keep the `*Config` parameter for backward compatibility but don't require `DirectoryPath` to be set. - Update validation: if `conf` is nil, create a default config with empty DirectoryPath (already handled). 2. **Update `registerEventHandlers`** in `backend/library/library.go`: - Remove the `LibraryConfigChanged` event handler entirely. This handler assumed a single-directory model where changing the config triggers a scan. In the multi-library model: - Libraries are added/removed through the library CRUD API (Phase 12) - Scanning is triggered explicitly via `ScanLibrary()` or `ScanAllLibraries()` - The `LibraryConfigChanged` event and `handleConfigUpdate` method can be deleted or marked deprecated - Delete `handleConfigUpdate` method - Delete `errLibraryDirNotConfigured` sentinel error (no longer needed) 3. **Update `NewYellowJacketApp` in `backend/app.go`**: - Change the `library.NewLibrary(...)` call. The Config parameter is less important now since DirectoryPath is ignored. Pass `yjApp.appConfig.Library` as before (it still has ScanConcurrency which is useful as a default). 4. **Add auto-scan on startup** in `backend/app.go`: - In `OnDomReady` (or via a goroutine started in `OnStartup` that waits for DOM ready), trigger auto-scan. - Best approach: In `OnDomReady`, after the startup error check, launch a goroutine: ```go go func() { if err := yj.library.ScanAllLibraries(); err != nil { yj.logger.Error("auto-scan failed", "err", err) } }() ``` - This uses the same `ScanAllLibraries()` codepath as the UI button (per CONTEXT.md: "Auto-scan on launch should use the same ScanAllLibraries() codepath as the UI button — single implementation"). - It runs in a goroutine so it doesn't block the DOM ready callback. - Only run if there are libraries in the DB: check `l.db.Queries.CountLibraries(l.ctx)` first (or let ScanAllLibraries handle the empty case gracefully by returning immediately when GetAllLibraries returns an empty slice). 5. **Clean up legacy `Scan()` method**: - In Plan 01, the old `Scan()` was kept as backward-compatible wrapper. Now review: since we're removing `handleConfigUpdate` which was the only caller of the legacy `Scan()` via `l.handleConfigUpdate → l.Scan()`, we can either: - Keep `Scan()` for tests (it's used in `scan_test.go`) - Update it to call `scanInternal` with the library from `l.conf.DirectoryPath` if set, or return early if not set - Keep `FullRescan()` — it's still called from the config-page UI. It should work with the first/default library. Update it to look up the default library from DB rather than using `l.conf.DirectoryPath`. 6. **Update `FullRescan()`** in `backend/library/rescan.go`: - Instead of using `l.conf.DirectoryPath`, look up the first library from DB: `libs, err := l.db.Queries.GetAllLibraries(l.ctx)` and use `libs[0]`. - If no libraries exist, return an error. - Call `scanInternal(lib.ID, lib.Name, lib.Path)` instead of `l.Scan()`. - Per-library FullRescan will be added in Phase 12 — for now this rescans the first/only library. **Linting requirements:** - Doc comments ending with period - Blank line after early returns - Lines under 100 chars cd /mnt/vault/dev/golang/yellowjacket && go build ./... && go vet ./backend/... && go test ./backend/library/... -count=1 -timeout 60s - Auto-scan on startup calls ScanAllLibraries (same codepath as UI button) - Legacy LibraryConfigChanged handler removed - Legacy handleConfigUpdate removed - FullRescan uses library from DB instead of config DirectoryPath - go build passes, go vet passes, existing tests pass ```bash # Full build go build ./... # Vet go vet ./backend/... # Tests pass (including scan_test.go) go test ./backend/library/... -count=1 -timeout 60s # No references to removed handler grep -rn "LibraryConfigChanged" backend/library/ | grep -v "_test.go" # Should return no hits (only events.go constant definition, not handler registration) ``` - App auto-scans all libraries on launch via ScanAllLibraries - LibraryConfigChanged handler removed from library package - handleConfigUpdate removed - FullRescan works with DB-sourced library (not config DirectoryPath) - All tests pass, build passes After completion, create `.planning/phases/11-per-library-scan-pipeline/11-03-SUMMARY.md`