Commit Graph

3 Commits

Author SHA1 Message Date
Sylvain Duchesne 107f9d1633 refactor(vocabulary): les noms publiés parlent la langue de la cible, et un test le tient
La correction de nomenclature du 2026-07-30 — en amont un *wallet* n'est qu'un
trousseau, ce qui possède des stores est un **user** (un *site*) — s'était faite
à la main. `walletInbox` y a échappé et a vécu des semaines, en faisant des
dégâts : le nom rendait « une inbox par wallet » évident, masquant qu'un user en
a **deux** en amont (repos de store public et protected, les deux seuls
`AddInboxCap` du moteur). Une discipline appliquée à la main en oublie un ; un
test non.

D'où `test/vocabulary.test.ts` : tout nom publié est bâti sur des mots que la
CIBLE emploie — vérifiés dans `nextgraph-rs` — ou porte un marqueur disant
POURQUOI il n'existe qu'ici (`virtual`, `physical`, `shim`, `emulated`,
`polyfill`), ce qui dit aussi quand il disparaît. Un échec n'est pas « renommer
pour faire passer le test », c'est une question : la cible a-t-elle un mot pour
ça ? la chose n'existe-t-elle qu'ici ? le mot est-il vraiment de la glue ?

Ce que le test a trouvé, et les réponses :

- `walletInbox` → `userInbox`, avec l'écart de cardinalité écrit noir sur blanc
  plutôt que caché par le nom.
- `accounts` / `AccountRecord` / `AccountStorage` → `virtualUsers` /
  `VirtualUserRecord` / `VirtualUserStorage`, module `accounts.ts` →
  `virtual-users.ts`. « account » n'est pas de la cible : c'est notre mot pour
  l'utilisateur virtuel, et le marqueur le dit désormais.
- `readModel` → la fonction `readUnion`, exposée directement. « model » n'était
  ni de la cible ni de la glue, et le namespace ne tenait qu'une fonction.
- Le reste était du vocabulaire légitime à déclarer (`subject`, `base`,
  `schema`, `connected`, le modèle réactif de l'ORM).

Corrigé au passage, sur signalement du contrat interne : l'en-tête d'`open-repo`
justifiait son correctif par un mécanisme que le source contredit. Un repo absent
de `self.repos` lève bien `RepoNotFound`
(`engine/verifier/src/request_processor.rs:264,269`). Les 0 lignes observées
viennent d'ailleurs — `Verifier::load` repeuple `self.repos` depuis le stockage
sur un profil persistant (`verifier.rs:535-560`), et notre propre `readDoc`
attrape toute erreur et rend `[]`. Le correctif est bon, le diagnostic écrit à
côté ne l'était pas.

159 tests unitaires, typecheck src/test/e2e vert, e2e 40/40 contre le broker.
2026-08-04 14:35:01 +02:00
Sylvain Duchesne 88914f50ae refactor(layout): ranger les modules par destin à la migration
Les 25 modules étaient à plat, nommés d'après ce qu'ils font mécaniquement
(`store-registry`, `read-model`, `reach`, `caps`). Rien dans l'arborescence ne
disait lesquels DEVIENDRONT le vrai SDK, lesquels tiennent lieu du travail que
le verifier fera nativement, et lesquels n'existent que parce qu'un wallet est
partagé — trois destins sans rapport.

Quatre dossiers, les deux fichiers d'entrée restant à la racine pour que
l'`exports` du paquet et le code du consommateur ne bougent pas :

- `model/` — le modèle d'adressage de la cible, transcrit : vocabulaire pur,
  pas d'I/O. Survit comme connaissance.
- `surface/` — ce que l'app touche, chaque symbole ayant un pendant cible
  documenté. Supprimé quand l'alias bascule ; le code de l'app est inchangé.
- `emulated-verifier/` — les doublures de ce que le verifier fait nativement :
  possession, dépôt des caps, frontière, non-livraison, traitement des inbox,
  registres de branche, ouverture de repo. **C'est le dossier où diverger du
  modèle est possible.** Le préfixe `emulated-` porte le sens : tient lieu de,
  jamais est — cette bibliothèque ne réside dans aucune couche de la cible,
  elle les référence.
- `shared-wallet/` — n'existe que parce qu'un wallet héberge toutes les
  identités. Aucun pendant, rien sur quoi s'aligner ; sa seule loi est de
  rester invisible depuis `surface/`. S'évapore, remplacé par rien.

`store-registry-api.ts` devient `surface/placement.ts` : il faisait déjà à la
main ce que la frontière de dossier fait structurellement — c'est la meilleure
preuve interne du bien-fondé de ce rangement.

Ce commit ne fait que déplacer et recâbler les imports (src, test, e2e). Les
scissions des modules à cheval suivent.

157 tests unitaires, typecheck src/test/e2e vert.
2026-08-04 12:46:44 +02:00
Sylvain Duchesne ae9c32e271 Align the cap emulation on NextGraph's model, and confine it to a virtual user
Two batches, verified against nextgraph-rs throughout.

P1a — the capability surface. Reading was an ACL (Map<doc, Set<principal>>),
the exact inversion of key possession. It is now possession: `capFor(nuri)` is
the only question, there is no principal parameter anywhere, and nothing turns
a bare reference into a cap. Sharing is `shareCap(cap, toInbox)`, a Link
deposit; receiving needs no operation. `Nuri` and `ReadCap` are template
literal types, so passing a bare reference where a cap belongs is a compile
error, with runtime guards behind it for JavaScript callers.

The virtual user boundary. Every access function is now confined to the
connected user, through two rules on one criterion (possession), implemented in
two places so a lapse in either is caught by the other: authorization at the
passage points, and "do not even attempt" at the callers. The polyfill's own
machinery moved to physical.ts — unguarded, never exported — which replaced an
exemption list: the machinery no longer gets waved through the guard, it calls
something the guard never saw.

Removed, as emulating capabilities the target does not have:
- discovery.ts and its global index. There is no discovery in NextGraph; you
  follow links. It also pooled user data across wallets.
- the cross-account fan-out (listEntityDocs, resolveReadGraphs, allAccounts,
  loadShim), which was cross-user enumeration by construction.
- resolveInboxAnchor, a single inbox common to every user.

Caps are now stored where NextGraph stores them, and read back rather than
recomputed: AddRepo on the store's Store branch for documents a user creates,
AddLink on its User branch for caps received. Inboxes belong to someone — the
user's own, plus one per document — and connecting a user drains them all;
that is the library's job, not the app's.

Corrections worth recording: a ReadCap is `r:`, not `:k:` (reported by
NextGraph's developer, verified in BlockRef::readcap_nuri); received caps DO
have a register (AddLink), contrary to what this repo's notes claimed; and
"wallet" upstream means keyring — what owns three stores is a user, so the
vocabulary follows.

The cap value is the constant OK: the only question the emulation answers is
whether a cap is held. P1b replaces that one constant with a real key.

After this the shape is right and the isolation is still fake. Nothing here may
be described as anonymous or private.
2026-08-03 11:22:01 +02:00