fix: quatre écarts entre la surface publiée et ce que NextGraph déclare

Un audit de la surface contre la source amont en a trouvé cinq ; voici les
quatre mécaniques. La cinquième — l'adresse d'inbox, qui traverse sept symboles
— relève du dessin et reste ouverte.

L'identifiant de session bloquait. Amont le déclare string | number
(sdk/js/web/src/index.ts:16) et le binding désérialise un u64 ; nous exigions
une chaîne. Une application ne pouvait donc pas passer la valeur que le SDK
venait de lui remettre. Élargi à ce qu'amont déclare, sur toute la chaîne, et
jamais converti : une chaîne échoue pour de vrai (Deserialization error of
session_id JsValue("1"), observé).

sparqlUpdate annonçait Promise<void> alors qu'il relayait DÉJÀ les commits.
C'était donc un mensonge de typage, pas un comportement — et la doublure de test
qui rendait undefined, un état que le vrai broker ne produit jamais, est ce qui
l'a laissé sans contradicteur.

ng était publié en Record<string, any>, ce qui perdait les 88 membres typés
d'amont — 88, pas 77 : le chiffre de notre propre documentation était faux.

Et materialize, second nom publié de read, sans appelant ni contrepartie amont,
est retiré.

docs/api-contract.md qualifiait docs.* de passthrough « 1:1 ». C'était faux sur
les deux premiers points. Corrigé, pas complété : un document qui se déclare
vérifié et qui ment est pire qu'un document absent, parce qu'on cesse d'aller
voir.

Une déviation assumée : amont type le retour en any, interdit ici ; on rend
unknown, comme sparqlQuery le fait déjà pour le même any amont.
This commit is contained in:
Sylvain Duchesne
2026-08-14 10:00:40 +02:00
parent e32b6d04fc
commit 12eba6eea6
17 changed files with 227 additions and 99 deletions
@@ -95,15 +95,15 @@ export function sessionIsComing(): boolean {
*
* ── The session id is RELAYED, never rebuilt — and that is load-bearing ────
* Upstream declares it `string | number` (`Session`, `index.d.ts:266`) and the broker
* returns a NUMBER; the whole chain below here types it `string` and hands it to `ng.*`,
* returns a NUMBER; the chain below here now types it the same way and hands it to `ng.*`,
* whose binding takes it as-is. So this reads the field and passes it on untouched. It is
* not a detail: normalizing it to a string was written here first, and the applicative e2e
* refused every call in the batch with `Deserialization error of session_id JsValue("1")`
* — the wasm side deserializes the id by its own type, and a stringified number is not it.
*
* The `string` in the declared shape is therefore inherited, not asserted: the inaccuracy
* is the chain's and predates this module (every consumer's thunk declared it the same way
* and relayed the same value). Widening it belongs to the chain, not to the capture.
* The chain used to narrow it to `string`, which made the value the SDK hands an application
* unpassable through this library. Widened to upstream's own declared type on 2026-08-14, at
* the capture and along every hop that relays it; the id is still only ever RELAYED.
*/
export function captureSession(event: unknown): boolean {
if (typeof event !== "object" || event === null) return false;
@@ -116,7 +116,7 @@ export function captureSession(event: unknown): boolean {
protected_store_id: protectedStoreId,
public_store_id: publicStoreId,
} = session as {
session_id?: string;
session_id?: string | number;
private_store_id?: string;
protected_store_id?: string;
public_store_id?: string;