docs(admin): hash conflicts page — the duplicate-content queue

Sixth admin page. Documents what creates conflict groups (scanner at
scan time + the hash backfill retroactively), the group card anatomy
(count badge, library + short SHA-256, per-copy rows with usage summary
highlighted when that copy holds reading data), and both resolutions
with verified semantics: Keep this copy (reparents the other copies'
children into the kept copy, deletes their catalog rows, records the
resolver — files on disk never touched, consistent with platform-wide
delete semantics) and Keep both (mark resolved, stop flagging, nothing
merged). Glossary gains Keep both and Keep this copy.
This commit is contained in:
John O'Keefe committed 2026-11-02 14:12:10 -05:00
1 parent 3e55ddd88a
commit aee1aafc74
2 files changed
+64

No files matched your search

+56
View File
@@ -0,0 +1,56 @@
<!-- Ground truth: templates/admin_hash_conflicts.templ (cards, actions,
confirms), internal/handlers/hash_conflicts.go (ResolveHashConflict:
keep_all marks resolved; keep reparents children to the kept copy,
deletes the other catalog rows, records who resolved), internal/
services/media_scanner.go (recordHashConflictIfAny), internal/services/
hash_backfill.go (backfill-created conflicts). Re-verify against these
when the page changes. -->
# Hash Conflicts
Hash Conflicts is the duplicate-content queue: groups of catalog items
whose files are **byte-identical** (same SHA-256) but stored at more than
one path in the same library. The scanner flags these as it goes — and
the hash backfill job flags them retroactively for content that predates
hash tracking — so both fresh scans and old libraries surface their
duplicates here.
Duplicates usually mean the same book was copied into two folders, or a
library was reorganized and both the old and new copies got scanned. The
harm is quiet but real: split reading histories, double-counted library
stats, and two entries for one book.
## What a conflict looks like
One card per group: a **Duplicate content** heading, a badge with the
copy count, the library and a short fingerprint of the shared hash, and
one row per copy — title, author, full path, file size, and a usage
summary that's **highlighted when that copy holds reading data** (someone
has actually read it).
## Resolving a group
Two outcomes, both confirm-guarded:
### Keep this copy
Per-copy button. Keeps the copy you chose and folds the **other copies'
reading data into it** (their catalog rows are removed; the underlying
files on disk are never touched — this platform's deletes act on the
catalog, not your files). The row-level usage summaries exist precisely
so you can pick the copy that already carries the reading history.
### Keep both
The group-level button, for duplicates that are intentional. It records
the resolution and stops flagging this group — every copy stays in the
catalog exactly as it is, nothing merged, nothing removed.
After either choice the card is replaced with the outcome, and the group
never comes back (unless the same duplicate situation re-arises from a
*new* scan, which creates a fresh group).
## When the page is empty
"No content duplicates detected." — the queue drains from the top; groups
appear as scans and the backfill find them.