From 5945f7420ad1c20cdb7e5c28f518961ecc250061 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sat, 22 Aug 2026 14:00:13 +0200 Subject: [PATCH] Say where an uploaded original lands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FR-NC-7 specifies how bytes travel and deliberately has no opinion about where they go, and nothing else said either — so the one thing a photograph needs on arrival, a folder, was unwritten. FR-NC-7a gives it `{yyyy}/{yyyy}-{mm}-{dd}`, and two consequences the transport mechanics do not supply on their own: expansion is a pure function of capture metadata, so two devices computing a destination for the same frame agree; and the template governs placement on upload only, because the remote library is reachable by other clients and restructuring it under them is not ours to do. FR-NC-7b joins import to upload. A move-import must not erase the card until the upload is confirmed — a card erased against an in-flight transfer is the one failure in this application with no undo. Co-Authored-By: Claude Opus 5 (1M context) --- docs/requirements.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/docs/requirements.md b/docs/requirements.md index a90ecf7..950d262 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -634,6 +634,35 @@ resume or `DELETE` stranded uploads on startup. Small files (sidecars) use **bulk upload** via `POST /remote.php/dav/bulk` with a `multipart/related` body, allowing hundreds of edit-graph sidecars in a single request. +**FR-NC-7a — Remote layout.** FR-NC-7 says how bytes travel; this says where they land. An +uploaded original shall be placed under the account's library root in a directory expanded from a +date template, defaulting to `{yyyy}/{yyyy}-{mm}-{dd}` — one directory per year, one per capture +day beneath it. The template is configurable per account and uses the same token vocabulary as +export naming (FR-EXP-6), so `{date}` means the image's **capture** date and never today's; an +image with no readable capture time falls back to file mtime, and the fallback is visible in the +import report rather than silent. + +Two consequences the mechanics do not give for free: + +- **The path is derived, not remembered.** The same image uploaded twice from two devices must + compute the same destination, so expansion is a pure function of capture metadata and the + template — never of local library layout, which differs per device. +- **Existing trees are not restructured.** An image already present remotely stays where it is. + The template governs placement on upload only; DarkRoom shall not move server-side files to + conform, because the remote library is also reachable by other clients (§1.3). + +Sidecars follow their image, not the template: they are named from `oc:fileid` (FR-NC-8) and live +beside the file they describe. + +**FR-NC-7b — Ingest to remote.** Import (FR-CAT-10) and upload (FR-NC-7) shall compose: a card +import may target a Nextcloud library, in which case files are copied locally first, verified by +checksum, and only then queued for upload. The local copy is not deleted on a move-import until +the upload is confirmed — a card erased against an in-flight upload is unrecoverable, which is the +one failure in this app with no undo. + +Duplicate detection (FR-CAT-11) runs against the catalog before upload, so re-inserting a card +that was already imported transfers nothing. + **FR-NC-8 — Edit metadata sync.** Edit graphs sync bidirectionally as sidecars, one per image, named deterministically from `oc:fileid`. Each sidecar carries a monotonic revision counter, a per-device UUID, and a last-edit timestamp.