Untag FR-PLAT-AND-1 from the two places that record its absence

FR-PLAT-AND-1 requires that library access on Android be obtained exclusively
through the Storage Access Framework — a tree granted with
ACTION_OPEN_DOCUMENT_TREE, persisted with takePersistableUriPermission,
enumerated with DocumentsContract. None of those three appears anywhere.

What carried the tag was a type and a negation.

`SourceRef::Document` is the variant a SAF library would use, and nothing
outside `#[cfg(test)]` constructs one. `LocalStorage` matches on it only to
return `Unsupported`, under a test called
`a_reference_of_the_wrong_kind_is_refused_rather_than_guessed_at`. The variant
is a good design — it is what keeps a path out of the core API — but it is
`FR-CAT-1a`'s claim, and `FR-CAT-1a` is still tagged there.

`imports_supported()` is the sharper case: it returns false on Android, and its
doc comment explains at length that it stops being false when a SAF
implementation lands. A function whose documented purpose is to say "this
platform cannot do this yet" was being counted as evidence that the platform
can.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-30 10:18:45 +02:00
co-authored by Claude Opus 5
parent 421a47f1eb
commit 62e585844a
3 changed files with 14 additions and 9 deletions
+1 -1
View File
@@ -51,7 +51,7 @@ pub struct CollectionId(pub u64);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct FolderId(pub u64);
/// TRACES: FR-CAT-1a | FR-PLAT-AND-1
/// TRACES: FR-CAT-1a
/// An opaque, re-resolvable reference to source image data.
///
/// **Never a filesystem path.** Android's Storage Access Framework provides no
+12 -7
View File
@@ -177,13 +177,18 @@ The Android app is not a stub — it builds an APK, runs the whole application,
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.
**FR-PLAT-AND-1 is tagged and should not be relied on.** The requirement demands that library access
be obtained *exclusively* through the Storage Access Framework. There is no SAF code: no
`ACTION_OPEN_DOCUMENT_TREE`, no `takePersistableUriPermission`, no `DocumentsContract`. The two tags
rest on a `SourceRef::Document` variant that nothing constructs and a volumes helper, which is the
"plumbing a future feature would use" case [CONTRIBUTING.md](../CONTRIBUTING.md) and
[code-health.md CH-4](code-health.md) both warn about. Android reaches a library through a Nextcloud
account or a folder, over paths, like the desktop.
**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
Framework. There is no SAF code: no `ACTION_OPEN_DOCUMENT_TREE`, no `takePersistableUriPermission`,
no `DocumentsContract`. Its two tags rested on a `SourceRef::Document` variant constructed only
inside `#[cfg(test)]` — `LocalStorage::open` refuses it, and the test that proves so is named
`a_reference_of_the_wrong_kind_is_refused_rather_than_guessed_at` — and on
`dr_plat::imports_supported`, which *returns false on Android* and whose own documentation says it
"stops being false when a SAF implementation lands". The second tag documented the absence of the
thing it was counted as evidence for. Both have been removed; this is the "plumbing a future feature
would use" case [CONTRIBUTING.md](../CONTRIBUTING.md) and [code-health.md CH-4](code-health.md) both
warn about. Android reaches a library through a Nextcloud account or a folder, over paths, like the
desktop.
That has a consequence for the rest of the cluster: **FR-PLAT-AND-2** — detecting the loss of a
granted tree permission and marking images offline rather than deleting rows — cannot be built until
+1 -1
View File
@@ -59,7 +59,7 @@ impl Volume {
}
}
/// TRACES: FR-CAT-10 | FR-PLAT-AND-1 | NFR-PORT-1
/// TRACES: FR-CAT-10 | NFR-PORT-1
/// Whether an import can reach a card on this platform at all.
///
/// **Not the same question as "did [`volumes`] find anything".** An empty list