Files
DarkRoom/docs/traceability.md
T
dtourolleandClaude Opus 5 489465faf0 Show the photograph the way it was taken
Nothing read EXIF orientation, so every frame from a body held sideways
lay on its side — in the grid, in develop, and in the read-only preview.

The tag is honoured as part of *reading the file*, at the same standing
as a RAW's masked-photosite crop, never as an edit. It lives as a
baseline on Framing rather than as a starting value for quarter_turns,
which is what keeps four things true: a sideways file opens unmodified,
reset returns it to upright rather than to the sensor's scan order, its
sidecar stays empty, and the rotate button still moves the image 90°
whatever the file underneath it says.

Framing::effective composes the baseline with the user's own turns
through the group law rather than by adding turns and OR-ing flags. The
naive version gets one case wrong — an odd baseline turn plus a user
mirror — and gets it wrong quietly, because the result is still a
plausible orientation. The composition collapses to a single
permutation, so obeying the tag costs nothing per pixel.

dr_decode::orientation is a header-only IFD walk, separate from
metadata() for the reason the entry points are separate at all: the grid
asks once per cell and must not build a rawler decoder to get one tag.
CR3 and RAF fall back to the full read, being neither TIFF nor JPEG.

Written down as FR-DEV-3h.

Known gap: thumbnails cached before this stay sideways. The store is
keyed by file and size, and its shards sync — invalidating them would
have every client re-download 25 MB a shard, which is not this commit's
call to make.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 15:37:05 +02:00

19 KiB

Requirements traceability matrix

Denominators are parsed from requirements.md at run time, never hardcoded. Coverage is the intersection of tagged and defined IDs over defined IDs, so it cannot exceed 100%.

Summary

Metric Value
Source files scanned 93
TRACES tags found 141
Requirements defined 151
Requirements covered 72
Coverage 47.7% (72/151)

By type

Type Covered Defined
FR 56 97
NFR 14 48
R 2 6

Orphan tags

A tag naming an ID requirements.md does not define — what renumbering produces, and what a typo produces.

None.

Tagged requirements

