2026-10-06 13:12:23 -04:00
|
|
|
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
|
|
|
|
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
|
|
|
|
|
|
|
|
|
// Les correctifs du journal (§ 8.6, § 8.9). Un correctif est la liste des
|
|
|
|
|
// opérations qui mènent d'une charge à une autre ; le journal en porte un
|
|
|
|
|
// dans chaque entrée qui n'est pas un instantané.
|
|
|
|
|
//
|
|
|
|
|
// Une opération pose une valeur au bout d'un chemin, ou retire
|
|
|
|
|
// l'enregistrement qu'il désigne : { op: 'poser', chemin, valeur } ou
|
|
|
|
|
// { op: 'retirer', chemin }. Un chemin est une liste d'étapes depuis la
|
|
|
|
|
// charge : une clé d'objet, chaîne ; un rang de liste, entier ≥ 0 ; ou
|
|
|
|
|
// { id }, qui désigne dans une liste d'enregistrements à identifiant —
|
|
|
|
|
// participants, tables, propositions — l'enregistrement de cet identifiant,
|
|
|
|
|
// et non une place.
|
|
|
|
|
//
|
|
|
|
|
// Les deux fonctions travaillent sur les copies canoniques de canonique.js :
|
|
|
|
|
// un rang y désigne une place de l'ordre canonique, que la charge seule
|
|
|
|
|
// détermine, et non de l'ordre dans lequel une liste s'est remplie en
|
|
|
|
|
// mémoire. Un correctif calculé depuis une charge s'applique ainsi à toute
|
|
|
|
|
// charge qui lui est égale à l'ordre près, quelle que soit la façon dont
|
|
|
|
|
// l'une ou l'autre s'est construite.
|
|
|
|
|
//
|
|
|
|
|
// Le correctif reste local (§ 8.9). Une personne qui prend, au même tour
|
|
|
|
|
// d'une proposition, la place d'une autre à une autre table touche les deux
|
|
|
|
|
// listes de ces tables et rien d'autre : une entrée de chacune quand chaque
|
|
|
|
|
// personne prend le rang de l'autre, ce que garde l'ordre des sièges quand
|
[ADD] storage: empty chair in a table list when seats are assigned
The operator plans, the room is not tracked live: removing a person from
the retained plan must free her chair and leave every other seat where it
is. With seats assigned, a table list may now hold null before its last
occupant, seat 1 included, and never end on it; without assignment no
list holds null. Validation, canonical form, patches and the named form
handle it, and the delivered demos keep their bytes.
Checked: 1549 node and 58 node-long tests; depot.js, journal.js at 100 %.
--- FR ---
[ADD] stockage : chaise vide dans une liste de table aux sièges attribués
L'opérateur planifie, la salle ne se suit pas en direct : retirer une
personne du plan retenu doit libérer sa chaise et laisser tout autre siège
à sa place. Sièges attribués, une liste de table peut porter null avant son
dernier occupant, siège 1 compris, jamais à la fin ; sans attribution,
aucune liste ne porte null. Contrôle, forme canonique, correctifs et forme
nommée le traitent, et les démonstrations livrées gardent leurs octets.
Vérifié : 1549 node et 58 node-long ; depot.js, journal.js à 100 %.
Assisted-by: Claude Opus 5.5
2026-10-07 02:31:26 -04:00
|
|
|
// ils sont attribués ; une personne qui quitte sa chaise sans être
|
|
|
|
|
// remplacée y laisse null, le marqueur d'une chaise vide, une pose au rang
|
|
|
|
|
// de la chaise. Sans attribution, chaque liste se trie par
|
2026-10-06 13:12:23 -04:00
|
|
|
// identifiant, et l'entrée qui change de rang décale celles qu'elle
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
// franchit ; une liste d'identifiants de même longueur se pose alors
|
|
|
|
|
// entière dès que ses poses rang par rang pèsent plus que cette pose, et
|
|
|
|
|
// l'échange ne coûte jamais plus que les deux listes posées entières. Une
|
|
|
|
|
// liste qui change de longueur se pose entière.
|
2026-10-06 13:12:23 -04:00
|
|
|
//
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
// Une charge que rend appliquer, l'analyse l'admet comme celle qu'il
|
|
|
|
|
// reçoit, et chacune de ses propositions suit sa règle, comme dans la
|
|
|
|
|
// charge que rend examiner : une valeur posée suit la règle de sa place, sa
|
|
|
|
|
// forme entière (premiereFaute), jusque dans les propositions et le retenu,
|
|
|
|
|
// que l'analyse ne lit que comme conteneurs (§ 8.9). Le retenu posé entier
|
|
|
|
|
// fait seul exception. Un retenu que sa règle refuse, que la lecture admet
|
|
|
|
|
// et que le contrôle des placements garde, canonique.js le recopie hors du
|
|
|
|
|
// schéma, et difference le pose entier : à sa place, seul son conteneur se
|
|
|
|
|
// contrôle (premiereFauteEnPlace), et le journal le libère, le remet en
|
|
|
|
|
// place ou le remplace tel quel, quelle que soit sa faute. Sous un tel
|
|
|
|
|
// retenu, rien ne se pose : difference ne descend pas en lui.
|
2026-10-06 13:12:23 -04:00
|
|
|
import { canoniser, retenuHorsDeSaRegle } from './canonique.js';
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
import { SCHEMA, premiereFaute, premiereFauteEnPlace } from './document.js';
|
2026-10-06 13:12:23 -04:00
|
|
|
import { ErreurStockage } from './erreurs.js';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Une étape d'un chemin : la clé d'un objet, le rang d'un élément de liste,
|
|
|
|
|
* ou { id }, l'enregistrement de cet identifiant dans une liste
|
|
|
|
|
* d'enregistrements à identifiant.
|
|
|
|
|
* @typedef {string|number|{id: number}} Etape
|
|
|
|
|
*
|
|
|
|
|
* @typedef {Object} OperationPoser
|
|
|
|
|
* @property {'poser'} op
|
|
|
|
|
* @property {Etape[]} chemin non vide
|
|
|
|
|
* @property {*} valeur valeur JSON, null comprise
|
|
|
|
|
*
|
|
|
|
|
* @typedef {Object} OperationRetirer
|
|
|
|
|
* @property {'retirer'} op
|
|
|
|
|
* @property {Etape[]} chemin non vide, terminé par { id }
|
|
|
|
|
*
|
|
|
|
|
* @typedef {OperationPoser|OperationRetirer} Operation
|
|
|
|
|
*/
|
|
|
|
|
|
[ADD] storage: empty chair in a table list when seats are assigned
The operator plans, the room is not tracked live: removing a person from
the retained plan must free her chair and leave every other seat where it
is. With seats assigned, a table list may now hold null before its last
occupant, seat 1 included, and never end on it; without assignment no
list holds null. Validation, canonical form, patches and the named form
handle it, and the delivered demos keep their bytes.
Checked: 1549 node and 58 node-long tests; depot.js, journal.js at 100 %.
--- FR ---
[ADD] stockage : chaise vide dans une liste de table aux sièges attribués
L'opérateur planifie, la salle ne se suit pas en direct : retirer une
personne du plan retenu doit libérer sa chaise et laisser tout autre siège
à sa place. Sièges attribués, une liste de table peut porter null avant son
dernier occupant, siège 1 compris, jamais à la fin ; sans attribution,
aucune liste ne porte null. Contrôle, forme canonique, correctifs et forme
nommée le traitent, et les démonstrations livrées gardent leurs octets.
Vérifié : 1549 node et 58 node-long ; depot.js, journal.js à 100 %.
Assisted-by: Claude Opus 5.5
2026-10-07 02:31:26 -04:00
|
|
|
// Règles de la charge, du retenu, d'un identifiant et de l'occupant d'une
|
|
|
|
|
// liste de table — un identifiant, ou null pour une chaise vide — dans le
|
|
|
|
|
// schéma du fichier d'état.
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
const regleDuChamp = (regle, cle) => regle.champs.find(([nom]) => nom === cle)[1];
|
|
|
|
|
const CHARGE = regleDuChamp(SCHEMA, 'charge');
|
|
|
|
|
const RETENU = regleDuChamp(CHARGE, 'retenu');
|
[ADD] storage: empty chair in a table list when seats are assigned
The operator plans, the room is not tracked live: removing a person from
the retained plan must free her chair and leave every other seat where it
is. With seats assigned, a table list may now hold null before its last
occupant, seat 1 included, and never end on it; without assignment no
list holds null. Validation, canonical form, patches and the named form
handle it, and the delivered demos keep their bytes.
Checked: 1549 node and 58 node-long tests; depot.js, journal.js at 100 %.
--- FR ---
[ADD] stockage : chaise vide dans une liste de table aux sièges attribués
L'opérateur planifie, la salle ne se suit pas en direct : retirer une
personne du plan retenu doit libérer sa chaise et laisser tout autre siège
à sa place. Sièges attribués, une liste de table peut porter null avant son
dernier occupant, siège 1 compris, jamais à la fin ; sans attribution,
aucune liste ne porte null. Contrôle, forme canonique, correctifs et forme
nommée le traitent, et les démonstrations livrées gardent leurs octets.
Vérifié : 1549 node et 58 node-long ; depot.js, journal.js à 100 %.
Assisted-by: Claude Opus 5.5
2026-10-07 02:31:26 -04:00
|
|
|
const PROPOSITION = regleDuChamp(CHARGE, 'propositions').element;
|
|
|
|
|
const IDENTIFIANT = regleDuChamp(PROPOSITION, 'id');
|
|
|
|
|
const OCCUPANT = regleDuChamp(regleDuChamp(PROPOSITION, 'placement').element, 'sieges').element.element;
|
2026-10-06 13:12:23 -04:00
|
|
|
|
|
|
|
|
// Vrai pour un objet qui n'est ni null ni une liste.
|
|
|
|
|
const estObjet = (valeur) => typeof valeur === 'object' && valeur !== null && !Array.isArray(valeur);
|
|
|
|
|
|
|
|
|
|
// Vrai quand chaque élément de liste est un objet dont l'id est un entier
|
|
|
|
|
// exact, et qu'aucun id ne s'y répète : la seule forme où { id } désigne un
|
|
|
|
|
// enregistrement et un seul. Une copie canonique ne porte que les clés du
|
|
|
|
|
// schéma, où seuls les participants, les tables et les propositions ont un
|
|
|
|
|
// champ id : aucune autre liste non vide n'a cette forme. Une liste vide
|
|
|
|
|
// l'a, quelle que soit sa sorte, sans que comparer en tire un { id } : deux
|
|
|
|
|
// listes vides ne donnent rien, et contre une liste non vide, c'est la
|
|
|
|
|
// forme de celle-ci qui décide.
|
|
|
|
|
function identifiantsDistincts(liste) {
|
|
|
|
|
const vus = new Set();
|
|
|
|
|
for (const element of liste) {
|
|
|
|
|
if (!estObjet(element) || !Number.isSafeInteger(element.id) || vus.has(element.id)) return false;
|
|
|
|
|
vus.add(element.id);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
// Poids d'opérations dans un correctif : la longueur de leur texte JSON, en
|
|
|
|
|
// unités UTF-16, des octets pour les chemins du schéma et les identifiants.
|
|
|
|
|
const poids = (operations) => JSON.stringify(operations).length;
|
|
|
|
|
|
2026-10-06 13:12:23 -04:00
|
|
|
// Ajoute à operations ce qui mène de a à b, deux valeurs canoniques de la
|
|
|
|
|
// règle regle, au bout de chemin. null contre une valeur, et deux scalaires
|
|
|
|
|
// différents, posent b. Un retenu que sa règle refuse, d'un côté ou de
|
|
|
|
|
// l'autre, n'a pas de champs que le schéma décrive : b se pose entier quand
|
|
|
|
|
// les deux copies canoniques s'écrivent autrement, et rien sinon. Deux
|
|
|
|
|
// objets se comparent champ par champ, dans l'ordre du schéma. Deux listes
|
|
|
|
|
// d'enregistrements à identifiants distincts se comparent par identifiant ;
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
// deux autres listes, de longueurs différentes, posent b entière, et de même
|
[ADD] storage: empty chair in a table list when seats are assigned
The operator plans, the room is not tracked live: removing a person from
the retained plan must free her chair and leave every other seat where it
is. With seats assigned, a table list may now hold null before its last
occupant, seat 1 included, and never end on it; without assignment no
list holds null. Validation, canonical form, patches and the named form
handle it, and the delivered demos keep their bytes.
Checked: 1549 node and 58 node-long tests; depot.js, journal.js at 100 %.
--- FR ---
[ADD] stockage : chaise vide dans une liste de table aux sièges attribués
L'opérateur planifie, la salle ne se suit pas en direct : retirer une
personne du plan retenu doit libérer sa chaise et laisser tout autre siège
à sa place. Sièges attribués, une liste de table peut porter null avant son
dernier occupant, siège 1 compris, jamais à la fin ; sans attribution,
aucune liste ne porte null. Contrôle, forme canonique, correctifs et forme
nommée le traitent, et les démonstrations livrées gardent leurs octets.
Vérifié : 1549 node et 58 node-long ; depot.js, journal.js à 100 %.
Assisted-by: Claude Opus 5.5
2026-10-07 02:31:26 -04:00
|
|
|
// longueur se comparent rang par rang — deux listes d'identifiants ou
|
|
|
|
|
// d'occupants exceptées, qui posent b entière quand leurs poses rang par
|
|
|
|
|
// rang pèsent plus que la sienne.
|
2026-10-06 13:12:23 -04:00
|
|
|
function comparer(a, b, regle, chemin, operations) {
|
|
|
|
|
if (a === null || b === null || (regle.genre !== 'objet' && regle.genre !== 'liste')) {
|
|
|
|
|
if (a !== b) operations.push({ op: 'poser', chemin, valeur: b });
|
|
|
|
|
} else if (retenuHorsDeSaRegle(a, regle) || retenuHorsDeSaRegle(b, regle)) {
|
|
|
|
|
if (JSON.stringify(a) !== JSON.stringify(b)) operations.push({ op: 'poser', chemin, valeur: b });
|
|
|
|
|
} else if (regle.genre === 'objet') {
|
|
|
|
|
for (const [cle, regleDeCle] of regle.champs) comparer(a[cle], b[cle], regleDeCle, [...chemin, cle], operations);
|
|
|
|
|
} else if (identifiantsDistincts(a) && identifiantsDistincts(b)) {
|
|
|
|
|
comparerParIdentifiant(a, b, regle.element, chemin, operations);
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
} else if (a.length !== b.length) {
|
2026-10-06 13:12:23 -04:00
|
|
|
operations.push({ op: 'poser', chemin, valeur: b });
|
[ADD] storage: empty chair in a table list when seats are assigned
The operator plans, the room is not tracked live: removing a person from
the retained plan must free her chair and leave every other seat where it
is. With seats assigned, a table list may now hold null before its last
occupant, seat 1 included, and never end on it; without assignment no
list holds null. Validation, canonical form, patches and the named form
handle it, and the delivered demos keep their bytes.
Checked: 1549 node and 58 node-long tests; depot.js, journal.js at 100 %.
--- FR ---
[ADD] stockage : chaise vide dans une liste de table aux sièges attribués
L'opérateur planifie, la salle ne se suit pas en direct : retirer une
personne du plan retenu doit libérer sa chaise et laisser tout autre siège
à sa place. Sièges attribués, une liste de table peut porter null avant son
dernier occupant, siège 1 compris, jamais à la fin ; sans attribution,
aucune liste ne porte null. Contrôle, forme canonique, correctifs et forme
nommée le traitent, et les démonstrations livrées gardent leurs octets.
Vérifié : 1549 node et 58 node-long ; depot.js, journal.js à 100 %.
Assisted-by: Claude Opus 5.5
2026-10-07 02:31:26 -04:00
|
|
|
} else if (regle.element === IDENTIFIANT || regle.element === OCCUPANT) {
|
|
|
|
|
comparerIdentifiants(a, b, regle.element, chemin, operations);
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
} else {
|
|
|
|
|
a.forEach((element, rang) => comparer(element, b[rang], regle.element, [...chemin, rang], operations));
|
2026-10-06 13:12:23 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
// Ajoute à operations ce qui mène de a à b, deux listes d'identifiants de
|
[ADD] storage: empty chair in a table list when seats are assigned
The operator plans, the room is not tracked live: removing a person from
the retained plan must free her chair and leave every other seat where it
is. With seats assigned, a table list may now hold null before its last
occupant, seat 1 included, and never end on it; without assignment no
list holds null. Validation, canonical form, patches and the named form
handle it, and the delivered demos keep their bytes.
Checked: 1549 node and 58 node-long tests; depot.js, journal.js at 100 %.
--- FR ---
[ADD] stockage : chaise vide dans une liste de table aux sièges attribués
L'opérateur planifie, la salle ne se suit pas en direct : retirer une
personne du plan retenu doit libérer sa chaise et laisser tout autre siège
à sa place. Sièges attribués, une liste de table peut porter null avant son
dernier occupant, siège 1 compris, jamais à la fin ; sans attribution,
aucune liste ne porte null. Contrôle, forme canonique, correctifs et forme
nommée le traitent, et les démonstrations livrées gardent leurs octets.
Vérifié : 1549 node et 58 node-long ; depot.js, journal.js à 100 %.
Assisted-by: Claude Opus 5.5
2026-10-07 02:31:26 -04:00
|
|
|
// même longueur, d'élément de règle regleElement, au bout de chemin : une
|
|
|
|
|
// pose par rang qui change, ou b entière quand ces poses pèsent plus que la
|
|
|
|
|
// sienne. Une liste triée par identifiant — une liste de table sans
|
|
|
|
|
// attribution, une réserve, les participants d'un plan — décale d'un rang
|
|
|
|
|
// chaque entrée qu'un identifiant remplacé franchit, et ses poses rang par
|
|
|
|
|
// rang se multiplient ; ce qui s'en écrit ne pèse jamais plus que la liste
|
|
|
|
|
// posée entière. Sous des sièges attribués, une chaise libérée ou reprise
|
|
|
|
|
// ne change que son rang : une pose, de valeur null ou de l'occupant.
|
|
|
|
|
function comparerIdentifiants(a, b, regleElement, chemin, operations) {
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
const parRang = [];
|
[ADD] storage: empty chair in a table list when seats are assigned
The operator plans, the room is not tracked live: removing a person from
the retained plan must free her chair and leave every other seat where it
is. With seats assigned, a table list may now hold null before its last
occupant, seat 1 included, and never end on it; without assignment no
list holds null. Validation, canonical form, patches and the named form
handle it, and the delivered demos keep their bytes.
Checked: 1549 node and 58 node-long tests; depot.js, journal.js at 100 %.
--- FR ---
[ADD] stockage : chaise vide dans une liste de table aux sièges attribués
L'opérateur planifie, la salle ne se suit pas en direct : retirer une
personne du plan retenu doit libérer sa chaise et laisser tout autre siège
à sa place. Sièges attribués, une liste de table peut porter null avant son
dernier occupant, siège 1 compris, jamais à la fin ; sans attribution,
aucune liste ne porte null. Contrôle, forme canonique, correctifs et forme
nommée le traitent, et les démonstrations livrées gardent leurs octets.
Vérifié : 1549 node et 58 node-long ; depot.js, journal.js à 100 %.
Assisted-by: Claude Opus 5.5
2026-10-07 02:31:26 -04:00
|
|
|
a.forEach((id, rang) => comparer(id, b[rang], regleElement, [...chemin, rang], parRang));
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
const entiere = { op: 'poser', chemin, valeur: b };
|
|
|
|
|
operations.push(...(poids(parRang) > poids([entiere]) ? [entiere] : parRang));
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-06 13:12:23 -04:00
|
|
|
// Fusion de deux listes d'enregistrements rangées par identifiant croissant,
|
|
|
|
|
// comme les laisse canoniser : un identifiant de a seul est retiré, un
|
|
|
|
|
// identifiant de b seul est posé entier, un identifiant commun se compare
|
|
|
|
|
// champ par champ. Les opérations suivent l'ordre des identifiants.
|
|
|
|
|
function comparerParIdentifiant(a, b, regleElement, chemin, operations) {
|
|
|
|
|
let i = 0;
|
|
|
|
|
let j = 0;
|
|
|
|
|
while (i < a.length || j < b.length) {
|
|
|
|
|
const idA = i < a.length ? a[i].id : Infinity;
|
|
|
|
|
const idB = j < b.length ? b[j].id : Infinity;
|
|
|
|
|
if (idA < idB) {
|
|
|
|
|
operations.push({ op: 'retirer', chemin: [...chemin, { id: idA }] });
|
|
|
|
|
i += 1;
|
|
|
|
|
} else if (idB < idA) {
|
|
|
|
|
operations.push({ op: 'poser', chemin: [...chemin, { id: idB }], valeur: b[j] });
|
|
|
|
|
j += 1;
|
|
|
|
|
} else {
|
|
|
|
|
comparer(a[i], b[j], regleElement, [...chemin, { id: idA }], operations);
|
|
|
|
|
i += 1;
|
|
|
|
|
j += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Correctif menant de a à b (§ 8.6), calculé entre leurs copies canoniques :
|
|
|
|
|
* [] quand elles sont égales, c'est-à-dire quand a et b ne diffèrent que par
|
|
|
|
|
* l'ordre de leurs clés ou d'une liste que canoniser range. Les opérations
|
|
|
|
|
* suivent l'ordre du schéma, puis celui des identifiants, puis celui des
|
|
|
|
|
* rangs : le correctif ne dépend que des deux charges. Une valeur posée est
|
|
|
|
|
* prise à la copie canonique de b, et ne partage aucun objet avec b. Ni a ni
|
|
|
|
|
* b ne sont modifiées.
|
|
|
|
|
*
|
|
|
|
|
* Règles : les objets clé par clé dans l'ordre du schéma ; les listes
|
|
|
|
|
* d'enregistrements à identifiant — participants, tables, propositions —
|
|
|
|
|
* par identifiant : retirés, posés entiers, communs comparés ; une telle
|
|
|
|
|
* liste dont un identifiant se répète, ce que la lecture admet pour deux
|
|
|
|
|
* propositions, se compare comme les autres listes ; les autres listes de
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
* longueurs différentes posées entières, et de même longueur rang par rang,
|
|
|
|
|
* sauf une liste d'identifiants dont les poses rang par rang pèsent plus,
|
|
|
|
|
* en JSON, que sa pose entière : elle se pose entière ; null contre une
|
|
|
|
|
* valeur, et deux scalaires différents, posent la valeur de b ; un retenu
|
|
|
|
|
* que sa règle refuse, dans a ou dans b, se pose entier quand les deux
|
|
|
|
|
* diffèrent, sans rien pour lui sinon. L'échange de deux personnes, au même
|
|
|
|
|
* tour d'une proposition, ne touche ainsi que les listes de leurs deux
|
|
|
|
|
* tables, et ne pèse jamais plus que ces deux listes posées entières,
|
|
|
|
|
* attribution des sièges ou non (§ 8.9).
|
2026-10-06 13:12:23 -04:00
|
|
|
*
|
|
|
|
|
* @param {import('./types.js').Charge} a
|
|
|
|
|
* @param {import('./types.js').Charge} b
|
|
|
|
|
* @returns {Operation[]}
|
|
|
|
|
*/
|
|
|
|
|
export function difference(a, b) {
|
|
|
|
|
const operations = [];
|
|
|
|
|
comparer(canoniser(a), canoniser(b), CHARGE, [], operations);
|
|
|
|
|
return operations;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Règle de ce que désigne etape dans une valeur de règle regle, lue dans le
|
|
|
|
|
// schéma : pour un objet, le champ de cette clé ; pour une liste, son
|
|
|
|
|
// élément, la valeur refusant toute étape qui n'y est ni un rang ni { id }.
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
// undefined quand l'étape sort du schéma — une clé que porte un retenu
|
|
|
|
|
// recopié hors du schéma, par exemple —, et pour toute étape qui la suit.
|
2026-10-06 13:12:23 -04:00
|
|
|
function regleSuivante(regle, etape) {
|
|
|
|
|
if (regle?.genre === 'objet') return regle.champs.find(([cle]) => cle === etape)?.[1];
|
|
|
|
|
return regle?.genre === 'liste' ? regle.element : undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Vrai quand regle est celle d'une liste d'enregistrements à identifiant,
|
|
|
|
|
// dont l'élément a un champ id : participants, tables, propositions.
|
|
|
|
|
const estListeDEnregistrements = (regle) => regle?.element?.cles?.has('id') === true;
|
|
|
|
|
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
// Lecture de chemin dans le schéma, depuis la charge : { regle }, la règle
|
|
|
|
|
// de la place qu'il désigne, undefined quand une étape sort du schéma ; null
|
|
|
|
|
// quand une étape { id } ne tombe pas dans une liste d'enregistrements à
|
|
|
|
|
// identifiant. C'est le schéma qui en décide, et non le contenu de la
|
|
|
|
|
// liste : une liste vide n'a aucun élément qui montre la forme des autres,
|
|
|
|
|
// et tout identifiant y est absent, ce qui ouvrirait une insertion par
|
|
|
|
|
// { id } dans une réserve, une liste de table ou les réservations. Une clé
|
|
|
|
|
// et un rang se contrôlent sur la valeur, qui les porte ou non.
|
2026-10-06 13:12:23 -04:00
|
|
|
function lireChemin(chemin) {
|
|
|
|
|
let regle = CHARGE;
|
|
|
|
|
for (const etape of chemin) {
|
|
|
|
|
if (estObjet(etape) && !estListeDEnregistrements(regle)) return null;
|
|
|
|
|
regle = regleSuivante(regle, etape);
|
|
|
|
|
}
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
return { regle };
|
2026-10-06 13:12:23 -04:00
|
|
|
}
|
|
|
|
|
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
// Vrai quand valeur, posée au bout de chemin dans charge, à la place de
|
|
|
|
|
// règle regle, suit cette règle, sa forme entière (premiereFaute),
|
|
|
|
|
// propositions et retenu compris : une proposition qui suit sa règle la
|
|
|
|
|
// suit encore après la pose, et canoniser comme serialiser la lisent. À la
|
|
|
|
|
// place du retenu, son conteneur seul (premiereFauteEnPlace) : un retenu que
|
|
|
|
|
// sa règle refuse se pose entier. Sous le retenu, une valeur ne se pose que
|
|
|
|
|
// quand il suit sa règle, et une valeur qui suit la sienne l'y garde. Une
|
|
|
|
|
// place hors du schéma n'en admet aucune.
|
|
|
|
|
function valeurAdmise(charge, chemin, regle, valeur) {
|
|
|
|
|
if (regle === undefined) return false;
|
|
|
|
|
if (regle === RETENU) return premiereFauteEnPlace(valeur, RETENU) === null;
|
|
|
|
|
if (chemin[0] === 'retenu' && premiereFaute(charge.retenu, RETENU) !== null) return false;
|
|
|
|
|
return premiereFaute(valeur, regle) === null;
|
2026-10-06 13:12:23 -04:00
|
|
|
}
|
|
|
|
|
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
// Rangs des éléments de liste dont l'id vaut id, ou null quand id n'est pas
|
|
|
|
|
// un entier exact : { id } n'y désigne rien. liste est une liste
|
|
|
|
|
// d'enregistrements du schéma — participants, tables, propositions —,
|
|
|
|
|
// chacun un objet à id entier, que l'analyse et valeurAdmise gardent
|
|
|
|
|
// conformes à leur règle.
|
2026-10-06 13:12:23 -04:00
|
|
|
function rangsDeLIdentifiant(liste, id) {
|
|
|
|
|
if (!Number.isSafeInteger(id)) return null;
|
|
|
|
|
const rangs = [];
|
|
|
|
|
for (let rang = 0; rang < liste.length; rang += 1) {
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
if (liste[rang].id === id) rangs.push(rang);
|
2026-10-06 13:12:23 -04:00
|
|
|
}
|
|
|
|
|
return rangs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Vrai quand etape est une clé propre de l'objet conteneur. Une clé héritée
|
|
|
|
|
// du prototype commun des objets — __proto__, constructor, toString — n'en
|
|
|
|
|
// est pas une : aucune opération ne lit ni n'écrit ce prototype, que partage
|
|
|
|
|
// tout le processus.
|
|
|
|
|
const estCleDe = (conteneur, etape) =>
|
|
|
|
|
typeof etape === 'string' && estObjet(conteneur) && Object.hasOwn(conteneur, etape);
|
|
|
|
|
// Vrai quand etape est un rang de la liste conteneur.
|
|
|
|
|
const estRangDe = (conteneur, etape) =>
|
|
|
|
|
Number.isSafeInteger(etape) && etape >= 0 && Array.isArray(conteneur) && etape < conteneur.length;
|
|
|
|
|
// Rangs que désigne l'étape { id } dans conteneur, ou null.
|
|
|
|
|
const rangsDe = (conteneur, etape) => (estObjet(etape) ? rangsDeLIdentifiant(conteneur, etape.id) : null);
|
|
|
|
|
|
|
|
|
|
// Valeur que désigne etape dans conteneur, ou undefined : une clé absente,
|
|
|
|
|
// un rang hors de la liste, un { id } qui ne désigne pas exactement un
|
|
|
|
|
// enregistrement, une étape d'une autre sorte. Aucune valeur d'une charge
|
|
|
|
|
// n'est undefined.
|
|
|
|
|
function descendre(conteneur, etape) {
|
|
|
|
|
if (estCleDe(conteneur, etape) || estRangDe(conteneur, etape)) return conteneur[etape];
|
|
|
|
|
const rangs = rangsDe(conteneur, etape);
|
|
|
|
|
return rangs?.length === 1 ? conteneur[rangs[0]] : undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pose une copie de valeur à la place que désigne etape dans conteneur :
|
|
|
|
|
// une clé qu'il porte déjà, un rang qu'il contient, ou { id } absent de la
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
// liste, quand valeur, un enregistrement que valeurAdmise a contrôlé, porte
|
|
|
|
|
// cet id ; l'enregistrement s'insère alors avant le premier d'id plus
|
|
|
|
|
// grand, ce qui garde une liste rangée par identifiant croissant. Rend faux
|
|
|
|
|
// quand etape ne désigne aucune de ces places.
|
2026-10-06 13:12:23 -04:00
|
|
|
function poser(conteneur, etape, valeur) {
|
|
|
|
|
if (estCleDe(conteneur, etape) || estRangDe(conteneur, etape)) {
|
|
|
|
|
conteneur[etape] = structuredClone(valeur);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
if (rangsDe(conteneur, etape)?.length !== 0 || valeur.id !== etape.id) return false;
|
2026-10-06 13:12:23 -04:00
|
|
|
const suivant = conteneur.findIndex((element) => element.id > etape.id);
|
|
|
|
|
conteneur.splice(suivant === -1 ? conteneur.length : suivant, 0, structuredClone(valeur));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Retire de conteneur l'enregistrement que désigne l'étape { id } ; rend
|
|
|
|
|
// faux quand elle n'en désigne pas exactement un. Une clé ou un rang ne se
|
|
|
|
|
// retirent pas : les objets de la charge ont les clés du schéma, et une
|
|
|
|
|
// liste sans identifiants se pose entière quand sa longueur change.
|
|
|
|
|
function retirer(conteneur, etape) {
|
|
|
|
|
const rangs = rangsDe(conteneur, etape);
|
|
|
|
|
if (rangs?.length !== 1) return false;
|
|
|
|
|
conteneur.splice(rangs[0], 1);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Applique operation à charge, en place ; rend faux, sans rien changer,
|
|
|
|
|
// quand elle est mal formée, qu'une étape { id } de son chemin tombe hors
|
|
|
|
|
// d'une liste d'enregistrements, que ce chemin ne désigne pas une place de
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
// charge, ou que la valeur posée n'y suivrait pas sa règle (valeurAdmise).
|
|
|
|
|
// Un chemin vide n'a pas de dernière étape, et undefined n'en désigne
|
|
|
|
|
// aucune. Une opération poser porte une valeur : JSON n'en écrit pas
|
|
|
|
|
// d'undefined.
|
2026-10-06 13:12:23 -04:00
|
|
|
function executer(charge, operation) {
|
|
|
|
|
if (!estObjet(operation) || !Array.isArray(operation.chemin)) return false;
|
|
|
|
|
const { op, chemin, valeur } = operation;
|
|
|
|
|
const lecture = lireChemin(chemin);
|
|
|
|
|
if (lecture === null) return false;
|
|
|
|
|
let conteneur = charge;
|
|
|
|
|
for (const etape of chemin.slice(0, -1)) {
|
|
|
|
|
conteneur = descendre(conteneur, etape);
|
|
|
|
|
if (conteneur === undefined) return false;
|
|
|
|
|
}
|
|
|
|
|
const derniere = chemin[chemin.length - 1];
|
|
|
|
|
if (op === 'poser') {
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
if (valeur === undefined || !valeurAdmise(charge, chemin, lecture.regle, valeur)) return false;
|
|
|
|
|
return poser(conteneur, derniere, valeur);
|
2026-10-06 13:12:23 -04:00
|
|
|
}
|
|
|
|
|
if (op === 'retirer') return retirer(conteneur, derniere);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Applique un correctif à la copie canonique de a (§ 8.6), opération après
|
|
|
|
|
* opération, et rend cette copie : une charge neuve, qui ne partage aucun
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
* objet avec a ni avec le correctif. a n'est pas modifiée ; c'est une charge
|
|
|
|
|
* que l'analyse admet et dont chaque proposition suit sa règle, comme celle
|
|
|
|
|
* que rend examiner (placements.js). Appliqué à a, le correctif de
|
|
|
|
|
* difference(a, b) rend la copie canonique de b. Une insertion par { id }
|
|
|
|
|
* garde l'ordre des identifiants.
|
2026-10-06 13:12:23 -04:00
|
|
|
*
|
|
|
|
|
* Lève ErreurStockage('CORRECTIF', { rang }) à la première opération qui ne
|
|
|
|
|
* s'applique pas — rang est sa place dans le correctif, à partir de 0 — :
|
|
|
|
|
* une opération mal formée ; une étape { id } hors des participants, des
|
|
|
|
|
* tables et des propositions, même dans une liste vide ; une étape qui ne
|
|
|
|
|
* trouve pas sa clé, que l'objet porte en propre et n'hérite pas, son rang
|
|
|
|
|
* ou exactement un enregistrement ; poser à une clé que l'objet ne porte
|
|
|
|
|
* pas ou au-delà de la fin d'une liste ; poser par { id } un identifiant
|
|
|
|
|
* déjà présent, ou une valeur qui n'est pas l'enregistrement de cet
|
[FIX] storage: compact seat patches, checked poses, 32-bit identifiers
Without seat attribution a table list stays sorted, so swapping two
people shifted every rank between them and a patch grew to over a
kilobyte; a same-length list of ids is now set whole when that is
lighter, and a swap costs two small operations (§ 8.9). A patch that
sets a malformed proposition or retained placement is refused at its
rank, instead of a TypeError at the next application. Identifiers stay
below 2^31, the engine's Int32Array bound: beyond, the record is a
form fault, and a proposition is set aside.
Checked: storage tests; reviewed, remaining findings minor.
--- FR ---
[FIX] stockage : correctifs compacts, poses contrôlées, id sous 2^31
Sans attribution des sièges, une liste de table reste triée : échanger
deux personnes décalait chaque rang entre elles, et un correctif passait
le kilo-octet ; une liste d'identifiants de même longueur se pose
désormais entière quand c'est plus léger, et un échange coûte deux
petites opérations (§ 8.9). Un correctif qui pose une proposition ou un
retenu mal formé est refusé à son rang, au lieu d'une TypeError à
l'application suivante. Les identifiants restent sous 2^31, borne des
Int32Array du moteur : au-delà, faute de forme, et proposition écartée.
Vérifié : épreuves du stockage ; revue, constats restants mineurs.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 19:22:02 -04:00
|
|
|
* identifiant ; poser une valeur qui sort de la règle de sa place, en
|
|
|
|
|
* genre, en domaine, null compris, ou en clés, jusque dans les propositions
|
|
|
|
|
* et le retenu ; à la place du retenu, autre chose qu'un objet ou null ;
|
|
|
|
|
* sous un retenu que sa règle refuse, quoi que ce soit ; retirer autre
|
|
|
|
|
* chose qu'un enregistrement par { id }. Une charge rendue garde ainsi
|
|
|
|
|
* chaque proposition dans sa règle, et canoniser, difference et serialiser
|
|
|
|
|
* la lisent sans lever. Le contrôle porte sur la forme : une opération ne
|
|
|
|
|
* porte pas la valeur qu'elle remplace, et un correctif dont chaque chemin
|
|
|
|
|
* existe aussi dans une autre charge s'y applique sans lever. Un correctif
|
|
|
|
|
* qui n'est pas une liste lève TypeError.
|
2026-10-06 13:12:23 -04:00
|
|
|
*
|
|
|
|
|
* @param {import('./types.js').Charge} a
|
|
|
|
|
* @param {Operation[]} correctif
|
|
|
|
|
* @returns {import('./types.js').Charge}
|
|
|
|
|
*/
|
|
|
|
|
export function appliquer(a, correctif) {
|
|
|
|
|
if (!Array.isArray(correctif)) {
|
|
|
|
|
throw new TypeError(`appliquer : liste d'opérations attendue, reçu ${JSON.stringify(correctif)}`);
|
|
|
|
|
}
|
|
|
|
|
const charge = canoniser(a);
|
|
|
|
|
correctif.forEach((operation, rang) => {
|
|
|
|
|
if (!executer(charge, operation)) throw new ErreurStockage('CORRECTIF', { rang });
|
|
|
|
|
});
|
|
|
|
|
return charge;
|
|
|
|
|
}
|