feat(client): union-read ReadCap gate + listMyEntityDocs + doc corrections

- read-model.ts `readUnion` now applies the emulated ReadCap gate (drops a
  subject when its doc is governsRead && !canRead for the current identity), so
  per-scope isolation holds by construction AND by filter.
- store-registry: `listMyEntityDocs(username, scope)` (current account only) vs
  the all-accounts `listEntityDocs` fallback (documented as the enumeration to
  avoid on the read path).
- docs: nextgraph-current-state / read-model — corrected to the SOURCE-VERIFIED
  reality that the JS SDK exposes NO open/sync-by-cap primitive
  (load_repo_from_read_cap is pub(crate)); in the mono-wallet all repos are
  already local (same session), so the anchorless union spans them with no open
  step. simulation.md: listEntityDocs+useShape({graphs}) is a fallback, not the
  read path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sylvain Duchesne
2026-07-05 20:49:01 +02:00
parent e24f52749f
commit 6a3501e700
5 changed files with 112 additions and 36 deletions
+11 -3
View File
@@ -14,7 +14,14 @@ The governing constraints (all verified in `nextgraph-rs`, cited there):
- `sparql_query` with **no anchor** → the **LOCAL UNION** of all opened graphs;
with an anchor → **one** repo. Union is **read-only**.
- A repo is queryable **only after it is opened/synced** (needs its NURI + ReadCap;
no store-level read inheritance).
no store-level read inheritance). **VERIFIED (T03.k):** the current JS SDK exposes
**no primitive that syncs an *unknown* repo**`sparql_query`/`doc_subscribe`/
`orm_start_graph` all resolve via `self.repos.get().ok_or(RepoNotFound)` and only
touch a repo already present; the real loader `load_repo_from_read_cap` is
`pub(crate)`, unexposed. In THIS mono-wallet polyfill that is fine: every account's
docs are `doc_create`d in the SAME session, so they are all already in `self.repos`
and the anchorless union spans them with no per-doc open needed. The open step
becomes a real broker sync only at the multi-store migration.
- **No reactive union query**, and the reactive ORM **hangs** if handed a per-entity
/ unsynced graph fan-out (`RepoNotFound` aborts `orm_start_graph`).
@@ -128,8 +135,9 @@ explicit `GRAPH ?g` body spans every opened graph independently of the anchor.
The anchor's "one repo" restriction is observable only for a body that reads the
**default graph** (no `GRAPH` wrapper). The read model never needs the anchored
form for listing — it uses the anchorless `GRAPH ?g` union — so this does not
affect it. (The per-doc **open** step in `read-model.ts` uses an anchored `ASK`
purely for its side effect of opening the repo, not to restrict a read.)
affect it. (The per-doc "open" step in `read-model.ts` uses an anchored `ASK`
only to CONFIRM presence — it cannot sync an unknown repo, see the VERIFIED note
above; a repo absent from `self.repos` throws `RepoNotFound` and is skipped.)
## Implementation — `read-model.ts`