The change feed dropped every row older than 30 days, and an app syncing
from cursor 0 builds its library from the feed, so a fresh install, cleared
data or a 410 reset got only what had changed lately.
The compactor now keeps each entity's latest change however old. It drops a
row once a newer one for the same entity exists, and a delete once it is 30
days old, raising a stored mark (library_changes_trimmed) past it; an app
whose cursor is below the mark is sent back to 0, as before. Migration 0101
adds the index the compactor needs, seeds the mark where the old compactor
left off, and gives every artist, album and track whose latest row is not an
upsert a fresh one, so the rows the old compactor dropped come back. The
backup carries the mark with the feed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment blocks in cmd/minstrel and 30 smaller internal packages, rewritten
to rule 32's standard with each citation read in context: task, milestone
and rule ids go, and so do the stories they were attached to ("used to",
"Before #N", measurements from one install, quotes, pointers to Fabled
Librarian's records and to docs/superpowers files that are not in the
repo). Where a story wrapped a real constraint, the constraint stays as a
present-tense fact.
Comments that had gone stale are corrected:
- taste.Config: the tuned values are overlaid by internal/recsettings;
it is no longer "a later follow-up".
- StorageConfig: the data dir holds art, caches and the stream secret,
not only playlist covers.
- the Subsonic album list and getUser comments describe what they do
today rather than a milestone that would change it.
Checked: the non-comment token stream of every changed file matches
HEAD, and gofmt -l is empty.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closes the last deferred follow-up from #357. The library_changes
table is the append-only change log that drives /api/library/sync's
delta semantics — every mutation (scanner upsert, like, playlist
edit, track delete) writes one row. Without a retention policy the
table grows unbounded; the original migration (0025) called out the
follow-up explicitly.
New goroutine: sync.Compactor runs daily, deletes rows where
changed_at < now - 30 days. Logs a row count when non-zero so
operators can see compaction activity in the journal. First tick
fires on startup so a process that hasn't been compacted in a
while catches up immediately.
30-day retention matches the offline-mode spec
(docs/superpowers/specs/2026-05-09-flutter-offline-mode-design.md).
Clients with a cursor older than that hit the existing 410 fallback
path and resync from scratch.
Imported as syncpkg in main.go to follow the existing convention
(see internal/library/scanner.go).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>