# Persistent Library Snapshot — Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Make the desktop GUI render the last-known library instantly at launch from a persisted snapshot, then background-rescan and swap in the fresh result. **Architecture:** Serialize the scanned `Vec` to a JSON blob in a new one-row-per-root SQLite table. On desktop launch, load and seed `self.library` before spawning the (unchanged) background scan thread. The scan stays authoritative and swaps in truth via the existing `library_load_rx` drain, so the snapshot is a display optimization that can only be briefly stale, never wrong. **Tech Stack:** Rust, rusqlite (r2d2 pool), serde / serde_json (already dependencies), eframe/egui. ## Global Constraints - Desktop GUI only. Do **not** touch `web.rs` startup or the web in-memory snapshot machinery. - Snapshot writes and loads are **error-swallowing / non-fatal** — a failure just means no fresh snapshot next launch (same tolerance as `info_cache_put_many`). - The background scan runs unchanged and remains authoritative. - New DB schema goes in `init_schema()` as idempotent `CREATE TABLE IF NOT EXISTS` (no migration framework in this repo). - Never commit `cookies.txt`, `config.toml`, or `catacomb.db` (gitignored). - Commits are SSH-signed: `export SSH_AUTH_SOCK=/tmp/luna-ssh-agent.sock` before `git commit`. End commit messages with `Co-Authored-By: Claude Opus 4.8 `. - Spec: `docs/superpowers/specs/2026-07-09-library-snapshot-instant-startup-design.md`. --- ### Task 1: DB snapshot persistence layer Make the library structs serializable and add save/load to the DB, fully tested headlessly. Derives are scaffolding for the persistence API, so they live in this task. **Files:** - Modify: `src/library.rs` — add `Serialize, Deserialize` derives to `Subtitle`, `Video`, `Playlist`, `ChannelMeta`, `Channel`; add `#[serde(default)]` to `Video` and `Channel` fields for forward-compat. - Modify: `src/database.rs` — add `library_snapshot` table to `init_schema()`; add `save_library_snapshot` + `load_library_snapshot`; add unit tests. **Interfaces:** - Produces: - `Database::save_library_snapshot(&self, root: &std::path::Path, library: &[crate::library::Channel])` — serializes to JSON, `INSERT OR REPLACE` into `library_snapshot`. Error-swallowing (returns `()`). - `Database::load_library_snapshot(&self, root: &std::path::Path) -> Option>` — `None` on missing row **or** deserialize failure. - [ ] **Step 1: Add serde derives to the library structs** In `src/library.rs`, confirm the serde import at the top of the file (add if absent): ```rust use serde::{Deserialize, Serialize}; ``` Change each derive line as follows: ```rust // Subtitle #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Subtitle { ``` ```rust // Playlist #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Playlist { ``` ```rust // ChannelMeta #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ChannelMeta { ``` For `Video`, add the derives **and** `#[serde(default)]` on every field so old snapshots missing a future field still deserialize: ```rust #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Video { #[serde(default)] pub id: String, #[serde(default)] pub title: String, #[serde(default)] #[allow(dead_code)] pub stem: String, #[serde(default)] pub video_path: Option, #[serde(default)] pub thumb_path: Option, #[serde(default)] pub description_path: Option, #[serde(default)] pub info_path: Option, #[serde(default)] pub subtitles: Vec, #[serde(default)] pub has_live_chat: bool, #[serde(default)] pub duration_secs: Option, #[serde(default)] pub has_chapters: bool, #[serde(default)] pub file_size: Option, #[serde(default)] pub mtime_unix: Option, #[serde(default)] pub upload_date: Option, } ``` For `Channel`, add the derives and `#[serde(default)]` on every field (keep the existing doc comments): ```rust #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Channel { #[serde(default)] pub name: String, #[serde(default)] pub path: PathBuf, #[serde(default)] pub platform: Platform, #[serde(default)] pub source_url: Option, #[serde(default)] pub videos: Vec