Photo versions follow-ups (#194) #198

Merged
cameron merged 6 commits from fix/194-version-followups into master 2026-10-31 01:57:09 +00:00
Owner

Closes #194

  1. Name-link starvation. unlinked_name_candidates now takes an id > after_id cursor and returns ids. sweep_name_links keeps a per-library in-memory cursor (up to 4 pages of 500 per call), wraps to 0 at the end and continues, so unresolvable matches no longer block later candidates. Test: 120 orphans with a 50-row page, then a linkable pair is still linked.
  2. Stranded annotations / unscoped canonical path.
    • canonical_rel_path takes Option<library_id> and scopes the version row by it. The tag and favorite DAOs only know a rel_path (library-agnostic by design), so they pass None. As the scoping that applies to them, a path that also names a non-version file in any library is never redirected, since annotations are keyed by rel_path alone.
    • link_version (so ingest and link_versions alike) merges favorites and tags from the version's path into the original's inside the link transaction. Rows are unioned, duplicates dropped, and the version path's rows removed.
    • Not covered: a link made while the original was not yet indexed does not migrate later when the original appears.
  3. Hidden-only folders. The non-recursive /photos listing drops a dir when it has indexed rows and all of them are hidden versions. Unless include_versions=true. One indexed prefix-range query per dir, no walk. Dirs with no indexed rows are kept.
  4. Minor.
    • Display change and reels: no change needed. Reels build from memory items whose path is always the original's, and never use the display file. The reason is documented in CLAUDE.md.
    • POST /image/exif/date and /clear on a version path now apply to the original's row (new original_location_of), and the response describes the original.
    • link_versions --reviewed now also checks original_hash against the index (hash must exist, and the TSV's original path must not hold other bytes). Stale rows are refused and listed on stderr.

Tests: DATABASE_URL=":memory:" cargo test (1036 pass), cargo fmt --check and cargo clippy --all-targets are clean.

🤖 Generated with Claude Code

Closes #194 1. **Name-link starvation.** `unlinked_name_candidates` now takes an `id > after_id` cursor and returns ids. `sweep_name_links` keeps a per-library in-memory cursor (up to 4 pages of 500 per call), wraps to 0 at the end and continues, so unresolvable matches no longer block later candidates. Test: 120 orphans with a 50-row page, then a linkable pair is still linked. 2. **Stranded annotations / unscoped canonical path.** - `canonical_rel_path` takes `Option<library_id>` and scopes the version row by it. The tag and favorite DAOs only know a rel_path (library-agnostic by design), so they pass `None`. As the scoping that applies to them, a path that also names a non-version file in any library is never redirected, since annotations are keyed by rel_path alone. - `link_version` (so ingest and `link_versions` alike) merges favorites and tags from the version's path into the original's inside the link transaction. Rows are unioned, duplicates dropped, and the version path's rows removed. - Not covered: a link made while the original was not yet indexed does not migrate later when the original appears. 3. **Hidden-only folders.** The non-recursive `/photos` listing drops a dir when it has indexed rows and all of them are hidden versions. Unless `include_versions=true`. One indexed prefix-range query per dir, no walk. Dirs with no indexed rows are kept. 4. **Minor.** - Display change and reels: no change needed. Reels build from memory items whose `path` is always the original's, and never use the display file. The reason is documented in CLAUDE.md. - `POST /image/exif/date` and `/clear` on a version path now apply to the original's row (new `original_location_of`), and the response describes the original. - `link_versions --reviewed` now also checks `original_hash` against the index (hash must exist, and the TSV's original path must not hold other bytes). Stale rows are refused and listed on stderr. Tests: `DATABASE_URL=":memory:" cargo test` (1036 pass), `cargo fmt --check` and `cargo clippy --all-targets` are clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
cameron added 6 commits 2026-10-30 23:29:29 +00:00
cameron merged commit c6d04589d1 into master 2026-10-31 01:57:09 +00:00
cameron deleted branch fix/194-version-followups 2026-10-31 01:57:09 +00:00
Sign in to join this conversation.