Treat a placeholder as the photograph, not as a one-byte file
The folder connector was pointed at a Nextcloud VFS tree and got three things wrong, the first of which loses work. **A dehydrated sidecar read as absent.** `a.drsc` does not exist when the client has dehydrated it — only `a.drsc.nextcloud` does — so `get` missed, `.ok()` swallowed the `NotFound`, and the sidecar writer took that for "there is no sidecar yet" and wrote a fresh document over the existing one. Every edit another device had put there went with it. That function's own doc comment calls this the exact loss the format's unknown-key preservation exists to prevent. **A stub was catalogued as a 1-byte image**, and ARCH §9.0 measured this machine at 121,785 placeholders against 10,267 real files — so a folder library on a synced tree was ~92% broken rows. **Identity changed on hydration**, so downloading a photograph looked like a delete and an add, orphaning its thumbnail and its face rows. Entries now carry the photograph's own name and a `materialised` flag; `get` on a stub returns the new `RemoteError::NotMaterialised`, which is distinct from `NotFound` precisely because the sidecar writer must treat them differently — it fetches the sidecar and merges, or leaves the entry queued. Hydration is a **borrow**. `BorrowPool` records what was on disk before it asked, so `release_all` dehydrates only what a pass brought and leaves what the user already had. Reference counted: the thumbnail pass and the face pass meet on the same RAW, and without counting the first to finish dehydrates the file the second is reading. A borrow against a plain folder or a server does nothing, so a pass written for VFS runs everywhere. Releasing means asking the client to dehydrate and never deleting: a deletion inside a synced tree propagates to the server and removes the photograph from every device. Not a second backend — the capability is per *connection*, not per type, since the same folder hydrates only while the client runs. The convention arrives through a detector the registry supplies, so `dr-sync-folder` still knows nothing about any client's protocol. ARCH §9.0a records this as an amendment: finding 3 rejected hydration because it costs 100× a range read, and that comparison assumed a connector was available. A folder library has none.
This commit is contained in:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user