Write down the trash requirement the code already implements

The traceability gate failed on an orphan tag: seventeen sites across
dr-catalog, dr-sync, dr-thumbs and the UI claim FR-CAT-15, and
requirements.md defines FR-CAT-1 through FR-CAT-14. Not a typo and not a
renumbering — the trash was built, designed and documented in the modules
that implement it, and the requirement itself was never written. An orphan
is the gate working: a tag naming an undefined ID would otherwise count as
covered, which is how a matrix comes to report coverage of things nobody
specified.

FR-CAT-15 now says what trash.rs does, in the terms the module already
argues: a soft delete moves the file into `.darkroom-trash/` and the
catalog records that it happened, because a flag alone would not survive
invariant 5.2.4 — the catalog is rebuildable from sources, so a rescan
would find every deleted file still in the library and re-index it. That
is also why the scanner exclusion is part of the requirement rather than
an implementation detail; the folder and the exclusion are one mechanism
and neither works alone. Permanent delete removes the file before the row,
a delete of something already gone counts as success, and the count and
bytes are shown before emptying.

docs/traceability.md is regenerated: 150 requirements, 71 covered, no
orphans.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 14:48:31 +02:00
co-authored by Claude Opus 5
parent 03326242a1
commit fe66eba87f
2 changed files with 23 additions and 6 deletions
+16
View File
@@ -159,6 +159,22 @@ Lightroom `.lrcat` and a darktable `library.db`. Edit graphs are explicitly **no
develop parameters do not translate meaningfully between pipelines, and a partial translation is
worse than none. This is the path in for users with existing libraries.
**FR-CAT-15 — Trash and permanent delete.** Deleting an image shall be reversible by default. A
soft delete **moves the file** into a `.darkroom-trash/` folder under the library root and records
in the catalog when it was trashed and the path it came from; restore moves it back to that path.
Permanent delete removes the file first and the catalog row second, and a delete of something
already gone counts as success.
A flag alone would not survive invariant 5.2.4: the catalog is rebuildable from sources, so a
rescan would find every "deleted" file still in the library and re-index it. The folder is the
durable fact and the row is the convenience — which also means the scanner shall exclude the trash
folder, and that a user can recover by hand without DarkRoom. Derived data keyed on the file
(thumbnails, cached previews) is dropped when the image is permanently deleted, not when it is
trashed.
The trash shall be listable newest-first, with the count and total bytes it holds shown before any
destructive action, since that figure is what tells the user whether they meant it.
### 3.2 RAW decoding
**FR-RAW-1 — Format support.** Decode mainstream RAW formats. Minimum launch set: Canon (CR2,
+7 -6
View File
@@ -11,15 +11,15 @@ Denominators are parsed from [`requirements.md`](requirements.md) at run time, n
|---|---|
| Source files scanned | 93 |
| TRACES tags found | 137 |
| Requirements defined | 149 |
| Requirements covered | 70 |
| **Coverage** | **47.0%** (70/149) |
| Requirements defined | 150 |
| Requirements covered | 71 |
| **Coverage** | **47.3%** (71/150) |
### By type
| Type | Covered | Defined |
|---|---|---|
| FR | 54 | 95 |
| FR | 55 | 96 |
| NFR | 14 | 48 |
| R | 2 | 6 |
@@ -27,7 +27,7 @@ Denominators are parsed from [`requirements.md`](requirements.md) at run time, n
A tag naming an ID `requirements.md` does not define — what renumbering produces, and what a typo produces.
- `FR-CAT-15`
_None._
## Tagged requirements
@@ -36,6 +36,7 @@ A tag naming an ID `requirements.md` does not define — what renumbering produc
| FR-CAT-1 | [`core/dr-catalog/src/scan.rs:1`](../core/dr-catalog/src/scan.rs#L1), [`core/dr-sync/src/scan.rs:93`](../core/dr-sync/src/scan.rs#L93), [`core/dr-types/src/lib.rs:185`](../core/dr-types/src/lib.rs#L185), [`tools/traceability/src/lib.rs:473`](../tools/traceability/src/lib.rs#L473), [`tools/traceability/src/lib.rs:505`](../tools/traceability/src/lib.rs#L505), [`ui/dr-ui/src/library.rs:1`](../ui/dr-ui/src/library.rs#L1) |
| FR-CAT-11 | [`ui/dr-ui/src/library.rs:150`](../ui/dr-ui/src/library.rs#L150) |
| FR-CAT-12 | [`core/dr-pipeline/src/sidecar.rs:108`](../core/dr-pipeline/src/sidecar.rs#L108) |
| FR-CAT-15 | [`core/dr-catalog/src/schema.rs:258`](../core/dr-catalog/src/schema.rs#L258), [`core/dr-catalog/src/trash.rs:1`](../core/dr-catalog/src/trash.rs#L1), [`core/dr-sync-nextcloud/src/lib.rs:366`](../core/dr-sync-nextcloud/src/lib.rs#L366), [`core/dr-sync/src/lib.rs:122`](../core/dr-sync/src/lib.rs#L122), [`core/dr-sync/src/scan.rs:426`](../core/dr-sync/src/scan.rs#L426), [`core/dr-sync/src/scan.rs:57`](../core/dr-sync/src/scan.rs#L57), [`core/dr-thumbs/src/lib.rs:341`](../core/dr-thumbs/src/lib.rs#L341), [`ui/dr-ui/src/collections_ui.rs:1191`](../ui/dr-ui/src/collections_ui.rs#L1191), [`ui/dr-ui/src/collections_ui.rs:730`](../ui/dr-ui/src/collections_ui.rs#L730), [`ui/dr-ui/src/library.rs:150`](../ui/dr-ui/src/library.rs#L150), [`ui/dr-ui/src/library.rs:167`](../ui/dr-ui/src/library.rs#L167), [`ui/dr-ui/src/library.rs:2005`](../ui/dr-ui/src/library.rs#L2005), [`ui/dr-ui/src/library.rs:2037`](../ui/dr-ui/src/library.rs#L2037), [`ui/dr-ui/src/library_ui.rs:112`](../ui/dr-ui/src/library_ui.rs#L112), [`ui/dr-ui/src/library_ui.rs:436`](../ui/dr-ui/src/library_ui.rs#L436), [`ui/dr-ui/src/trash.rs:1`](../ui/dr-ui/src/trash.rs#L1), [`ui/dr-ui/ui/collections.slint:458`](../ui/dr-ui/ui/collections.slint#L458) |
| FR-CAT-1a | [`core/dr-types/src/lib.rs:47`](../core/dr-types/src/lib.rs#L47) |
| FR-CAT-2 | [`core/dr-catalog/src/lib.rs:1`](../core/dr-catalog/src/lib.rs#L1), [`core/dr-catalog/src/schema.rs:1`](../core/dr-catalog/src/schema.rs#L1), [`tools/traceability/src/lib.rs:473`](../tools/traceability/src/lib.rs#L473) |
| FR-CAT-3 | [`core/dr-catalog/src/jobs.rs:1`](../core/dr-catalog/src/jobs.rs#L1), [`core/dr-sync/src/scan.rs:69`](../core/dr-sync/src/scan.rs#L69), [`core/dr-thumbs/src/codec.rs:1`](../core/dr-thumbs/src/codec.rs#L1), [`core/dr-thumbs/src/lib.rs:1`](../core/dr-thumbs/src/lib.rs#L1), [`ui/dr-ui/src/derived_sync.rs:1`](../ui/dr-ui/src/derived_sync.rs#L1) |
@@ -106,7 +107,7 @@ A tag naming an ID `requirements.md` does not define — what renumbering produc
## Not yet tagged
79 of 149 requirements have no implementation tag. Expected while the codebase is young; each should gain one as it is built.
79 of 150 requirements have no implementation tag. Expected while the codebase is young; each should gain one as it is built.
<details><summary>Show untagged requirements</summary>