ID Tagged in
FR-CAT-1 core/dr-catalog/src/scan.rs:1, core/dr-sync/src/scan.rs:93, core/dr-types/src/lib.rs:185, tools/traceability/src/lib.rs:473, tools/traceability/src/lib.rs:505, ui/dr-ui/src/library.rs:1
FR-CAT-11 ui/dr-ui/src/library.rs:150
FR-CAT-12 core/dr-pipeline/src/sidecar.rs:108
FR-CAT-15 core/dr-catalog/src/schema.rs:258, core/dr-catalog/src/trash.rs:1, core/dr-sync-nextcloud/src/lib.rs:366, core/dr-sync/src/lib.rs:122, core/dr-sync/src/scan.rs:426, core/dr-sync/src/scan.rs:57, core/dr-thumbs/src/lib.rs:341, ui/dr-ui/src/collections_ui.rs:1191, ui/dr-ui/src/collections_ui.rs:730, ui/dr-ui/src/library.rs:150, ui/dr-ui/src/library.rs:167, ui/dr-ui/src/library.rs:2020, ui/dr-ui/src/library.rs:2052, ui/dr-ui/src/library_ui.rs:112, ui/dr-ui/src/library_ui.rs:436, ui/dr-ui/src/trash.rs:1, ui/dr-ui/ui/collections.slint:458
FR-CAT-1a core/dr-types/src/lib.rs:47
FR-CAT-2 core/dr-catalog/src/lib.rs:1, core/dr-catalog/src/schema.rs:1, tools/traceability/src/lib.rs:473
FR-CAT-3 core/dr-catalog/src/jobs.rs:1, core/dr-sync/src/scan.rs:69, core/dr-thumbs/src/codec.rs:1, core/dr-thumbs/src/lib.rs:1, ui/dr-ui/src/derived_sync.rs:1
FR-CAT-4 core/dr-catalog/src/lib.rs:1, core/dr-catalog/src/query.rs:1, ui/dr-ui/src/library.rs:1, ui/dr-ui/src/library_ui.rs:1
FR-CAT-5 core/dr-catalog/src/rating.rs:1, core/dr-decode/src/lib.rs:240, core/dr-decode/src/lib.rs:307, core/dr-pipeline/src/sidecar.rs:125
FR-CAT-6 core/dr-catalog/src/collections.rs:1, core/dr-catalog/src/lib.rs:1, core/dr-catalog/src/query.rs:1, core/dr-catalog/src/rating.rs:1, core/dr-types/src/selector.rs:1, ui/dr-ui/src/library.rs:177
FR-CAT-7 core/dr-catalog/src/collections.rs:1, core/dr-catalog/src/merge.rs:1, core/dr-catalog/src/sync.rs:1, core/dr-types/src/selector.rs:1, ui/dr-ui/src/collections_ui.rs:1, ui/dr-ui/src/derived_sync.rs:1, ui/dr-ui/ui/collections.slint:4
FR-CAT-8 core/dr-pipeline/src/sidecar.rs:89, ui/dr-ui/src/library.rs:289
FR-CAT-9 core/dr-catalog/src/cache.rs:1, core/dr-catalog/src/scan.rs:1, core/dr-catalog/src/schema.rs:230, core/dr-sync-nextcloud/src/desktop_client.rs:30, core/dr-sync/src/reachability.rs:1, core/dr-types/src/lib.rs:104, ui/dr-ui/src/library.rs:136, ui/dr-ui/src/library.rs:194, ui/dr-ui/src/library.rs:2140, ui/dr-ui/src/library.rs:994, ui/dr-ui/src/library_ui.rs:1240, ui/dr-ui/src/library_ui.rs:1391, ui/dr-ui/src/library_ui.rs:165, ui/dr-ui/src/library_ui.rs:1682, ui/dr-ui/src/library_ui.rs:1756, ui/dr-ui/src/library_ui.rs:1870, ui/dr-ui/src/library_ui.rs:2717, ui/dr-ui/src/library_ui.rs:2745, ui/dr-ui/src/library_ui.rs:283, ui/dr-ui/src/library_ui.rs:936, ui/dr-ui/src/library_ui.rs:978
FR-CULL-1 core/dr-decode/src/preview.rs:134
FR-CULL-2 core/dr-decode/src/locate.rs:1, core/dr-decode/src/preview.rs:161
FR-CULL-4 core/dr-catalog/src/rating.rs:1, core/dr-pipeline/src/sidecar.rs:125, ui/dr-ui/src/library.rs:177, ui/dr-ui/src/library.rs:289
FR-DEV-3 core/dr-pipeline/src/framing.rs:174
FR-DEV-3a core/dr-pipeline/src/descriptor.rs:91, core/dr-pipeline/src/graph.rs:138, core/dr-pipeline/src/graph.rs:15, core/dr-pipeline/src/graph.rs:39, core/dr-pipeline/src/operation.rs:104
FR-DEV-3b core/dr-pipeline/src/descriptor.rs:91, core/dr-pipeline/src/graph.rs:39, core/dr-pipeline/src/operation.rs:104
FR-DEV-3c core/dr-pipeline/src/graph.rs:138
FR-DEV-3d core/dr-pipeline/src/framing.rs:174
FR-DEV-3e core/dr-decode/src/lib.rs:492, core/dr-decode/src/lib.rs:612
FR-DEV-3h core/dr-decode/src/lib.rs:307, core/dr-decode/src/preview.rs:29, core/dr-pipeline/src/framing.rs:188, core/dr-types/src/lib.rs:272
FR-DEV-4 core/dr-gpu/src/lib.rs:123
FR-DSP-1 ui/dr-ui/src/lib.rs:44
FR-EXP-1 core/dr-types/src/settings.rs:1, ui/dr-ui/src/settings_ui.rs:1
FR-EXP-2 core/dr-types/src/settings.rs:1, ui/dr-ui/src/settings_ui.rs:1
FR-EXP-3 core/dr-types/src/settings.rs:1, ui/dr-ui/src/settings_ui.rs:1
FR-EXP-4 ui/dr-ui/src/settings_ui.rs:1
FR-EXP-5 ui/dr-ui/src/settings_store.rs:1
FR-EXP-6 core/dr-types/src/settings.rs:1, ui/dr-ui/src/settings_ui.rs:1
FR-EXP-8 ui/dr-ui/src/settings_ui.rs:1
FR-EXP-9 core/dr-decode/src/lib.rs:409
FR-NC-1 core/dr-sync-nextcloud/src/auth.rs:132, core/dr-sync-nextcloud/src/auth.rs:44, core/dr-sync-nextcloud/src/session.rs:128, ui/dr-ui/src/launch.rs:256, ui/dr-ui/src/launch.rs:49, ui/dr-ui/src/launch_ui.rs:344
FR-NC-12 core/dr-sync-nextcloud/src/lib.rs:34, core/dr-sync-nextcloud/src/lib.rs:811, core/dr-sync/src/lib.rs:155, core/dr-sync/src/lib.rs:38, core/dr-sync/src/reachability.rs:1
FR-NC-2 core/dr-sync-nextcloud/src/session.rs:128, core/dr-sync-nextcloud/src/session.rs:34
FR-NC-3 core/dr-decode/src/locate.rs:1, core/dr-decode/src/preview.rs:161, core/dr-sync/src/capability.rs:41, core/dr-thumbs/src/lib.rs:1, ui/dr-ui/src/library.rs:1, ui/dr-ui/src/library_ui.rs:1
FR-NC-4 core/dr-sync-nextcloud/src/propfind.rs:100, core/dr-sync-nextcloud/src/propfind.rs:51, core/dr-sync/src/capability.rs:6, core/dr-sync/src/lib.rs:155, core/dr-sync/src/scan.rs:93, ui/dr-ui/src/launch.rs:49
FR-NC-5 core/dr-sync-nextcloud/src/propfind.rs:51
FR-NC-6a core/dr-catalog/src/cache.rs:1, core/dr-catalog/src/schema.rs:230, core/dr-catalog/src/schema.rs:631, core/dr-types/src/selector.rs:1, core/dr-types/src/settings.rs:1, ui/dr-ui/src/lib.rs:1323, ui/dr-ui/src/lib.rs:883, ui/dr-ui/src/library.rs:813, ui/dr-ui/src/library.rs:836, ui/dr-ui/src/library.rs:994, ui/dr-ui/src/library_ui.rs:1041, ui/dr-ui/src/library_ui.rs:173, ui/dr-ui/src/library_ui.rs:184, ui/dr-ui/src/library_ui.rs:193, ui/dr-ui/src/library_ui.rs:242, ui/dr-ui/src/library_ui.rs:252, ui/dr-ui/src/library_ui.rs:2734, ui/dr-ui/src/library_ui.rs:294, ui/dr-ui/src/library_ui.rs:309, ui/dr-ui/src/library_ui.rs:340, ui/dr-ui/src/library_ui.rs:699, ui/dr-ui/src/library_ui.rs:800, ui/dr-ui/src/library_ui.rs:903, ui/dr-ui/src/settings_store.rs:1, ui/dr-ui/src/settings_ui.rs:1, ui/dr-ui/ui/library.slint:799
FR-NC-6c core/dr-sync-nextcloud/src/desktop_client.rs:30, core/dr-types/src/lib.rs:104, core/dr-types/src/lib.rs:186
FR-NC-7 core/dr-sync-nextcloud/src/lib.rs:95, ui/dr-ui/src/derived_sync.rs:1
FR-NC-8 core/dr-pipeline/src/sidecar.rs:108, core/dr-pipeline/src/sidecar.rs:89, ui/dr-ui/src/library.rs:289
FR-NC-9 core/dr-catalog/src/merge.rs:1, core/dr-catalog/src/sync.rs:1, core/dr-pipeline/src/sidecar.rs:212
FR-PLAT-AND-1 core/dr-types/src/lib.rs:47
FR-PLAT-AND-3 core/dr-catalog/src/jobs.rs:1
FR-PLAT-LIN-1 core/dr-types/src/settings.rs:1, ui/dr-ui/src/settings_store.rs:1
FR-RAW-1 core/dr-decode/src/lib.rs:198, core/dr-types/src/lib.rs:114, core/dr-types/src/lib.rs:185
FR-RAW-3 core/dr-decode/src/lib.rs:409, core/dr-decode/src/lib.rs:94
FR-RAW-4 core/dr-decode/src/error.rs:1
FR-RAW-5 core/dr-decode/src/lib.rs:122
FR-UI-1 ui/dr-ui/src/lib.rs:1405, ui/dr-ui/src/lib.rs:52
FR-UI-2 ui/dr-ui/src/lib.rs:52
FR-UI-3 ui/dr-ui/ui/collections.slint:4
FR-UI-4 ui/dr-ui/ui/app.slint:966
FR-UI-5 ui/dr-ui/src/collections_ui.rs:1, ui/dr-ui/src/lib.rs:1439, ui/dr-ui/ui/collections.slint:4
NFR-ARCH-2 core/dr-catalog/src/jobs.rs:1
NFR-ARCH-4 core/dr-catalog/src/error.rs:1, core/dr-thumbs/src/error.rs:1
NFR-OPS-1 tools/traceability/src/lib.rs:266
NFR-P1 core/dr-catalog/src/lib.rs:1, core/dr-catalog/src/scan.rs:1, tools/traceability/src/lib.rs:473
NFR-P13 core/dr-decode/src/preview.rs:134
NFR-P9 ui/dr-ui/src/collections_ui.rs:1, ui/dr-ui/src/library.rs:1, ui/dr-ui/src/library_ui.rs:1, ui/dr-ui/src/trash.rs:1
NFR-R1 core/dr-catalog/src/sync.rs:1
NFR-R2 core/dr-catalog/src/trash.rs:1
NFR-R5 core/dr-catalog/src/collections.rs:1, core/dr-catalog/src/error.rs:1, core/dr-catalog/src/schema.rs:1
NFR-R7 core/dr-gpu/src/error.rs:1
NFR-R8 core/dr-gpu/src/error.rs:1
NFR-RES-1 ui/dr-ui/src/lib.rs:44
NFR-RES-4 core/dr-catalog/src/cache.rs:1, core/dr-catalog/src/schema.rs:230, core/dr-thumbs/src/codec.rs:1, core/dr-thumbs/src/lib.rs:1, core/dr-thumbs/src/lib.rs:341
NFR-SEC-1 core/dr-decode/src/error.rs:1
R1 tools/traceability/src/lib.rs:489, tools/traceability/src/lib.rs:493
R4 core/dr-gpu/src/lib.rs:123

