From 79c051e8a6cd70642b0357fcf4ad3cd33024d2e6 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sat, 26 Sep 2026 07:45:00 -0400 Subject: [PATCH] Bring the outstanding register up to 0.16.0 Five things in it were no longer true of the tree: - FR-DSP-4 said "Unbuilt" and that nothing tracked a provisional frame. 0.15.0 finished it: the settle debounce in ui/dr-ui/src/refine.rs, the draft flag reaching the histogram (canvas-draft, Levels.provisional), and the 150 ms fade of the last draft (app.slint's canvas-previous). - The note that dedup.rs is re-import detection stood alone. FR-CAT-11a is now built beside it: dr_catalog::duplicates, dr_ui::duplicates and the Duplicate originals review. - Culling counted three unbuilt clauses and named two. - Section 6 still counted zero @tr( and five accessible-* lines, figures from before 2026-08-30. The launch screen is converted (build.rs holds the mechanism, no .po exists), 127 accessible-* lines sit across eleven files, and ui_controls_are_accessible.rs holds the structure. - Section 11 said nothing of the panorama was built. It was, the same week; FR-MRG-9, NFR-MRG-1 and NFR-MRG-2 are what carry no tag. A new section 4a lists what the develop, mask and keyboard work left open, so that its closed clauses are not looked for: FR-UI-5's wheel on sliders, FR-RAW-2's second decoder, mask-editing M2's remainder, and FR-DEV-17's deliberate blind spot for range and region parts. The Android paragraph now says develop is zero-copy there since TD-1 was paid off, and that the APK carries the manual. --- docs/dev/outstanding.md | 126 +++++++++++++++++++++++++++++----------- 1 file changed, 93 insertions(+), 33 deletions(-) diff --git a/docs/dev/outstanding.md b/docs/dev/outstanding.md index e19cff4..a42cb52 100644 --- a/docs/dev/outstanding.md +++ b/docs/dev/outstanding.md @@ -28,6 +28,12 @@ Android memory pressure and lost-root recovery (FR-PLAT-AND-5, FR-PLAT-AND-2), i recorded where it appears rather than deleted, because a requirement that is *half* met is the one most likely to be reported as closed. +**Swept again on 2026-09-26, for 0.16.0.** Progressive refinement (FR-DSP-4) and the panorama +(§3.11) were built and are struck below; consolidating duplicate originals (FR-CAT-11a) now sits +beside re-import detection; the accessibility and localisation counts in §6 had not been read +against the tree since 2026-08-30 and are replaced; and §4a records what the develop and keyboard +work of 0.15.0 and 0.16.0 left open. + --- ## 1. Plugins — post-v1 since 2026-09-19 @@ -80,8 +86,8 @@ somebody reads the matrix. ## 2. Culling — the stated differentiator, half built -[D11](requirements.md) names culling "the core differentiator". FR-CULL-1, -2, -3, -4 and -8 through --12 are built. Three are not. +[D11](requirements.md) names culling "the core differentiator". FR-CULL-1 through -5 and -8 through +-13 are built. Two are not. **FR-CULL-3 — Raw-truth overlays. Built, all three bullets.** Focus peaking is `core/dr-gpu/src/focus.rs` and `ui/dr-ui/src/peaking.rs`; the raw histogram and the raw clipping @@ -115,8 +121,13 @@ labels — that comment was a forward reference and is now simply wrong, rather And `dr_catalog::bursts::choose_representative` is written and tested but bound to no gesture, so today the only override is expanding the burst. -`core/dr-catalog/src/dedup.rs` remains a different thing: re-import detection under FR-CAT-11, -matching a file against one already catalogued, not two photographs against each other. +Neither is deduplication, which is about one file held twice rather than two frames that look +alike. `core/dr-catalog/src/dedup.rs` is re-import detection under FR-CAT-11, matching a file on +a card against one already catalogued. Its other half, FR-CAT-11a, is built since 0.16.0: +`core/dr-catalog/src/duplicates.rs` groups the copies a library already holds (same root, camera, +capture instant and size), `ui/dr-ui/src/duplicates.rs` proves each group the same by digest and +compares their edits, and a group is folded onto one survivor with the others trashed in one +transaction, from the Duplicate originals review in the sidebar and in Settings. **FR-CULL-6 — Compare and survey.** Absent. No side-by-side view, no synchronised zoom or pan. This is the one of the four with no adjacent machinery at all, and it is also the one that most @@ -166,10 +177,15 @@ Two measurements say it costs more than it saves on the interactive path. Neithe about the export path or about a device under memory pressure, which is where the case for it actually lives — and that is spike S6, which has not run. -**FR-DSP-4 — Progressive refinement.** Unbuilt. FR-DSP-1's proxy rendering and TD-4's -quarter-resolution base are adjacent and are not it: both are fixed choices about what resolution to -compute at, where FR-DSP-4 asks for a first frame that is deliberately cheap and a second that -replaces it. Nothing tracks a "this frame is provisional" state. +**FR-DSP-4 — Progressive refinement. Built in 0.15.0.** While a gesture moves the canvas renders +a half-resolution draft, and the sharp frame lands once, 120 ms after the last movement: the +decision is `ui/dr-ui/src/refine.rs`, a debounce whose every draft re-arms the settle timer, driven +through simulated timelines in its tests. The draft flag reaches the interface (`canvas-draft`, +`Levels.provisional`), so the histogram dims while it describes a frame older than the one on +screen, and the last draft is kept and faded out over 150 ms when the sharp frame arrives, so +refinement is not a swap. Draft frames themselves date from 2026-08-09; what this entry missed, +and 0.15.0 finished, was a settle that waited for the gesture to stop, a provisional state the +interface could see, and a refinement that was not a jarring swap. **NFR-RES-2 — Images larger than GPU memory.** Half answered. NFR-R8's "decide explicitly" was decided on 2026-09-19: there is no CPU render pipeline, the degraded mode is the viewer on @@ -181,11 +197,42 @@ settle both this and FR-DSP-2, and there is no evidence it has run. --- +## 4a. Develop, masks and the keyboard — what the 0.15.0 and 0.16.0 work left open + +Most of what these two releases built closed a clause outright, and is listed here only so that +nobody looks for it below: perspective correction (FR-DEV-20, Vertical and Horizontal in Compose), +the crop that says when it orphans a mask (FR-DEV-17), intersection as a third mask join +(FR-DEV-19a), the keyboard vocabulary for develop (FR-DEV-16, met and held by `gestures-check` in +both directions), colour labels set, shown and filtered (NFR-A11Y-3's first example), the RAW +decoder behind a trait (FR-RAW-2), and duplicate originals (FR-CAT-11a, §2 above). What they left: + +- **FR-UI-5's pointer half.** Keys cover navigation, rating and common adjustments in every view, + and judging works in develop on the open photograph; but no develop slider takes the scroll + wheel, which the clause names. Its status note in the register says so rather than rounding up. +- **FR-RAW-2's second decoder.** The trait is built and a stub decoder is proved to reach the + scan, the preview ladder and export without a caller changing; LibRaw itself (D2) is not + built, and S7 is what would say when it is needed. +- **FR-DEV-19's M2 remainder** ([mask-editing.md §11](mask-editing.md)). The panel's Add, Subtract + and Intersect buttons join a painted part only, so an intersection with a gradient or a range is + painted where the mask should survive; per-part distance fields and folding two layers are + unbuilt. From M1, the brush has no ring drawn on the photograph and a layer's whole stroke + history is redrawn per dab. +- **FR-DEV-17's blind spot, by design.** A layer that adds a range or a region selection is never + reported, because the check has no label map to measure it with (`dr_pipeline::orphan`); a + false alarm on the common path would teach the notice to be dismissed unread. + +The desktop's scrollbars (the develop column, the grid, the sidebar, Settings, the film list and +the help sheet) are drawn only where `dr_plat::is_touch_first()` is false; on Android the lists +still scroll by flick alone, which is deliberate rather than outstanding. + +--- + ## 5. Android beyond running, and Flatpak The Android app is not a stub — it builds an APK, runs the whole application, unpacks bundled face -models, and has been measured on a tablet ([faces.md §12.1](faces.md), -[technical-debt.md TD-1](technical-debt.md)). What is missing is the platform contract around it. +models, has been measured on a tablet ([faces.md §12.1](faces.md)), and since 0.15.0 draws the +develop view zero-copy as the desktop does ([technical-debt.md TD-1](technical-debt.md), paid off). +It carries the manual and opens it in a WebView. What is missing is the platform contract around it. **FR-PLAT-AND-1 is untagged, and what it was tagged for was intent rather than code.** The requirement demands that library access be obtained *exclusively* through the Storage Access @@ -267,21 +314,29 @@ fixes users will need — is unaddressed, and there is no update mechanism of an ## 6. Accessibility and internationalisation — the hard half is done and the easy half is not -**NFR-A11Y-1 — Localisation.** `@tr(` appears **zero** times across 14,482 lines of Slint. That -number overstates the problem, because the part that is genuinely architectural was got right: -`LocalizedKey` keeps display strings out of `core/` entirely, every operation publishes a key rather -than a label, and `labels::resolve` is the single point where a key becomes text. What that single -point does, however, is a hardcoded English `match` in Rust source — so changing a translation -requires a recompile, which is the one thing the requirement explicitly forbids. There is no message -catalogue in any format, no locale-resolution rule, and no decision recorded about RTL. +**NFR-A11Y-1 — Localisation.** One screen of twenty-eight Slint files is converted: the launch +screen wraps its strings in `@tr(`, and `ui/dr-ui/build.rs` records the mechanism — Slint's bundled +translations, a `.po` per language under `ui/dr-ui/lang/`, extracted with `slint-tr-extractor` — +and why bundling rather than gettext (Android has no path a `.mo` could sit at). No `.po` exists +yet, so every build is the original English, and the rest of the interface (about 23,600 lines of +markup) is unwrapped. The part that is genuinely architectural was got right: `LocalizedKey` keeps +display strings out of `core/` entirely, every operation publishes a key rather than a label, and +`labels.rs` is the single point where a key becomes text — but that point is a hardcoded English +`match`, and `build.rs` says the Rust-side mechanism is not built. Two things the requirement asks +remain against it even when the conversion is finished: a bundled translation is compiled in, so +changing one still needs a rebuild, which the clause forbids; and there is no locale-resolution rule +and no decision recorded about RTL. -The work left is therefore smaller than it looks and entirely mechanical: a catalogue format, a load -path behind `resolve`, and `@tr(` around the Slint literals. The design it needs already exists. - -**NFR-A11Y-2 — Accessibility.** `accessible-*` appears five times in the whole interface, all five -on one control — the parameter slider in `adjust.slint` — and nothing is set from the Rust side at -all. Everything else in eighteen Slint files is unnamed to AT-SPI and TalkBack. The requirement's own -caveat, that Slint's Android accessibility needs verifying, is spike S13, which has not run. +**NFR-A11Y-2 — Accessibility. Tagged, and half tested.** `accessible-*` properties now appear on +some 127 lines across eleven Slint files — the shared controls in `widgets.slint` and +`controls.slint`, every slider, the rail, the grid's cells (named by file), the rating stars, the +labels and the duplicates review — and `ui/dr-ui/tests/ui_controls_are_accessible.rs` fails when a +shared control stops declaring its role or a slider is added without a name. The manual's recording +scripts find every control they press through the same names (`dr_ui::automation`), so a control a +screen reader cannot name is also one the manual cannot record. What is unchecked is stated in the +test: whether the words are good, and whether Android exposes any of it — spike S13, which has not +run. The requirement's other two clauses are debt with entries of their own: platform font scaling +([TD-7](technical-debt.md)) and WCAG AA contrast ([TD-6](technical-debt.md)). **NFR-A11Y-3 — Colour-independent status.** Built, and now asserted. The clipping readout pairs a marker that appears or disappears with a figure in words; the rating strip is a solid star against @@ -453,18 +508,23 @@ whether something *should* be built — which is the opposite of the order §9 a --- -## 11. Merging — specified 2026-09-19, nothing built +## 11. Merging — the panorama built, three clauses open §3.11 was written on 2026-09-19 under D18, undeferring the panorama from §7 and leaving HDR merge -and focus stacking there with their data model decided. Eleven `FR-MRG` clauses and two `NFR-MRG` -figures entered the register at once with no code behind any of them, which is why the coverage -figure fell from 83.0% to 77.2% on the same day — a specification, not a regression. +and focus stacking there with their data model decided. Its clauses entered the register with no +code behind them, which is why the coverage figure fell from 83.0% to 77.2% on that day — and the +panorama was then built in the same week: alignment, projections, the chunked composite written as +a DNG beside its sources, the auto-crop and the model's border fill. +[panorama.md](panorama.md) §11 and §13 are where it stands. -[panorama.md](panorama.md) is the design, and its §10 is the order of work. Nothing starts before -**S15**: whether rawler reads back a linear DNG the application writes, whether XFeat loads under -tract at a fixed shape, whether the working-space texture can be tapped where FR-MRG-2 needs it, -and what a chunked blend of a 100 MP composite costs on the tablet. The first two are a day each -and either can change the design, which is the reason they come first. +Three clauses carry no tag: + +- **FR-MRG-9 — the tablet.** Android runs the same code, but the stated ceiling on frame count and + source resolution, refused with a message rather than an out-of-memory kill, is not set. +- **NFR-MRG-1 — merge latency.** Measured on the desktop and inside its 60 s (panorama.md §11); + the tablet's figure is S15.4's open half, and nothing asserts either per commit. +- **NFR-MRG-2 — reproducible merges.** Nothing checks that the same sources and settings give a + byte-identical composite. ## 12. D12, which governed all of the above