Commit Graph

278 Commits

Author SHA1 Message Date
Sylvain Duchesne dc1536e8a1 chore: T01.p bloque sur l-item 2, trois items sur quatre sont clos
Items 1, 3 et 4 verifies, corriges et commits separement. Item 2 (sept symboles
invention/none/none au-dessus de la barriere polyfill-era) demande une decision de
conception que la tache reservait explicitement a un humain - deux des sept
symboles ont une reponse claire (ShapeQuery devrait etre re-annote, cinq types de
champs de EventuallyConfig devraient descendre sous la barriere) mais ShapeObservable
depend d-un fait que rien dans ce depot ne permet de verifier (la forme reelle du
futur useShapeQuery annonce). Renvoyee en todo avec l-analyse complete pour eviter
de la refaire.
2026-09-24 21:12:58 +02:00
Sylvain Duchesne 63973c27e0 fix: retirer l-annotation de provenance sur resolveScopeGraph, non publie depuis dev.5
storeRegistry.resolveScopeGraph n-existe pas : resolveScopeGraph a cesse d-etre
reexporte par surface/placement.ts (donc par le namespace storeRegistry) depuis
1.0.0-dev.5, comme ses deux voisins resolveWriteGraph et listMyEntityDocs, qui eux
n-ont jamais porte d-annotation. Une annotation @provenance qualifiee storeRegistry.
sur un symbole non publie revendique une surface qui n-existe pas.

