Document trash, collections, thumbnails, and the UI direction

Specs for the work that follows: soft delete via a MOVE that preserves the
remote id, the collection tree and smart collections, the thumbnail store,
and the derived-state folder.

Adds two design documents. ui-refinement.md names the structural gaps
between the v0.1 UI and something that feels like a photo editor.
view-composition.md proposes a view controller for the display layer,
against the 500-line run() that has become one by accretion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-09 20:36:23 +02:00
co-authored by Claude Opus 5
parent 5365123d92
commit 02b66ddfcf
6 changed files with 1335 additions and 61 deletions
+85
View File
@@ -269,6 +269,49 @@ UI; the core is unaware presentation varies.
Adding an operation therefore requires no UI change (FR-DEV-3c) unless it needs a new `WidgetKind`.
### 4.3a The presentation contract
The division of labour, stated once so neither side drifts:
> **The core declares capabilities and hints. The frontend composes.**
The core says what a parameter *is* (`ParamKind`), what an operation would
*like* (`Presentation`), and what a widget inherently *demands*
(`WidgetDemand`). It never says what is drawn, where it sits, how wide it is,
or whether it is currently visible. Those are compositional decisions and they
belong to whoever knows the window, the input modality and the platform —
which is never the core.
**Hints are plural and ordered.** `Presentation.widgets` is a list of
`WidgetKind` in descending preference. The frontend walks it and takes the
first it both implements and can afford. Falling off the end is not an error:
every parameter remains an individually addressable scalar, so plain sliders
are always the final fallback and the edit still works — merely more tediously.
That fallback is why curve points are scalars rather than an opaque blob.
**Demands describe the control, not the screen.** A hint may carry what the
widget inherently needs — two-dimensional direct manipulation, precision
pointing, a minimum useful number of simultaneous values. It must never carry
pixels, breakpoints, DPI, or a platform name. `min_width: 240px` in a
descriptor is the core making a layout decision, and a core that reasons about
pixels will eventually be wrong about a display it never saw. The frontend maps
demands onto its own thresholds; those thresholds live in `dr-ui` and may
differ per platform without the core knowing.
**Presentation state is derived, never transmitted.** Whether a section is
collapsed, whether a group contains a modified value, where a heading falls —
all of it is computed frontend-side from capabilities plus current values. The
core exposing a `group_modified` flag or a `starts_group` marker would be the
core deciding the panel has groups at all, which is a composition decision. The
frontend has the descriptors and the values; that is sufficient to derive any
of it.
**The test.** A second frontend — a CLI, a test harness, a differently-shaped
mobile UI — must be able to consume the same capability output and compose
something entirely different, without the core changing. If a core change
would be needed to lay something out differently, the boundary has been
crossed.
---
## 5. GPU architecture
@@ -444,10 +487,51 @@ mechanism rather than a last resort.
| Version | UUID | everything |
| Remote file | `oc:fileid` | server-side rename/move |
| Cache entry | `(version, kind, resolution, graph_hash)` | — |
| Person | UUID | rename, merge, re-index |
Content-hash identity is what makes FR-CAT-9's reconnection work: a moved file is recognised rather
than re-imported, and a server-side move is not a re-download of 80 MB.
A person's identity is their UUID, never their name: renaming "Mum" to "Sarah" must not create a
second person, and two devices that name the same face independently must be mergeable rather than
duplicated (FR-CULL-12). Same reasoning as collections, same mechanism.
### 6.4 People and faces
Specified by FR-CULL-8 … FR-CULL-12 and NFR-SEC-5. Sketched here because the split across the trust
boundary is an architectural decision, not a schema detail.
**Three entities.** A `face` is a detection: an image, a box, landmarks, a detector confidence, and
an embedding. A `person` is a UUID and a name. `face_person` links them, carrying a calibrated
probability and — critically — a **confirmed** flag separating what the user asserted from what the
system guessed.
That flag is the whole design. Suggestions are derived data and may be recomputed at will; a
confirmation is a user judgement and is never overwritten by a later inference pass. Conflating them
would mean a model upgrade silently rewriting the user's own labelling, which is the kind of loss
this architecture exists to prevent.
**Where each part lives, and why they differ:**
| Data | Home | Rebuildable | Rationale |
|---|---|---|---|
| Embeddings, boxes, landmarks | Catalog only | Yes — re-index | Expensive but reproducible. ARCH §6.12: derived data belongs in the disposable index. |
| Cluster assignments, suggestions | Catalog only | Yes | Inference output; changes whenever the model or calibration does. |
| Confirmed person name on an image | **Sidecar** | **No** | A human judgement, same class as a rating or keyword (FR-CAT-8). Must survive catalog deletion. |
| Person entity (UUID, name) | Catalog, synced | No | The merge identity; travels with collections under FR-CAT-7's rules. |
The asymmetry is deliberate: deleting the catalog costs an afternoon of re-indexing and loses nothing
the user typed. That is the same bargain §6.12 already makes everywhere else.
**Detection runs on proxies, not originals.** FR-CULL-8 pins this to the FR-CULL-2 preview ladder, so
face indexing consumes the same artefacts the grid already built rather than forcing RAW decodes. The
consequence for §5's GPU budget is that face inference competes with thumbnailing, not with
rendering, and NFR-ARCH-2's priority classes already express that.
**Embeddings never enter the diagnostics or crash paths** (NFR-SEC-5). This is a structural
exclusion, not a redaction rule: NFR-OPS-1's bundle is assembled from an allowlist, so a new table
does not silently become uploadable by existing.
---
## 7. Concurrency
@@ -1000,6 +1084,7 @@ Full rationale in [requirements.md §8](requirements.md). Summary:
| D10 | Single adaptive interface | Decided |
| D11 | Product positioning | Decided |
| D12 | Scope versus pace | **Open** |
| D13 | Face inference runtime and model licensing | **Open** |
---