Commit Graph

52 Commits

Author SHA1 Message Date
Sylvain Duchesne cdc09a1a1d refactor(api): l'application ne nomme plus son identité — elle l'apprend
Lot C de la revue adverse. Cinq corrections, dont une qui change la forme de la surface.

**L'application ne pouvait pas obtenir son identité par l'API.** `ensureIdentity()`
rendait `void`, `getCurrentUser` n'est plus publié — et pourtant `createEntityDoc(id, …)`
et `listMyEntityDocs(id, …)` l'exigeaient. L'app d'exemple s'en sortait en lisant
`localStorage["ng-eventually:identity"]` et le paramètre `?ng-id`, deux constantes
PRIVÉES du portail d'accès. Une frontière qu'aucun consommateur ne devrait voir, et
encore moins dont il devrait dépendre.

Vérifié au niveau 2 avant de trancher : `session_start(wallet_name, user_id)` prend
l'identité — donc en amont l'application la DÉTIENT, elle la tient du portefeuille
qu'elle a ouvert. Ici c'est le portail qui la choisit, donc c'est au portail de la
rendre. Deux changements, tous deux vers la cible :

- `ensureIdentity()` rend l'identité qu'il a établie ;
- `createEntityDoc(scope)`, `listMyEntityDocs(scope)`, `resolveWriteGraph(scope)`
  perdent leur paramètre d'identité. En amont `doc_create(session_id, …)` ne porte
  aucun utilisateur : une session EST celle d'un utilisateur. Passer la sienne à chaque
  appel de placement était un geste sans successeur.

L'application garde l'identité pour l'afficher, et ne la passe plus à rien.

**`inbox.share` provisionnait un destinataire inexistant.** Une faute de frappe créait
les trois stores et l'inbox de ce nom, et la clé atterrissait où personne ne regarde —
sans la moindre erreur. En amont on ne peut pas viser un nom qu'on invente : un dépôt est
scellé vers une clé d'inbox qui vous est parvenue par un contact entrant. Refuser est
fidèle ; provisionner était l'invention.

**`createEntityDoc` avalait l'échec de ses deux écritures** et rendait quand même une
référence — le document n'était dans aucun store, donc la session suivante ne le listait
pas et sa lecture rendait vide, en silence. Il lève maintenant, comme `doc_create` en
amont propage les siennes.

**Deux entrées prenaient `Nuri` au lieu de `NuriLike`** (`inbox.watch`,
`openDocumentInbox`), ce qui contredisait la raison même pour laquelle aucune garde de
type n'est publiée. Et **deux messages d'erreur nommaient des symboles retirés**
(`storeRegistry.documentInboxAddress`, `setCurrentUser`) : une erreur qui envoie vers une
fonction inexistante est pire qu'une erreur muette.

Contrat d'API et feuille `contract_sdk-surface` mis à jour ; `readForDocument` et le refus
de `share` obtiennent enfin leur règle en §9.

189 tests unitaires, e2e 40/40 et applicatif 12/12.
2026-08-10 10:31:15 +02:00
Sylvain Duchesne 30f6263db5 docs(concept): le contrat entre le polyfill et l'application qui l'utilise
Amorce le système `concept` dans ce dépôt et ouvre `app-contract` — la frontière
entre cette bibliothèque et les applications qui la consomment.

C'est un contrat **inter-dépôts** et ce dépôt en est le FOURNISSEUR : les applications
vivent ailleurs et tireront `sdk-surface` d'ici. D'où le type `contract_`, ses cinq
sections obligatoires, et l'inscription dans `.project/contracts.yaml` — c'est
l'inscription qui publie.

Trois feuilles :

- **`contract_sdk-surface`** — l'engagement, écrit du point de vue de l'appelant.
  Ce qu'il peut tenir pour acquis : permissif en entrée et précis en sortie ; toute
  référence rendue est NUE, aucun appel ne rend jamais de clé ; lire est la possession,
  écrire est la propriété ; donner à lire est un seul acte et le destinataire n'appelle
  rien ; un dépôt s'adresse à une inbox, jamais à un document ; `ensureIdentity()` est
  toute la connexion ; et `configure` est le seul appel qu'il supprimera.
  Ce qu'il ne doit PAS tenir pour acquis, dit aussi crûment : aucune confidentialité,
  rien de « par lecteur » sur un document public, aucune révocation, aucune écriture
  déléguée, et les références ne voyagent que dans un déploiement.

- **`rule_would-the-caller-unlearn-it`** — le test qui décide de tout : est-ce que
  ceci ferait apprendre à l'appelant quelque chose qu'il devra DÉSAPPRENDRE ? Avec les
  deux tells que la revue de ces jours-ci a rendus concrets : une exception nommée
  cesse d'en être une dès qu'on la publie, et un symbole gardé parce qu'il était là
  n'est pas une décision.

- **`knowledge_what-an-app-deletes-at-migration`** — les deux destins d'un symbole
  publié, le cas intermédiaire d'`ensureIdentity` (substance jetée, site d'appel
  conservé), et le fait que la liste de suppression n'est plus portée par un chemin
  d'import depuis la fusion des entrées : une garantie mécanique remplacée par une
  garantie documentaire, dont seule la moitié est tenue par un test.

Le vocabulaire du concept fixe trois termes que ce projet a déjà payé cher :
`reference` (jamais « lien »), `ReadCap`, `polyfill-era`. `lint` est conformant.

Reste à décider : ce dépôt n'a pas de `CLAUDE.md` racine, donc l'`AGENTS.md` généré
n'est chargé nulle part.
2026-08-10 09:25:01 +02:00