Retire la ligne, sans toucher au commentaire JSDoc au-dessus (toujours exact comme
note interne) ni a la ligne equivalente absente sur les deux voisins.
2026-09-24 21:11:06 +02:00
Sylvain Duchesne 2d2f96f74b docs: docChangeType rejoint la Surface du contrat concept, et le bloc est epingle
docChangeType est publie depuis index.ts mais absent du bloc ## Surface du contrat
concept (celui que tire la declaration usage_ d'un consommateur) - rien ne le liait
aux exports reels, contrairement au bloc "### Today" de docs/api-contract.md.

Ajoute la signature au bloc, et ajoute un test qui epingle desormais ce bloc contre
les exports reels de l'entree, sur le meme principe que le test existant pour
docs/api-contract.md : verifie a la fois (retire temporairement l'export de
docChangeType pour confirmer que le test tombe, puis restaure).
2026-09-24 21:10:54 +02:00
Sylvain Duchesne ae44e13859 fix!: Schema et DeepSignalSet quittent la surface publiee, sans signature pour les porter
La regle qui avait deja retire ReadCap et InboxScope (un type n'est publie que si
une signature publiee l'utilise) rattrape une seconde recurrence : les reexports
`Schema` (@ng-org/shex-orm) et `DeepSignalSet` (@ng-org/alien-deepsignals) n'etaient
utilises par aucune signature publiee. DeepSignalSet n'apparaissait que dans la
prose decrivant l'enveloppe de useShape, jamais comme un type reel dans le paquet ;
Schema n'apparaissait nulle part ailleurs que dans ce reexport.

Contrairement a ReadCap/InboxScope, aucun des deux n'a d'usage interne ou ils
resteraient DEFINIS : ils disparaissent, sans etre deplaces.

Bump majeur vers 1.0.0-dev.12 (symbole publie retire), entree de version dans le
contrat concept et mise a jour de docs/api-contract.md (bloc de code, section 14,
annexe d'inventaire).
2026-09-24 21:10:32 +02:00
Sylvain Duchesne 7da4a0ef29 chore: T01.o est close, la fuite de droits est fermee apres cinq rondes 2026-09-24 20:49:19 +02:00
Sylvain Duchesne 9e00a89fe8 chore: T01.o est close, cinq rounds adverses et la garde sait echouer 2026-09-24 20:47:46 +02:00
Sylvain Duchesne a88b4497d9 fix: le dossier dit la verite sur le code, et la garde peut echouer
Cinq constats, aucun sur la frontiere des droits : le dossier et la garde.

LE MOTIF ENREGISTRE SUR `copyWithin` ETAIT FAUX, et c-etait raisonne, pas joue -
dans le meme lot que `rule_justify-a-return-value-by-running-it`, dont la
premiere application est a son propre voisin. Mesure : le vrai `readOnlyArray`
ne leve PAS, il execute et DETRUIT le premier element (["A","B","C"] devient
["B","C","C"]), tandis que `push`/`splice`/`sort`/`reverse`/`fill`/`shift`
levent a la lecture. `copyWithin` echappe a cette garde pour la raison exacte
qui le fait nommer ici : il est dans la meme liste mal etiquetee dont la garde
est faite. Le refuser est donc PLUS protecteur que la cible, pas equivalent :
une divergence deliberee, consignee comme telle, avec la mesure a cote.

LE FICHIER DOCUMENTAIT ENCORE LA REGLE QUE CE ROUND A SUPPRIMEE, et se
contredisait : deux en-tetes decrivaient le defaut par TYPE ("un compte, un
drapeau passent") et disaient que `isLoading` n-est pas nomme, alors que la
ligne 759 le nomme. Le contrat gardait les deux politiques a la fois. Une
doctrine qui survit a une reecriture est precisement ce qui egare le suivant.

LA GARDE NE VERIFIAIT PAS CE QU-ELLE ANNONCAIT. Prouve : une copie verbatim avec
{copyWithin, slice} en exceptions et `slice` retire de la liste servie passait
1 pass / 0 fail. Elle compare maintenant une PARTITION - l-ensemble effectif
amont egale l-union, les trois parts sont disjointes, l-exception vaut exactement
un membre nomme - et lit aussi les entrees SYMBOLE, invisibles a l-ancienne
expression. Verifie rouge dans les trois directions.

LA DEROGATION ECMASCRIPT ETAIT INFONDEE TELLE QUE RAISONNEE : la specification
ferme les CLES, pas les VALEURS. Un `Symbol.toStringTag` implemente en accesseur
rendant `Set(${size})` publiait le compte non filtre. Les deux cles sont
calculees a partir de la forme enveloppee. Et ce que la vue divulgue par
ailleurs - l-existence d-une cle sur la cible - est dit exactement, avec la
mesure qui montre qu-aucune presence de cle ne depend des objets.

`reduce` branchait sur `init === undefined` au lieu de l-arite.
2026-09-24 20:43:04 +02:00
Sylvain Duchesne 5c50ca67c0 docs: une affirmation sur ce qu-une valeur retourne doit etre executee 2026-09-24 20:24:24 +02:00
Sylvain Duchesne 97699c232e chore: le Result porte le round 4 2026-09-24 20:16:02 +02:00
Sylvain Duchesne a4ec2546bf fix: ce qui n-est pas DECIDE est refuse, et les deux vues portent la meme regle
LE PRINCIPE ETAIT FAUX, pas un nom manquant. "Un primitif ne peut atteindre
aucun objet, donc il passe" : or un COMPTE est un primitif qui DECRIT les
objets. Le fichier le savait deja, puisque `size` et `length` sont calcules - par
une liste de deux noms, c-est-a-dire la liste de noms interdite reapparue en
repli des que le principe a cede. Tout ce qui s-epelle autrement passait :
mesure, un porteur qui peut lire 1 objet sur 3 lisait `totalCount` a 3. Et LA
SUITE EPINGLAIT LA REPONSE QUI FUIT COMME CORRECTE, six lignes au-dessus d-un
test dont le commentaire appelle ce meme compte non filtre le defaut.

Aucun test de TYPE ne separe un primitif qui est une valeur d-un primitif derive
de la collection : un nombre est un nombre. Dit clairement, parce que la reponse
precedente pretendait le contraire. Le defaut s-inverse donc : ce que ce fichier
a DECIDE, il le sert ; tout le reste refuse, primitif ou non. C-est sur pour une
forme que personne n-a vue PAR CONSTRUCTION. Deux exceptions, aucune n-etant une
epellation du SDK : un membre que la cible ne porte pas rend `undefined`, et les
cles de protocole du LANGAGE, fermees par la specification.

LES DEUX VUES, PAS UNE. L-enveloppe avait deux pieges contre neuf : `delete
resultat.data`, `resultat.isLoading = x`, `setPrototypeOf` et
`preventExtensions` atteignaient l-objet du SDK - dont les deux derniers avaient
ete fermes sur la collection au round precedent. Les cinq pieges d-ecriture
vivent maintenant dans UN objet etale dans les deux.

`add` demandait `keep`, qui repond vrai pour un objet ne nommant AUCUN document :
juste pour une lecture, vide de sens pour une ecriture. L-objet atterrissait dans
la collection partagee, et seul l-amont l-arretait - la garantie empruntee que ce
fichier avait justement retiree un round plus tot.

Enfin `copyWithin`, que la liste amont classe non mutant a tort et que son propre
`readOnlyArray` refuse : il n-est plus servi.
2026-09-24 20:10:13 +02:00
Sylvain Duchesne 7a37e44ac0 chore: T01.g est close, sa trouvaille bloquante est devenue T01.o 2026-09-24 19:54:42 +02:00
Sylvain Duchesne e07453c205 chore: le Result porte le round 3, et la correction sur le code de sortie e2e 2026-09-24 19:43:10 +02:00
Sylvain Duchesne 6c492e7734 fix: le prototype et l-extensibilite sont des ecritures, elles aussi
Trouve en balayant le correctif de ce round plutot qu-en raisonnant dessus.
`Object.setPrototypeOf(vue, ...)` remplacait le prototype de la VRAIE
collection - apres quoi l-iterer leve pour toutes les identites, c-est le
probleme de `clear` dans une autre tonalite - et `Object.preventExtensions`,
par quoi `Object.freeze` commence, la rendait non extensible : une ecriture, et
de surcroit la projection d-index de `ownKeys` n-est licite que tant que la
cible est extensible.
2026-09-24 19:40:29 +02:00
Sylvain Duchesne eb956c880d fix: les trois mutations posent la meme question, et la vue porte ses propres ecritures
L-ASYMETRIE, une fois de plus, et c-est elle le defaut. `delete` et `clear`
interrogeaient `keep`, `add` non - ce qui rendait la phrase publiee vraie par
coincidence : le `add` non garde FABRIQUAIT l-etat que les deux autres ne
traitaient qu-a moitie. Bob plante un objet dans le document d-ALICE, ne le
revoit jamais (la vue le refiltre), la liaison bidirectionnelle le commite
la-bas, puis `clear()` le laisse derriere. Les trois posent maintenant la meme
question ; ce qu-elles repondent differe parce que les membres different :
`delete` a `false`, qui est le mot du contrat Set pour "il n-y etait pas" et qui
est vrai de la vue ; `add` n-a aucune valeur de ce sens, donc il leve.

LA VUE PORTE SES PROPRES ECRITURES. Aucun piege `set`/`deleteProperty`/
`defineProperty` : contre un tableau ordonne, `view[0] = x`, `delete view[0]` et
`view.length = 0` passaient - le dernier defaisant `clear` entierement. Ce qui
est livre est un `readOnlyArray` qui les refuse, donc la production etait
protegee PAR LA CIBLE : la vue s-appuyait sur une garantie qu-elle ne donne pas,
sur une forme qu-elle ne controle pas. C-est l-hypothese empruntee qui est
derriere chaque defaut de cette famille.

LE PROPRIETAIRE EST SERVI. 18 membres declares d-un resultat ordonne levaient
pour celui dont ce sont les donnees, plus `includes` et les sept combinateurs
d-ensemble : une fuite echangee contre une panne. Tout ce que
`@ng-org/alien-deepsignals` declare non mutant est servi sur les objets
lisibles, et un test relit le fichier livre pour que la liste restee ici ne
derive pas.

Aussi : le descripteur d-un ACCESSEUR routait vers la cible ; le faux construit
maintenant les VRAIES collections (`deepSignal`, `readOnlyArray`) au lieu d-un
Set et d-un tableau nus, ce qui le rendait plus permissif que ce qui est livre
apres l-avoir rendu plus correct au round precedent.
2026-09-24 19:38:14 +02:00
Sylvain Duchesne b778fcff02 chore: T01.o repasse en doing, le Result porte le round 2 2026-09-24 19:05:30 +02:00
Sylvain Duchesne 84699c42b5 refactor: une seule fonction decide ce qu-une mutation peut rendre
Trois copies d-un raisonnement sur une valeur de retour, c-est trois occasions
de se tromper et c-est ce qui a produit le defaut. `handBack` porte la regle
une fois : la collection devient la vue, un scalaire passe, le reste refuse.
2026-09-24 19:04:04 +02:00
Sylvain Duchesne 98e46effc2 fix: le resultat d-une commande de page suit la meme regle que tout le reste
Balayage execute sur les deux proxys apres le correctif precedent : aucun membre
de la vue de collection ne rend plus la cible, mais une commande de page de
l-enveloppe qui rendrait la collection la rendait BRUTE. `nextPage` est declare
`() => void` et ne rend rien aujourd-hui, donc ce n-est pas un defaut vivant -
c-est la meme justification jamais executee, sur l-autre proxy, et c-est
exactement le motif qu-il fallait casser. Elle s-execute toujours ; seul son
retour est tenu a la regle.
2026-09-24 19:03:10 +02:00
Sylvain Duchesne d4b392a119 chore: les dix injections de useShape passent par le faux partage
Dix sites, pas les deux que le suivi annoncait : `(() => {}) as unknown as
UseShapeLike` survivait la ou le type etait encore `any` et ou le cast ne
disait donc rien. Ils prennent le faux partage, sans cast.
2026-09-24 19:00:25 +02:00
Sylvain Duchesne c5de1cea35 fix: une mutation ne rend ni n-atteint ce que la vue cache
`Set.prototype.add` rend THIS pour permettre le chainage - EXECUTE, pas deduit,
sur le `Set` nu et sur le vrai `DeepSignalSet`, qui rendent tous deux la
collection. Les trois mutations etaient transmises liees a la cible sur le motif
ecrit juste au-dessus : "elles prennent un objet et rendent void ou un booleen,
donc elles ne laissent rien fuir". Faux pour `add`, et `add` est le PREMIER
membre que l-exemple de documentation du SDK emploie. Toutes les entrees etaient
filtrees et la sortie rendait la collection entiere.

Les deux autres rendaient bien ce que le motif disait, et AGISSAIENT au-dela de
la vue : `delete` retirait un objet dont `has` refuse meme de confirmer
l-existence - les deux se contredisaient - et `clear` vidait la collection que
toutes les identites partagent.

Une seule regle : une mutation atteint la collection dessous, mais ne rend ni
n-atteint ce que la vue cache. `add` rend la VUE, `delete` n-agit que sur ce que
la vue montre, `clear` vide la vue. L-ecriture atteint toujours le document.

Le motif commun aux cinq fuites de cette famille est une JUSTIFICATION ECRITE
SUR UNE VALEUR DE RETOUR JAMAIS EXECUTEE. Chacune etait raisonnable, aucune
n-avait ete jouee.

Aussi : la forme ordonnee rend des index la ou un Set rend des objets
(`keys`/`entries`/`forEach`), et le faux partage reproduit desormais le
`isLoading` LIVRE - inverse de sa propre declaration. L-inversion est amont ;
un faux plus correct que la cible recree l-aveuglement que cette tache enleve.
2026-09-24 19:00:19 +02:00
Sylvain Duchesne 0e2b8ba394 chore: T01.o est close, la fuite du filtre de lecture est fermee et epinglee 2026-09-24 18:44:22 +02:00
Sylvain Duchesne 71d94b2816 style: ASCII dans les lignes ecrites pour cette tache 2026-09-24 18:42:32 +02:00
Sylvain Duchesne b1b099b6c7 chore: mesurer la forme du tableau au lieu de la supposer, et retirer les any
La branche tableau se decide sur `typeof cible.length === "number"`, ce qui est
une question posee a un proxy : mesure contre le vrai
`@ng-org/alien-deepsignals`, `deepSignal(new Set(...)).length` vaut `undefined`
et ses `Object.keys` sont vides, tandis que `deepSignal([...]).length` est un
nombre. La forme ensemble n-entre donc jamais dans cette branche, et c-est aussi
celle qui ne porte aucune cle nommant un objet.

Les trois `as any` des tests ajoutes et le `Iterable<any>` du harness sont
types.
2026-09-24 18:39:33 +02:00
Sylvain Duchesne e86e249a5a fix: le descripteur d-un membre repond ce que le lire repond
Trouve en rejouant l-adversaire sur le correctif precedent, et mesure :
`Object.getOwnPropertyDescriptor(resultat, "data")` rendait la collection BRUTE,
et `(..., "subscription")` le handle que `get` refuse. Cet appel ne passe pas
par le piege `get` - ni l-etalement, ni `Object.entries`, ni `JSON.stringify`,
ni une inspection console. Une deuxieme porte sur la meme piece, ouverte par un
piege que personne n-avait ecrit. Le meme trou existait sur la vue de collection
pour la trappe dunder.

La forme ordonnee dit combien d-objets elle porte a QUATRE endroits : `length`,
`Object.keys`, `in`, et le descripteur de chaque index. Un seul etait filtre,
donc le compte de ce que le porteur ne peut pas lire ressortait par les trois
autres, et le descripteur rendait les objets eux-memes.

Les quatre pieges appellent maintenant une seule fonction `read`, pas trois
copies de la regle : une frontiere faite de `switch` paralleles est une
frontiere qui derive. Et l-index nomme une position dans ce qui est LISIBLE,
donc la forme ordonnee sert son porteur au lieu de refuser.
2026-09-24 18:35:22 +02:00
Sylvain Duchesne e16fd6a707 docs: ce que le hook du SDK rend vraiment, la ou le depot disait un set nu
Six endroits affirmaient au present que `useShape` rend un `DeepSignalSet` nu.
C-est vrai de la revision citee, faux de celle contre laquelle ce paquet est
construit - et deux de ces six PORTAIENT une decision de provenance :

- `ShapeQuery kind=invention` etait justifie par "aucun type amont ne porte
  d-etat de chargement", alors que `isLoading` en porte un. Le kind tient, mais
  pour l-autre raison : ce qui n-a de contrepartie nulle part est le troisieme
  etat, la lecture ECHOUEE, parce qu-un `orm_start_graph` refuse n-est qu-un
  `console.error` et laisse une attente eternelle a tous les niveaux.
- l-annotation `announced-not-written` de `useShapeQuery` posait la meme
  premisse fausse. La direction annoncee ne bouge pas ; sa premisse si.

Ajoutee aussi l-instruction d-injection qui ne resolvait pas : l-export `.` de
`@ng-org/orm` pointe sur `core`, qui n-exporte aucun `useShape`. Il est sur
`/react` (export par defaut) ou `reactUseShape` sur `/all`.
2026-09-24 18:28:24 +02:00
Sylvain Duchesne 3a8941159f fix: le filtre de lecture couvre l-enveloppe que le SDK renvoie vraiment
Le `useShape` publie enveloppait le resultat du hook dans le filtre ecrit pour
une COLLECTION. Depuis que `@ng-org/orm` renvoie un `UseShapeResult` autour du
set, `[...resultat]` et `.size` levaient, et `data`, `promise` et `subscription`
revenaient BRUTS - chacun tous les objets de toutes les identites, sans que rien
ne casse. Aucun faux de ce depot ne renvoyait la forme livree, donc aucune garde
ne pouvait le voir.

Deux vues au lieu d-une : celle de la collection, et celle de l-enveloppe que
`surface/use-shape.ts` applique. `data` et `promise` sont filtres, `subscription`
est refuse (c-est le handle de l-ORM, que cette surface ne publie pas), les deux
commandes de page passent.

Le defaut par TYPE et non par une liste de noms : un primitif ne peut atteindre
aucun objet et passe, un objet ou une fonction refuse tant qu-il n-existe pas de
forme filtree ici. C-est pourquoi `isLoading` n-est nomme nulle part, et pourquoi
un membre qu-une version suivante ajoutera est deja tranche. `size` et `length`
comptent ce que le porteur peut lire.

`UseShapeLike` etait `(...args: any[]) => any`, ce qui est la raison pour
laquelle les deux typechecks restaient a 0 pendant que la fuite etait ouverte :
aucune declaration ne disait ce que le hook rend. Il rend `ShapeResult`, releve
sur le `.d.ts` installe, et le vrai `reactUseShape` y est assignable sans cast.

Le faux partage `test/use-shape-fake.ts` renvoie la forme livree et c-est celui
que les deux gardes injectent - unitaire et e2e. La garantie du contrat et le
test qui l-epingle arrivent ensemble ; `1.0.0-dev.11`, majeur.
2026-09-24 18:27:02 +02:00
Sylvain Duchesne 5993a8c4a2 feat: la fuite du filtre de lecture devient la tache prioritaire 2026-09-24 17:50:28 +02:00
Sylvain Duchesne a7f9f4e18a chore: T01.k est close, la version vit dans l-ancre et non sur 94 citations
Zero annotation touchee, trois citations reparees sur 94 - Scope,
BaseType et le compte de membres de NG - et la refutation de
where/orderBy/pageSize retiree des deux feuilles.
2026-09-24 15:07:14 +02:00
Sylvain Duchesne 139d21843a fix: les trois citations que les paquets installes invalidaient
Recherche menee sur les 94 citations vers engine/ et sdk/, en comparant
l'arbre cite aux artefacts publies. Onze citations de niveau 2
(doc_subscribe, sparql_update, sparql_query, doc_create) sont
identiques de part et d'autre, ainsi que ShapeType, Schema et
DeepSignalSet. Trois ne l'etaient pas.

- Scope : la revision citee declare `graphs?` OPTIONNEL,
  @ng-org/orm@0.1.2-alpha.21 le rend requis - et c'est le paquet
  installe qui est reexporte ici.
- BaseType : la revision citee garde "@graph" en commentaire derriere un
  TODO, @ng-org/shex-orm@0.1.2-alpha.9 le declare.
- NG : le compte de membres disait 88 en neuf endroits, dont un marque
  VERIFIED et un autre annoncant une mesure contre alpha.14. Le paquet
  installe en declare 90 - 84 fonctions et 6 classes RDF - et deux des
  quatre-vingt-dix, graph_orm_next_page et graph_orm_previous_page, sont
  absents de l'arbre cite. Le compte vit desormais a un seul endroit,
  avec la version a laquelle il appartient ; ailleurs il disparait, parce
  que la phrase disait "tous les membres" et que le chiffre n'y ajoutait
  qu'une occasion de perimer.

Les deux premieres reprennent la forme que RdfOrmConfig et
OrmSubscriptionHandle portaient deja : une clause finale nommant la
version publiee la ou elle diverge de la revision citee.
2026-09-24 15:05:50 +02:00
Sylvain Duchesne 7bcb52a5fd fix: retire la refutation de where/orderBy/pageSize, lue dans le mauvais arbre
Deux feuilles affirmaient que la richesse de la config ORM est perdue au
niveau 1 - "no caller can reach any of it. The richness is real and
unreachable". La preuve invoquee etait le `json!({})` de
`Verifier::start_orm` a 213338f6, et elle ne porte pas cet enonce.

Ce qui est verifie a 213338f6 : `OrmConfig` declare where_, order_by,
select, page_size et max_active_pages ; `from_json` les analyse tous les
cinq et refuse un pageSize sans orderBy ; schema_shape_to_sparql et le
pagineur les consomment. La machinerie est implementee. Il manque un
seul saut, a l'ENTREE : start_orm ne prend aucun argument de config, et
son TODO voisin nomme le maillon absent, "use json in libwasm context".

Or ce maillon existe dans le build : @ng-org/web@0.1.2-alpha.14 publie
orm_start_graph avec SIX parametres, config en position 4, quand l'arbre
cite en declare CINQ, et @ng-org/orm@0.1.2-alpha.21 l'appelle avec six
en passant tout son objet d'options. Lus dans les artefacts publies.

Donc la config quitte le niveau 3 et est acceptee au niveau 2 aux
versions installees. Ce que le moteur derriere ce binding en fait n'est
etabli par rien d'atteignable, et c'est dit ainsi.
2026-09-24 15:05:22 +02:00
Sylvain Duchesne 4e5b703949 feat: ancre la version que les citations de provenance decrivent
Une citation nomme un chemin et un symbole, jamais une version. Or le
checkout contre lequel toutes les citations `engine/` et `sdk/` se
resolvent declare des versions inferieures a celles que ce depot
installe, donc une citation prouve "c'etait vrai a ces versions-la" la
ou un lecteur lit "c'est vrai de ce qu'on construit".

La version est une propriete du CHECKOUT, pas d'une citation : aucune
des annotations n'est touchee. Elle est enregistree une seule fois, dans
l'`ANCHOR` de provenance.test.ts, a cote du resolveur qui s'en sert.

Ce que le mecanisme verifie maintenant, aux DEUX bouts de l'ecart, parce
qu'un enonce d'ecart peut perimer par l'un ou l'autre et a deja perime
par le bout eloigne : les versions que le checkout declare (a vide quand
le checkout est absent, comme la verification de resolution), celles que
les manifestes de l'espace de travail epinglent, et celle qui est
reellement installee sous packages/polyfill.

Pas un nouveau `kind` : les kinds classent la preuve d'UN symbole,
l'anciennete de toute la preuve appartient a l'arbre.
2026-09-24 14:55:10 +02:00
Sylvain Duchesne 50a77e5a95 chore: T01.n est close, la plage de l-orm etait insatisfiable 2026-09-24 14:47:02 +02:00
Sylvain Duchesne a0bedbf696 chore: T01.n est close - les quatre peers sont fixes par resolution, le script ajoute
Digest complet dans le fichier de tache : les quatre ranges "*" du
polyfill mesurees contre le registre (orm etait le cas le pire, "*"
n'y admettait aucune version), le plancher choisi par paquet a partir
du devDependencies actuel plutot que copie de ng-e2e-helpers, le
verdict peerDependenciesMeta confirme par lecture du code (imports
type-only uniquement), et l'installation fraiche verifiee a une seule
copie de chaque @ng-org.
2026-09-24 14:46:03 +02:00
Sylvain Duchesne f417841b6a chore: ajoute le script typecheck de l'exemple notebook
examples/notebook typecheckait correctement en invocation directe mais
n'avait aucun script pour le lancer, donc rien dans le depot ne le
verifiait automatiquement. Ajoute "typecheck": "pnpm exec tsc --noEmit -p
tsconfig.json", sur le meme modele que packages/ng-e2e-helpers.
2026-09-24 14:45:15 +02:00
Sylvain Duchesne 3aa3fb2d9b fix: les quatre peers du polyfill excluaient les alphas dont il depend
packages/polyfill/package.json declarait "*" sur les quatre @ng-org/*, ce
qui en semver n'admet aucune prerelease. Mesure sur le registre : "*"
n'admettait que le 0.1.2 bru (2025) pour web/shex-orm/alien-deepsignals et
ZERO version pour orm (qui n'a jamais publie de 0.1.2 stable). Les quatre
ranges deviennent ">=<pin actuel> <0.1.2>", le plancher etant la version
dont ce paquet est effectivement construit et teste (devDependencies) et
le plafond excluant le 0.1.2 perime, sur le modele mesure par T01.l pour
ng-e2e-helpers.
2026-09-24 14:45:03 +02:00
Sylvain Duchesne 1991696b27 chore: T01.m est close, le digest porte le compte a onze et la lecon du test 2026-09-24 14:38:43 +02:00
Sylvain Duchesne cf0618a2ee chore: T01.m - le suivi sur le rejeu est clos, et sa formulation corrigee
Le Result disait « pas un defaut aujourd'hui ». La reproduction le
falsifie : la fenetre est d'un saut de micro-tache, balayee de 0 a 6, et
le test echoue sans le correctif. Le suivi devient une ronde close, avec
l'ordonnancement discriminant consigne et la severite - diagnostique,
aucune lecture ne gagne d'autorite - dite explicitement.
2026-09-24 14:36:37 +02:00
Sylvain Duchesne 7736de0d68 fix: le rejeu relit la barriere au lieu de la capturer
Dernier chemin pouvant remettre une barriere deja oubliee. Le rejeu
lisait `lastState` a la souscription et le livrait une micro-tache plus
tard ; l'etablissement peut echouer dans cet intervalle, et
`reportSetupFailure` desavoue son `State` avec lui.

La fenetre est d'exactement UN saut de micro-tache, mesuree et non
raisonnee : a zero saut l'effacement n'a pas encore eu lieu, a deux ou
plus `lastState` est deja nul donc aucun rejeu n'est mis en file et
l'arrivant ouvre une tentative neuve. `isCurrentAttempt` ne couvre pas
ce cas : cet echec n'incremente pas `attempt`, donc l'entree et la
tentative sont toutes deux encore les courantes.

DIAGNOSTIC, et cadre comme tel : `getSyncState` lit l'enregistrement et
non la poussee, donc il repond toujours `"timed-out"` et aucune lecture
ne gagne d'autorite. Ce que la livraison perimee cassait est l'attente
bootstrap de `open-repo`, qui franchissait sa barriere dessus et
journalisait `BARRIER <nuri> synced` pour une barriere qui n'existait
plus - or cette ligne de trace est celle qui distingue une absence reelle
d'une lecture non synchronisee.

J'avais ecrit ce remede puis l'avais RETIRE, faute de test qui
discrimine. L'ordonnancement ci-dessus est ce qui manquait ; le test
echoue sans le correctif et passe avec.
2026-09-24 14:36:06 +02:00
Sylvain Duchesne 6de87a9edd chore: la troisieme ronde adverse a rendu son rapport, le Result le porte
Le Result disait que la troisieme passe n'avait pas rapporte et que
56072df n'avait donc pas de revue tierce. C'est faux depuis qu'elle a
rendu : elle refute 226db18 sur exactement le point trouve par
enumeration, reproduit independamment, confirme que 56072df le corrige,
et ne trouve rien d'autre. Sa porte et son balayage anti-triche sont
consignes.

Son unique observation - le rejeu capture lastState au lieu de le
relire - a ete sondee : la remise a lieu tant que la barriere TIENT, et
aucun ordonnancement ou la capture differe d'une relecture n'a pu etre
construit. La relecture a ete ecrite puis RETIREE, faute de test qui
discrimine ; elle est consignee en suivi, pas en correctif.
2026-09-24 13:55:01 +02:00
Sylvain Duchesne c3e07d2ce4 chore: T01.m est close - une seule reponse a « ce document est-il ouvert »
Le compte des enregistrements, l'invariant, les chemins de demontage, les
deux echecs prouves devenus des tests, trois rondes adverses et ce qu'elles
ont coute, la porte verbatim et les deux non-garanties retirees.
2026-09-24 13:45:51 +02:00
Sylvain Duchesne 56072df2af fix: la barriere n'est retenue que tant que la tentative tient
Trouve en enumerant moi-meme les ecritures de `establishing` et de
`lastState` plutot qu'en croyant l'argument du commit precedent, qui
affirmait que le garde retire etait inatteignable. Il ne l'etait pas.

`isCurrentAttempt` dit que cette entree et cet appel sont encore les
bons ; il ne dit rien sur le fait que l'appel ait REUSSI. Le courtier
expedie avant de repondre, donc un `State` peut arriver apres que
l'appel a deja ete rapporte en echec - et ce module tient lui-meme pour
atteignable qu'une poussee survive a son demontage, c'est la raison
d'etre du garde sur ce callback. Mesure : la poussee tardive recreait la
barriere morte que `reportSetupFailure` venait d'oublier, sans canal
derriere, et `getSyncState` y repondait `"synced"` - donc autorite.

`lastState !== null` implique desormais qu'une tentative tient, a
l'ecriture comme a l'effacement, donc les deux ne peuvent plus diverger
et aucun lecteur n'a a le reimposer. La poussee reste diffusee aux
auditeurs : seule la BARRIERE est une affirmation sur le document.
2026-09-24 13:35:30 +02:00
Sylvain Duchesne 226db18054 fix: une barriere appartient a la tentative qui l'a produite
Seconde revue adverse, sur le correctif precedent - et elle a raison :
celui-ci MASQUAIT la barriere perimee au lieu de l'oublier.

`reportSetupFailure` effacait `establishing` et laissait `lastState`. Le
premier correctif avait ajoute « et un canal tient » du cote du LECTEUR,
ce qui ne cachait la barriere morte que tant que rien ne tenait. La
souscription suivante sur le meme fan-out remettait `establishing` a
vrai, le masque tombait - et le fan-out rejouait ce `State` au nouvel
arrivant. Mesure : l'ouverture bootstrap franchissait sa barriere en 2 ms
sur un `State` d'un appel qui avait ECHOUE, journalisait
`BARRIER ... synced`, et `getSyncState` repondait `"synced"` - donc
autorite, donc `noRowsMeansAbsent` prenait une lecture a zero ligne pour
une absence definitive. Le depot detruit que le commit precedent
pretendait empecher.

Corrige a la source : une tentative qui echoue oublie sa barriere, comme
le font deja `releaseFanOut` et `resubscribeDocs`. Le garde du lecteur
est RETIRE avec, et pas garde en ceinture-bretelles : il etait devenu
inatteignable, et c'est lui qui avait cache la faute. Apres : la barriere
est oubliee, l'ouverture attend la sienne (202 ms), `"timed-out"`.

Et le test que j'avais ecrit affirmait le defaut - `barrierCrossed`
vrai - au lieu de l'etat correct. Corrige, plus un second test qui
conduit le rejeu de bout en bout.
2026-09-24 13:32:44 +02:00
Sylvain Duchesne 434838110d test: elargir la marge du test de latence du repli borne
Le test mesurait un ecart de 35 ms (repli de 60 ms, borne a 25). Ce qu'il
constate est la difference entre « a attendu la barriere » et « n'a pas
attendu du tout » ; 200 ms de repli et une borne a 60 laissent 140 ms
entre les deux, ce qui tient sur une machine chargee. La mutation qui
doit le tuer le tue toujours.
2026-09-24 13:27:14 +02:00
Sylvain Duchesne dcf3814446 fix: trois defauts trouves par la revue adverse sur le registre derive
Chacun reproduit avant d'etre corrige, chacun epingle par un test qui
echoue si la correction est retiree.

1. Une barriere pouvait survivre a la tentative qui l'avait produite.
`getSyncState` repondait `"synced"` sur le seul `barrierCrossed`, alors
que son propre commentaire disait « sur le canal qui tient MAINTENANT » :
`reportSetupFailure` efface `establishing` et laisse `lastState`, donc un
`doc_subscribe` qui poussait un `State` puis echouait laissait une
autorite - et `noRowsMeansAbsent` prend une lecture a zero ligne pour une
absence definitive. Le registre precedent repondait `"timed-out"` la ;
strict est la lecture de cette famille, son cout est la latence, jamais
la perte.

2. `release` ne repondait pas « est-ce que j'ecoute encore ».
`resetDocSubscriptions()` largue les auditeurs et supprime l'entree sans
rien dire, donc le handle continuait de paraitre vivant ; si autre chose
souscrivait d'abord au meme document, le court-circuit sautait l'ouverture
due et le document restait tenu par un auditeur libre de partir. Le
fan-out numerote desormais ses incarnations, et l'ouverture compare.

3. Une tentative sans reponse TIENT quand meme. Elle ne comptait pas, si
bien que chaque lecture d'un document dont le `doc_subscribe` ne repond
jamais repayait le repli borne - 8 s en production, sur le chemin de la
lecture ancree. Un second `doc_subscribe` est de toute facon impossible
tant qu'un est en vol, donc la tentative fraiche n'achetait rien.

Les trois questions sont maintenant une fonction nommee, `stillStanding`,
et chacune est posee a qui detient la reponse.
2026-09-24 13:17:10 +02:00
Sylvain Duchesne 2cf42a244b docs: l'invariant nomme les deux choses que le module enregistre vraiment
L'enonce disait « seulement l'auditeur pose » ; l'enregistrement porte
aussi l'attente bornee. Les deux sont du ressort de la demande, pas du
fait - c'est ce que la phrase doit dire.
2026-09-24 13:09:36 +02:00
Sylvain Duchesne 72b4607cbe docs: les deux commentaires qui decrivaient encore le mecanisme supprime
Le JSDoc de `resetOpenedRepos` parlait encore de demonter "les
souscriptions bootstrap detenues", et celui de `getSyncState` decrivait
ses valeurs comme stockees. Les deux disent maintenant ce que le code
fait : liberer les auditeurs et desavouer les documents, et deriver.
2026-09-24 13:06:03 +02:00
Sylvain Duchesne 8338b0efd7 docs: le lecteur du barriere dit que les quatre valeurs sont derivees
Le commentaire de `namedDocumentsArrived` decrivait `open-repo` comme
gardant trois valeurs, et disait que l'attente "resout sur le State ou
sur le repli, et chacun pose sa propre valeur" - plus vrai depuis que
rien ne les stocke.
2026-09-24 13:04:52 +02:00
Sylvain Duchesne 8fe8c052da docs: dire par situation ce qu'un appelant voit changer, pas les deux ensemble
Le texte disait que les deux situations passaient de `isSuccess` a
`isPending`. Verifie : seul le second `configure()` publiait un succes
(vide ou perime). Sur un refus tardif du courtier la lecture etait deja
retenue - ce qui change est la sortie, la nouvelle tentative se
re-souscrit au lieu d'etre bloquee pour la session.
2026-09-24 13:03:33 +02:00
Sylvain Duchesne 53dc7b9c6b docs: fusionner le commentaire duplique sur la souscription bootstrap
Deux blocs disaient « la souscription est maintenue vivante pour la
session », l'un accroche a onSetupFailed et l'autre a l'appel de
souscription - exactement le defaut de commentaire orphelin que cette
tache devait corriger ailleurs.
2026-09-24 13:01:49 +02:00
Sylvain Duchesne 82be3684fa docs: corriger la justification fausse a cote de l'assertion d'etat de synchro
Le commentaire affirmait qu'aucune des deux valeurs ne permet de prendre une
lecture a zero ligne pour une absence definitive. `unknown` le permet, et
deliberement : `index-deposit.noRowsMeansAbsent` l'accepte, parce que la ou il
n'y a pas de semantique `State` il n'y a pas de barriere a avoir manquee. Les
deux valeurs ne sont pas interchangeables, et laquelle est juste depend de la
page - ce que l'assertion epingle dans les deux mondes est la meme chose : pas
`"synced"`.
2026-09-24 12:57:43 +02:00
Sylvain Duchesne de9b8a0d67 feat!: une seule reponse a « ce document est-il ouvert dans cette session »
Trois reponses coexistaient et pouvaient se contredire : un ensemble de
souscriptions bootstrap et une carte d'etat de barriere dans `open-repo`, et les
auditeurs du fan-out dans `surface/subscribe`. Les deux premieres survivaient a
la disparition de la troisieme, parce que `resetDocSubscriptions()` - ce que fait
le `configure()` publie - largue ses auditeurs sans rien dire a personne.

Il n'en reste qu'une, l'etat de l'unique souscription reelle du document, exposee
par `docChannel(nuri)`. `open-repo` n'enregistre plus que l'auditeur qu'il a pose
- « est-ce que j'ecoute encore », rien de plus - et derive `getSyncState` a chaque
lecture, si bien qu'aucune reponse ne peut survivre au canal qu'elle decrit.
Disparaissent : `held`, `syncState`, `inFlight`, `settleInFlight` et le compteur
de generation, remplace par l'identite de l'enregistrement.

Les deux echecs prouves deviennent des tests qui echouent sans le changement :
un second `configure()` apres une synchro reussie (`"synced"` au lieu de
`"timed-out"`, zero re-souscription, et `isSuccess` cote application), et un
etablissement encore en cours quand la couche de souscription est demontee - cas
que le canal `onSetupFailed` ne peut pas rattraper, puisqu'il ne rapporte que la
tentative encore attendue.

La ligne qui ne casse pas : un document synchronise qui ne contient rien repond
toujours un succes a zero objet. `1.0.0-dev.10`, les deux non-garanties
nommees sont retirees et la garantie livree porte le test qui l'epingle.
2026-09-24 12:57:43 +02:00