Files
yellowjacket/.planning/phases/11-per-library-scan-pipeline/11-03-PLAN.md
T

183 lines
7.5 KiB
Markdown

---
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"
---
<objective>
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.
</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/phases/11-per-library-scan-pipeline/11-CONTEXT.md
@.planning/phases/11-per-library-scan-pipeline/11-01-SUMMARY.md
<interfaces>
<!-- From Plan 01 -->
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
})
}
```
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Wire auto-scan on startup and clean up legacy single-directory code</name>
<files>
backend/app.go
backend/library/library.go
</files>
<action>
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
</action>
<verify>
<automated>cd /mnt/vault/dev/golang/yellowjacket && go build ./... && go vet ./backend/... && go test ./backend/library/... -count=1 -timeout 60s</automated>
</verify>
<done>
- 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
</done>
</task>
</tasks>
<verification>
```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)
```
</verification>
<success_criteria>
- 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
</success_criteria>
<output>
After completion, create `.planning/phases/11-per-library-scan-pipeline/11-03-SUMMARY.md`
</output>