Add keep/do-not-delete flag for snapshots #144

Closed
opened 2026-08-12 07:25:48 +00:00 by agent-pi · 1 comment
Collaborator

The web UI should allow marking snapshots or events as "keep" / "do not delete". This requires:

  • A keep boolean column in the snapshots table (or a separate protected_snapshots table)
  • A web UI toggle (button or checkbox) on the day view and/or scrubber view
  • The cleanup daemon must skip protected snapshots during retention deletion
  • Requires DB write access from cangjian (connection is already read-write per the process split design)

This was intentionally deferred during the process split (#143) to keep scope tight.

The web UI should allow marking snapshots or events as "keep" / "do not delete". This requires: - A `keep` boolean column in the snapshots table (or a separate protected_snapshots table) - A web UI toggle (button or checkbox) on the day view and/or scrubber view - The cleanup daemon must skip protected snapshots during retention deletion - Requires DB write access from cangjian (connection is already read-write per the process split design) This was intentionally deferred during the process split (#143) to keep scope tight.
Author
Collaborator

Findings from exploration

  • src/cang/web/templates/_keep_btn.html is orphaned scaffolding from the original app commit (8b032bf) — it references a defunct clips concept (/clips/{id}/keep); no route renders it. It will be replaced with the real button.
  • Every motion snapshot belongs to exactly one journey (the group_events_* grouping is total over motion snapshots), so keep semantics are deterministic: motion frame → keep its whole journey; background frame → keep just that frame. No mixed states.
  • Retention deletion is concentrated in delete_expired_snapshots() (db.py); the daemon just unlinks returned paths, so one AND keep = 0 in both branches is sufficient.
  • Schema choice: keep boolean column on snapshots (issue option 1) — a separate protected table would force a NOT EXISTS join into cleanup for no benefit.

Plan of attack (design approved in chat with Marvin8):

  1. V6 migration: ALTER TABLE snapshots ADD COLUMN keep INTEGER NOT NULL DEFAULT 0 + set_keep() db helper.
  2. delete_expired_snapshots() skips keep = 1 rows (motion + background).
  3. POST /snapshots/{snapshot_id}/keep — toggle; recomputes the day's journeys with configured journey_gap_seconds, keeps the containing journey or the single background frame; returns JSON {kept: [ids], keep: bool}.
  4. Scrubber (primary surface): keep array in snapshots_json, Keep/Unkeep button beside Prev/Play/Next (fetch-driven), kept-variant motion markers on the density track.
  5. Day view (secondary): Keep/Unkeep button per event tile + "kept" badge for kept journeys.
  6. TDD throughout; docs in docs/web-ui.md; Release-Notes bullet.
**Findings from exploration** - `src/cang/web/templates/_keep_btn.html` is orphaned scaffolding from the original app commit (8b032bf) — it references a defunct `clips` concept (`/clips/{id}/keep`); no route renders it. It will be replaced with the real button. - Every motion snapshot belongs to exactly one journey (the `group_events_*` grouping is total over motion snapshots), so keep semantics are deterministic: **motion frame → keep its whole journey; background frame → keep just that frame**. No mixed states. - Retention deletion is concentrated in `delete_expired_snapshots()` (db.py); the daemon just unlinks returned paths, so one `AND keep = 0` in both branches is sufficient. - Schema choice: `keep` boolean column on `snapshots` (issue option 1) — a separate protected table would force a `NOT EXISTS` join into cleanup for no benefit. **Plan of attack** (design approved in chat with Marvin8): 1. V6 migration: `ALTER TABLE snapshots ADD COLUMN keep INTEGER NOT NULL DEFAULT 0` + `set_keep()` db helper. 2. `delete_expired_snapshots()` skips `keep = 1` rows (motion + background). 3. `POST /snapshots/{snapshot_id}/keep` — toggle; recomputes the day's journeys with configured `journey_gap_seconds`, keeps the containing journey or the single background frame; returns JSON `{kept: [ids], keep: bool}`. 4. Scrubber (primary surface): `keep` array in `snapshots_json`, Keep/Unkeep button beside Prev/Play/Next (fetch-driven), kept-variant motion markers on the density track. 5. Day view (secondary): Keep/Unkeep button per event tile + "kept" badge for kept journeys. 6. TDD throughout; docs in `docs/web-ui.md`; Release-Notes bullet.
Sign in to join this conversation.
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
marvin8/cang#144
No description provided.