From aee1aafc74ed970c6d93e497fc8a0c778f5f0e7f Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Mon, 2 Nov 2026 14:12:10 -0500 Subject: [PATCH] =?UTF-8?q?docs(admin):=20hash=20conflicts=20page=20?= =?UTF-8?q?=E2=80=94=20the=20duplicate-content=20queue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- docs/admin/hash-conflicts.md | 56 +++++++++++++++++++++++++++++++++++ docs/developer/ui-controls.md | 8 +++++ 2 files changed, 64 insertions(+) create mode 100644 docs/admin/hash-conflicts.md diff --git a/docs/admin/hash-conflicts.md b/docs/admin/hash-conflicts.md new file mode 100644 index 00000000..9eac96f2 --- /dev/null +++ b/docs/admin/hash-conflicts.md @@ -0,0 +1,56 @@ + + +# 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. diff --git a/docs/developer/ui-controls.md b/docs/developer/ui-controls.md index 2294c79c..576a7085 100644 --- a/docs/developer/ui-controls.md +++ b/docs/developer/ui-controls.md @@ -284,6 +284,14 @@ label: User Access where: Admin → Libraries → a library's panel → section heading does: Per-user visibility checkboxes — checked (default) shows the library to that user; unchecked hides it everywhere for them, enforced in the database. +label: Keep both +where: Admin → Hash Conflicts → a conflict group card +does: Records the resolution and stops flagging the group; every copy stays, nothing merged or removed. For intentional duplicates. + +label: Keep this copy +where: Admin → Hash Conflicts → a copy row +does: Keeps that copy, folds the other copies' reading data into it, and removes their catalog rows. Files on disk are never touched. + label: Resume rewind where: audio player → player settings; profile settings does: Rewinds playback a configured few seconds when you resume.