Not yet tagged

79 of 151 requirements have no implementation tag. Expected while the codebase is young; each should gain one as it is built.

Show untagged requirements
  • FR-CAT-10
  • FR-CAT-13
  • FR-CAT-14
  • FR-CULL-10
  • FR-CULL-11
  • FR-CULL-12
  • FR-CULL-3
  • FR-CULL-5
  • FR-CULL-6
  • FR-CULL-7
  • FR-CULL-8
  • FR-CULL-9
  • FR-DEV-1
  • FR-DEV-2
  • FR-DEV-3f
  • FR-DEV-3g
  • FR-DEV-5
  • FR-DEV-6
  • FR-DEV-7
  • FR-DEV-8
  • FR-DSP-2
  • FR-DSP-3
  • FR-DSP-4
  • FR-DSP-5
  • FR-DSP-6
  • FR-DSP-7
  • FR-DSP-8
  • FR-EXP-7
  • FR-NC-10
  • FR-NC-11
  • FR-NC-6
  • FR-NC-6b
  • FR-PLAT-AND-2
  • FR-PLAT-AND-4
  • FR-PLAT-AND-5
  • FR-PLAT-AND-6
  • FR-PLAT-LIN-2
  • FR-PLAT-LIN-3
  • FR-RAW-2
  • FR-UI-6
  • FR-UI-7
  • NFR-A11Y-1
  • NFR-A11Y-2
  • NFR-A11Y-3
  • NFR-ARCH-1
  • NFR-ARCH-3
  • NFR-COMPAT-1
  • NFR-COMPAT-2
  • NFR-OPS-2
  • NFR-OPS-3
  • NFR-OPS-4
  • NFR-P10
  • NFR-P11
  • NFR-P12
  • NFR-P14
  • NFR-P15
  • NFR-P2
  • NFR-P3
  • NFR-P4
  • NFR-P5
  • NFR-P6
  • NFR-P7
  • NFR-P8
  • NFR-PORT-1
  • NFR-PORT-2
  • NFR-PORT-3
  • NFR-R3
  • NFR-R4
  • NFR-R6
  • NFR-RES-2
  • NFR-RES-3
  • NFR-SEC-2
  • NFR-SEC-3
  • NFR-SEC-4
  • NFR-SEC-5
  • R2
  • R3
  • R5
  • R6