Merge master: pluggable storage, and a name that anchors
Conflicts were docs/traceability.md alone, and it is generated — so it was regenerated rather than hand-merged. dr-face was untouched on the other side; ui/dr-ui/src/faces.rs and identity_ui.rs auto-merged, the first around recluster's anchoring and the second around load_faces. Worth recording because the two branches met on the same problem from different ends. Master's "Let a name hold a group together" is the fix for the sixteen Catherines — fourteen of them empty — that this branch found while measuring the library and reported without fixing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+44
-3
@@ -647,6 +647,25 @@ proxies, then thumbnails. **Metadata and sidecars are never evicted** — they a
|
||||
extension, and size, so users do not know what they actually have. Sync failures of legibility are
|
||||
more damaging than failures of transport.
|
||||
|
||||
**FR-NC-6d — Placeholder libraries.** Where a library is a folder kept by a sync client in
|
||||
virtual-files mode, the app shall treat a placeholder as *the photograph, not downloaded* — never as
|
||||
a one-byte file and never as a missing one.
|
||||
|
||||
- A placeholder is catalogued under the photograph's own name, with an identity that does not change
|
||||
when it is downloaded
|
||||
- Reading one yields a distinct, actionable error; it shall **not** be reported as absent, because
|
||||
the sidecar writer creates a new document when a sidecar is absent and would discard the existing
|
||||
one (FR-CAT-8)
|
||||
- Its size is reported as unknown rather than as the stub's byte count
|
||||
|
||||
Where the client offers hydration, content may be fetched **as a borrow**: a file is returned to the
|
||||
state it was found in, so a pass releases what it downloaded and leaves alone what the user already
|
||||
had. Releasing means asking the client to dehydrate — **never deleting**, which inside a synced tree
|
||||
would propagate to the server and remove the photograph everywhere.
|
||||
|
||||
Hydration is whole-file and shall never serve browsing (ARCH §9.0 finding 3, §9.0a). It is for the
|
||||
originals tier and for passes the user has been quoted a cost on and has agreed to.
|
||||
|
||||
**FR-NC-7 — Upload.** Files above 5MB use **chunked upload v2** against
|
||||
`/remote.php/dav/uploads/<userid>/`: `MKCOL` to create the upload folder, `PUT` each chunk, then
|
||||
`MOVE` the `.file` pseudo-entry to the destination. Chunks are 5MB–5GB and named 1–10000.
|
||||
@@ -731,9 +750,16 @@ returns.
|
||||
WebDAV `SEARCH` (RFC 5323) against `/remote.php/dav/` filtered by mimetype and paginated via
|
||||
`d:limit`/`d:nresults`, in preference to walking thousands of folders with PROPFIND.
|
||||
|
||||
**FR-NC-12 — Backend independence.** Sync shall be implemented against a backend interface, with
|
||||
Nextcloud as the only implementation in v1. No protocol detail specific to Nextcloud may appear
|
||||
outside its connector.
|
||||
**FR-NC-12 — Backend independence.** Sync shall be implemented against a backend interface. No
|
||||
protocol detail specific to any one backend may appear outside its connector, and no layer above
|
||||
the interface may name a connector — with the single exception of the registry that constructs them
|
||||
(`dr_ui::remote`).
|
||||
|
||||
A trait over operations is not sufficient on its own, and the first release proved it: `dr-ui`
|
||||
constructed the Nextcloud backend directly in seven files, an account *was* a server URL beside a
|
||||
DAV user id, and the local cache directory was named after a hostname. Independence requires four
|
||||
things — operations, declared capabilities, an account model with no server in it, and a
|
||||
registration mechanism (ARCH §8.0, `docs/storage.md`).
|
||||
|
||||
Backends **declare capabilities** rather than conforming to a lowest common denominator, because
|
||||
the property that makes Nextcloud sync fast — directory ETags propagating up the tree, so an
|
||||
@@ -748,6 +774,21 @@ Where a capability is absent the app shall **degrade visibly, not silently**:
|
||||
- Without conditional writes, sidecar conflict detection falls back to revision comparison, which
|
||||
narrows but does not close the race; this is surfaced as a reduced-safety mode
|
||||
|
||||
**FR-NC-13 — Folder libraries.** A library shall be openable as a **plain directory** — a local
|
||||
disk, a network mount, an external drive, or a folder another client already syncs — with no
|
||||
account, no server and no credential.
|
||||
|
||||
This is a requirement rather than a convenience for three reasons. It is what a photographer with
|
||||
an archive drive and no server actually has. It is the only route that works where no secrets
|
||||
daemon exists, which FR-NC-2 otherwise treats as a degraded mode. And a second connector is the
|
||||
only way to keep FR-NC-12 honest: an interface with one implementation cannot be shown to be an
|
||||
interface.
|
||||
|
||||
The folder connector shall declare its capabilities truthfully rather than flatteringly — in
|
||||
particular it shall **not** claim propagating directory ETags, because a POSIX directory's mtime
|
||||
describes its own entry list and nothing beneath it, and a backend that claimed otherwise would
|
||||
hide edits rather than merely run slowly (ARCH §8.4a).
|
||||
|
||||
### 3.8 Platform integration
|
||||
|
||||
#### Android
|
||||
|
||||
Reference in New Issue
Block a user