[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
|
|
|
|
|
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
|
|
|
|
|
|
|
|
|
|
|
// Les commandes nommées (§ 8.2) : toute modification d'un événement passe par
|
|
|
|
|
|
// l'une d'elles. Une commande est une fonction pure (charge, arguments) →
|
|
|
|
|
|
// { charge, libelle, avertissements } : la charge rendue est neuve, celle
|
|
|
|
|
|
// reçue ne change jamais ; le libellé est le texte français que le journal
|
|
|
|
|
|
// fige (libelles.js) ; les avertissements, { code, details }, disent ce que le
|
|
|
|
|
|
// geste emporte de plus que son effet, sans rien empêcher. Un refus lève
|
|
|
|
|
|
// ErreurCommande(code, details), où details.remede nomme le geste qui le lève,
|
|
|
|
|
|
// null quand il n'y en a pas ; une faute du code — un argument de forme
|
|
|
|
|
|
// fausse, un état ou un champ inconnus — lève TypeError. Aucune commande
|
[ADD] outputs: dated placement, draft rule, page footer, machine paper
A badge or a printed page must say which placement it shows and whether it
is a draft. The retained plan and each proposal carry an optional
timestamp, set when their placement changes and kept when a command rebuilds
it unchanged, with or without an instant; format 1 is unchanged and the
shipped demonstrations stay byte-identical. Anything but the retained plan
under a retained or locked state prints as a draft, proposals included. The
footer names event, date, round, timestamp and visible version.
Checked: red first; 3085 node and 639 browser tests green from the index.
--- FR ---
[ADD] sorties : placement daté, règle du brouillon, pied, papier
Un badge ou une page imprimée doit dire quel placement il montre et s'il
est un brouillon. Le retenu et chaque proposition portent un horodatage
facultatif, posé quand leur placement change et gardé quand une commande le
reconstruit à l'identique, avec ou sans instant ; le format 1 ne change pas
et les démonstrations livrées restent identiques à l'octet. Tout ce qui
n'est pas le retenu sous l'état retenu ou bloqué s'imprime en brouillon,
propositions comprises. Le pied nomme événement, date, tour, horodatage et
version visible.
Vérifié : rouge d'abord ; 3085 node et 639 navigateur verts depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:50:31 -04:00
|
|
|
|
// n'écrit, ne lit l'horloge ni ne tire un aléa : appliquerCommande date le
|
|
|
|
|
|
// placement de l'instant que la séance lui passe (§ 8.9).
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
//
|
|
|
|
|
|
// La saisie entre en NFC, ses blancs de bord retirés, une chaîne vide valant
|
|
|
|
|
|
// null (§ 10.2) ; la règle d'un champ est celle du schéma du fichier d'état,
|
|
|
|
|
|
// que document.js tient seul. Les identifiants viennent de prochainsIds, qui
|
|
|
|
|
|
// ne recule jamais : un identifiant retiré ne revient pas (§ 4, § 5.7). Un
|
|
|
|
|
|
// plan bloqué refuse toute commande, débloquer excepté (§ 9).
|
|
|
|
|
|
//
|
|
|
|
|
|
// Les états du plan (§ 9) : brouillon, proposé et retenu se posent par
|
|
|
|
|
|
// changerEtat sans rien détruire, proposé quand une proposition existe,
|
|
|
|
|
|
// retenu quand un placement est retenu ; bloqué se pose de chacun d'eux, et
|
|
|
|
|
|
// seul debloquer le lève, vers l'état que le contenu impose (etatDeduit). Une
|
|
|
|
|
|
// génération fait passer un brouillon à proposé ; retenir pose retenu ;
|
|
|
|
|
|
// effacer les propositions ramène un plan proposé sans retenu à brouillon.
|
|
|
|
|
|
import { ErreurCsv } from '../csv/erreurs.js';
|
|
|
|
|
|
import { appliquerImport, bilanRemplacement } from '../csv/import.js';
|
|
|
|
|
|
import { serialiserCharge } from '../stockage/canonique.js';
|
|
|
|
|
|
import { SCHEMA, clesRangees, creerCharge, etatDeduit, premiereFaute } from '../stockage/document.js';
|
|
|
|
|
|
import { ErreurStockage } from '../stockage/erreurs.js';
|
[ADD] outputs: dated placement, draft rule, page footer, machine paper
A badge or a printed page must say which placement it shows and whether it
is a draft. The retained plan and each proposal carry an optional
timestamp, set when their placement changes and kept when a command rebuilds
it unchanged, with or without an instant; format 1 is unchanged and the
shipped demonstrations stay byte-identical. Anything but the retained plan
under a retained or locked state prints as a draft, proposals included. The
footer names event, date, round, timestamp and visible version.
Checked: red first; 3085 node and 639 browser tests green from the index.
--- FR ---
[ADD] sorties : placement daté, règle du brouillon, pied, papier
Un badge ou une page imprimée doit dire quel placement il montre et s'il
est un brouillon. Le retenu et chaque proposition portent un horodatage
facultatif, posé quand leur placement change et gardé quand une commande le
reconstruit à l'identique, avec ou sans instant ; le format 1 ne change pas
et les démonstrations livrées restent identiques à l'octet. Tout ce qui
n'est pas le retenu sous l'état retenu ou bloqué s'imprime en brouillon,
propositions comprises. Le pied nomme événement, date, tour, horodatage et
version visible.
Vérifié : rouge d'abord ; 3085 node et 639 navigateur verts depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:50:31 -04:00
|
|
|
|
import { estHorodatage } from '../stockage/journal.js';
|
[FIX] storage, session: strict UTF-8, lost journal, lock, exclusions
The whole-iteration review found ways to lose or corrupt data. A state
saved in another encoding opened with its accents turned to U+FFFD and was
then overwritten; decoding is now strict, the state opens read-only, and a
gesture from its secours first moves it to the trash. A journal decodes line
by line, so one bad byte drops only the lines after it. A vanished journal
is rewritten whole, never appended to. Deleting refuses an event another
session holds; switching to writing rereads the disk; an import that
changes exclu says which reservations it suspends.
Checked: 1504 node, 35 browser, 29 node-long; depot.js, journal.js 100 %.
--- FR ---
[FIX] stockage, séance : UTF-8 strict, journal perdu, verrou, exclusions
La revue d'ensemble a trouvé des façons de perdre ou d'abîmer des données.
Un état enregistré dans un autre encodage s'ouvrait, ses accents changés en
U+FFFD, puis s'écrasait ; le décodage est strict, l'état s'ouvre en lecture
seule, et un geste depuis son secours le range d'abord à la corbeille. Le
journal se décode ligne par ligne : un octet fautif n'écarte que la suite.
Un journal disparu se réécrit en entier, jamais par ajout. Supprimer refuse
un événement qu'une autre séance tient ; passer en écriture relit le disque ;
un import qui change exclu dit quelles réservations il suspend.
Vérifié : 1504 node, 35 navigateur, 29 node-long ; depot.js, journal.js 100 %.
Assisted-by: Claude Opus 5.5
2026-10-07 01:20:42 -04:00
|
|
|
|
import { fautes, formeNommee, versFichier } from '../stockage/placements.js';
|
[ADD] application: clock, translation, formatting, displayed name
The screens need one clock, one translation mechanism and one way to
format and name, or each component grows its own. horloge.js is the only
code that reads time and arms timers, injected so tests drive it.
traduction.js keys texts by domain, in two families of tables, raises on a
missing key and never shows a raw key; libelles/ splits the label table by
domain. format.js formats numbers and dates in French, and personnes.js
composes the displayed name the frozen labels use. Nothing persists
between sessions.
Checked: 1773 node and 43 browser tests.
--- FR ---
[ADD] application : horloge, traduction, mise en forme, nom affiché
Les écrans exigent une horloge, un mécanisme de traduction et une façon de
mettre en forme et de nommer, sans quoi chaque composant ferait les siens.
horloge.js est le seul code qui lit l'heure et arme des minuteries, injecté
pour que les épreuves le pilotent. traduction.js range les textes par
domaine, en deux familles de tables, lève sur une clé absente et ne montre
jamais une clé brute ; libelles/ découpe la table des libellés par domaine.
format.js met en forme nombres et dates en français, et personnes.js
compose le nom affiché qu'emploient les libellés figés. Rien ne persiste
d'une séance à l'autre.
Vérifié : 1773 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:39 -04:00
|
|
|
|
import { COMMANDES_MOBILIER } from './commandes_mobilier.js';
|
|
|
|
|
|
import { COMMANDES_PLACES } from './commandes_places.js';
|
|
|
|
|
|
import { ErreurCommande, refus } from './erreurs.js';
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
import { libelle } from './libelles.js';
|
[ADD] application: clock, translation, formatting, displayed name
The screens need one clock, one translation mechanism and one way to
format and name, or each component grows its own. horloge.js is the only
code that reads time and arms timers, injected so tests drive it.
traduction.js keys texts by domain, in two families of tables, raises on a
missing key and never shows a raw key; libelles/ splits the label table by
domain. format.js formats numbers and dates in French, and personnes.js
composes the displayed name the frozen labels use. Nothing persists
between sessions.
Checked: 1773 node and 43 browser tests.
--- FR ---
[ADD] application : horloge, traduction, mise en forme, nom affiché
Les écrans exigent une horloge, un mécanisme de traduction et une façon de
mettre en forme et de nommer, sans quoi chaque composant ferait les siens.
horloge.js est le seul code qui lit l'heure et arme des minuteries, injecté
pour que les épreuves le pilotent. traduction.js range les textes par
domaine, en deux familles de tables, lève sur une clé absente et ne montre
jamais une clé brute ; libelles/ découpe la table des libellés par domaine.
format.js met en forme nombres et dates en français, et personnes.js
compose le nom affiché qu'emploient les libellés figés. Rien ne persiste
d'une séance à l'autre.
Vérifié : 1773 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:39 -04:00
|
|
|
|
import { nomAffiche } from './personnes.js';
|
[ADD] application: observable session, events facade, places and seats
The screens of iteration 3 need state they can subscribe to and commands
that keep the plan sound. The session follows the Svelte store contract,
unblocks a plan, cancels a gesture, keeps the framing, and orders the
warnings of a gesture. The facade lists events, settles the working folder
and runs copy, rename, import and delete. Places cover reservations, titles,
designation and the retained plan: each gesture reads seats as the display
shows them, reserved people keep their table, and a person removed frees
her chair while every other seat stays.
Checked: 1973 node, 127 browser, 60 node-long tests; coverage floors kept.
--- FR ---
[ADD] application : séance observable, façade des événements, places
Les écrans de l'itération 3 exigent un état auquel s'abonner et des
commandes qui gardent le plan sain. La séance suit le contrat des magasins
de Svelte, débloque un plan, annule un geste, garde le cadrage et ordonne
les avertissements d'un geste. La façade liste les événements, règle le
dossier de travail et mène copie, renommage, import et suppression. Les
places couvrent réservations, titres, désignation et plan retenu : chaque
geste lit les sièges comme l'écran les montre, une personne réservée garde
sa table, et une personne retirée libère sa chaise sans déplacer les autres.
Vérifié : 1973 node, 127 navigateur, 60 node-long ; seuils de couverture.
Assisted-by: Claude Opus 5.5
2026-10-07 04:14:24 -04:00
|
|
|
|
import { couvre, repartirSieges, reservationsActives } from './places.js';
|
|
|
|
|
|
import { controlerReintegration, leverRefus } from './pose.js';
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
|
[ADD] application: clock, translation, formatting, displayed name
The screens need one clock, one translation mechanism and one way to
format and name, or each component grows its own. horloge.js is the only
code that reads time and arms timers, injected so tests drive it.
traduction.js keys texts by domain, in two families of tables, raises on a
missing key and never shows a raw key; libelles/ splits the label table by
domain. format.js formats numbers and dates in French, and personnes.js
composes the displayed name the frozen labels use. Nothing persists
between sessions.
Checked: 1773 node and 43 browser tests.
--- FR ---
[ADD] application : horloge, traduction, mise en forme, nom affiché
Les écrans exigent une horloge, un mécanisme de traduction et une façon de
mettre en forme et de nommer, sans quoi chaque composant ferait les siens.
horloge.js est le seul code qui lit l'heure et arme des minuteries, injecté
pour que les épreuves le pilotent. traduction.js range les textes par
domaine, en deux familles de tables, lève sur une clé absente et ne montre
jamais une clé brute ; libelles/ découpe la table des libellés par domaine.
format.js met en forme nombres et dates en français, et personnes.js
compose le nom affiché qu'emploient les libellés figés. Rien ne persiste
d'une séance à l'autre.
Vérifié : 1773 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:39 -04:00
|
|
|
|
// Le refus d'une commande vit dans erreurs.js, le nom affiché d'une personne
|
|
|
|
|
|
// dans personnes.js : les importateurs de ce module les y trouvent encore.
|
|
|
|
|
|
export { ErreurCommande, nomAffiche };
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
|
|
|
|
|
|
// Un avertissement : il n'empêche rien.
|
|
|
|
|
|
const avertissement = (code, details) => ({ code, details });
|
|
|
|
|
|
|
|
|
|
|
|
// Règles du schéma : celle d'un champ d'objet, puis celles de l'événement et
|
|
|
|
|
|
// d'un participant.
|
|
|
|
|
|
const regleDuChamp = (regle, cle) => regle.champs.find(([nom]) => nom === cle)[1];
|
|
|
|
|
|
const CHARGE = regleDuChamp(SCHEMA, 'charge');
|
|
|
|
|
|
const EVENEMENT = regleDuChamp(CHARGE, 'evenement');
|
|
|
|
|
|
const PARTICIPANT = regleDuChamp(CHARGE, 'participants').element;
|
|
|
|
|
|
// Le plus grand identifiant qu'admet la règle : le compteur, qui la suit,
|
|
|
|
|
|
// n'aurait plus de valeur au-delà de lui.
|
|
|
|
|
|
const IDENTIFIANT_MAX = regleDuChamp(PARTICIPANT, 'id').max;
|
|
|
|
|
|
|
|
|
|
|
|
const ETATS = ['brouillon', 'propose', 'retenu', 'bloque'];
|
|
|
|
|
|
|
|
|
|
|
|
// Les champs de texte d'un participant que la saisie pose, dans l'ordre du
|
|
|
|
|
|
// fichier.
|
|
|
|
|
|
const CHAMPS_DE_SAISIE = ['nom', 'prenom', 'appartenance', 'courriel', 'titrePressenti', 'notes'];
|
|
|
|
|
|
|
|
|
|
|
|
// Une saisie de texte : en NFC, ses blancs de bord retirés, null quand il ne
|
|
|
|
|
|
// reste rien ; null et l'absence valent null.
|
|
|
|
|
|
function saisie(valeur, champ) {
|
|
|
|
|
|
if (valeur === undefined || valeur === null) return null;
|
|
|
|
|
|
if (typeof valeur !== 'string') {
|
|
|
|
|
|
throw new TypeError(`${champ} : chaîne ou null attendue, reçu ${JSON.stringify(valeur)}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
const texte = valeur.normalize('NFC').trim();
|
|
|
|
|
|
return texte === '' ? null : texte;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Les champs d'une saisie de participant : chaque champ de texte présent, lu
|
|
|
|
|
|
// par saisie, et exclu quand admisExclu, un booléen. Un champ inconnu, ou
|
|
|
|
|
|
// d'une autre sorte, est une faute du code.
|
|
|
|
|
|
function lireChamps(champs, { admisExclu }) {
|
|
|
|
|
|
if (champs === null || typeof champs !== 'object' || Array.isArray(champs)) {
|
|
|
|
|
|
throw new TypeError(`champs : objet attendu, reçu ${JSON.stringify(champs)}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
const permis = admisExclu ? [...CHAMPS_DE_SAISIE, 'exclu'] : CHAMPS_DE_SAISIE;
|
|
|
|
|
|
const inconnu = clesRangees(champs).find((cle) => !permis.includes(cle));
|
|
|
|
|
|
if (inconnu !== undefined) throw new TypeError(`champs : ${inconnu} n'est pas un champ de cette saisie`);
|
|
|
|
|
|
const lus = {};
|
|
|
|
|
|
for (const champ of CHAMPS_DE_SAISIE) if (Object.hasOwn(champs, champ)) lus[champ] = saisie(champs[champ], champ);
|
|
|
|
|
|
if (Object.hasOwn(champs, 'exclu')) {
|
|
|
|
|
|
if (typeof champs.exclu !== 'boolean') {
|
|
|
|
|
|
throw new TypeError(`exclu : booléen attendu, reçu ${JSON.stringify(champs.exclu)}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
lus.exclu = champs.exclu;
|
|
|
|
|
|
}
|
|
|
|
|
|
return lus;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Un participant tel que la saisie le pose : nom requis (NOM_REQUIS) ; toute
|
|
|
|
|
|
// autre valeur hors de sa règle est une faute du code.
|
|
|
|
|
|
function exigerParticipant(participant) {
|
|
|
|
|
|
if (premiereFaute(participant.nom, regleDuChamp(PARTICIPANT, 'nom')) !== null) throw refus('NOM_REQUIS');
|
|
|
|
|
|
const faute = premiereFaute(participant, PARTICIPANT);
|
|
|
|
|
|
if (faute !== null) throw new TypeError(`participant : ${faute} hors de sa règle`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Le participant d'identifiant id, ou PARTICIPANT_INCONNU.
|
|
|
|
|
|
function participantDe(charge, id) {
|
|
|
|
|
|
if (!Number.isSafeInteger(id)) throw new TypeError(`id : entier attendu, reçu ${JSON.stringify(id)}`);
|
|
|
|
|
|
const participant = charge.participants.find((candidat) => candidat.id === id);
|
|
|
|
|
|
if (participant === undefined) throw refus('PARTICIPANT_INCONNU', { id });
|
|
|
|
|
|
return participant;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Un plan bloqué refuse toute modification ; débloquer seul le lève (§ 9).
|
|
|
|
|
|
function exigerModifiable(charge) {
|
|
|
|
|
|
if (charge.evenement.etat === 'bloque') throw refus('PLAN_BLOQUE', {}, { geste: 'debloquer' });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Titres de place pourvus (§ 4.1, § 4.4) : le compte que bilanRemplacement
|
|
|
|
|
|
// tient pour l'import, seule implémentation de la règle (§ 13.2).
|
|
|
|
|
|
const titresPourvus = (charge) => bilanRemplacement(charge).titresPourvus;
|
|
|
|
|
|
|
|
|
|
|
|
// La charge reçue, copiée, son participant id remplacé par ce que rend
|
|
|
|
|
|
// modifier de sa copie.
|
|
|
|
|
|
function avecParticipant(charge, id, modifier) {
|
|
|
|
|
|
const suivante = structuredClone(charge);
|
|
|
|
|
|
suivante.participants = suivante.participants.map((participant) =>
|
|
|
|
|
|
participant.id === id ? modifier(participant) : participant,
|
|
|
|
|
|
);
|
|
|
|
|
|
return suivante;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// La charge reçue, copiée, à l'état donné.
|
|
|
|
|
|
function avecEtat(charge, etat) {
|
|
|
|
|
|
const suivante = structuredClone(charge);
|
|
|
|
|
|
suivante.evenement.etat = etat;
|
|
|
|
|
|
return suivante;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** ajouterParticipant {champs} : seul le nom est requis ; exclu vaut faux par défaut. */
|
|
|
|
|
|
function ajouterParticipant(charge, { champs }) {
|
|
|
|
|
|
exigerModifiable(charge);
|
|
|
|
|
|
const lus = lireChamps(champs, { admisExclu: true });
|
|
|
|
|
|
const id = charge.prochainsIds.participant;
|
|
|
|
|
|
const participant = {
|
|
|
|
|
|
id,
|
|
|
|
|
|
nom: lus.nom ?? null,
|
|
|
|
|
|
prenom: lus.prenom ?? null,
|
|
|
|
|
|
appartenance: lus.appartenance ?? null,
|
|
|
|
|
|
courriel: lus.courriel ?? null,
|
|
|
|
|
|
titrePressenti: lus.titrePressenti ?? null,
|
|
|
|
|
|
notes: lus.notes ?? null,
|
|
|
|
|
|
exclu: lus.exclu ?? false,
|
|
|
|
|
|
};
|
|
|
|
|
|
exigerParticipant(participant);
|
|
|
|
|
|
if (id >= IDENTIFIANT_MAX) throw refus('COMPTEUR_SATURE', { compteur: 'participant' });
|
|
|
|
|
|
const suivante = structuredClone(charge);
|
|
|
|
|
|
suivante.participants.push(participant);
|
|
|
|
|
|
suivante.prochainsIds.participant = id + 1;
|
|
|
|
|
|
return {
|
|
|
|
|
|
charge: suivante,
|
[ADD] application: clock, translation, formatting, displayed name
The screens need one clock, one translation mechanism and one way to
format and name, or each component grows its own. horloge.js is the only
code that reads time and arms timers, injected so tests drive it.
traduction.js keys texts by domain, in two families of tables, raises on a
missing key and never shows a raw key; libelles/ splits the label table by
domain. format.js formats numbers and dates in French, and personnes.js
composes the displayed name the frozen labels use. Nothing persists
between sessions.
Checked: 1773 node and 43 browser tests.
--- FR ---
[ADD] application : horloge, traduction, mise en forme, nom affiché
Les écrans exigent une horloge, un mécanisme de traduction et une façon de
mettre en forme et de nommer, sans quoi chaque composant ferait les siens.
horloge.js est le seul code qui lit l'heure et arme des minuteries, injecté
pour que les épreuves le pilotent. traduction.js range les textes par
domaine, en deux familles de tables, lève sur une clé absente et ne montre
jamais une clé brute ; libelles/ découpe la table des libellés par domaine.
format.js met en forme nombres et dates en français, et personnes.js
compose le nom affiché qu'emploient les libellés figés. Rien ne persiste
d'une séance à l'autre.
Vérifié : 1773 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:39 -04:00
|
|
|
|
libelle: libelle('ENTREE_AJOUTER_PARTICIPANT', { personne: nomAffiche(participant) }),
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
avertissements: [],
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* modifierParticipant {id, champs} : les champs de texte donnés ; l'exclusion
|
|
|
|
|
|
* a ses deux commandes. Le libellé nomme la personne d'avant et chaque champ
|
|
|
|
|
|
* changé, puis le nouveau nom quand il change.
|
|
|
|
|
|
*/
|
|
|
|
|
|
function modifierParticipant(charge, { id, champs }) {
|
|
|
|
|
|
exigerModifiable(charge);
|
|
|
|
|
|
const avant = participantDe(charge, id);
|
|
|
|
|
|
const apres = { ...avant, ...lireChamps(champs, { admisExclu: false }) };
|
|
|
|
|
|
exigerParticipant(apres);
|
|
|
|
|
|
const changes = CHAMPS_DE_SAISIE.filter((champ) => apres[champ] !== avant[champ]);
|
|
|
|
|
|
if (changes.length === 0) throw refus('SANS_EFFET', { commande: 'modifierParticipant' });
|
|
|
|
|
|
const renomme = apres.nom !== avant.nom || apres.prenom !== avant.prenom;
|
|
|
|
|
|
return {
|
|
|
|
|
|
charge: avecParticipant(charge, id, () => apres),
|
|
|
|
|
|
libelle: libelle('ENTREE_MODIFIER_PARTICIPANT', {
|
[ADD] application: clock, translation, formatting, displayed name
The screens need one clock, one translation mechanism and one way to
format and name, or each component grows its own. horloge.js is the only
code that reads time and arms timers, injected so tests drive it.
traduction.js keys texts by domain, in two families of tables, raises on a
missing key and never shows a raw key; libelles/ splits the label table by
domain. format.js formats numbers and dates in French, and personnes.js
composes the displayed name the frozen labels use. Nothing persists
between sessions.
Checked: 1773 node and 43 browser tests.
--- FR ---
[ADD] application : horloge, traduction, mise en forme, nom affiché
Les écrans exigent une horloge, un mécanisme de traduction et une façon de
mettre en forme et de nommer, sans quoi chaque composant ferait les siens.
horloge.js est le seul code qui lit l'heure et arme des minuteries, injecté
pour que les épreuves le pilotent. traduction.js range les textes par
domaine, en deux familles de tables, lève sur une clé absente et ne montre
jamais une clé brute ; libelles/ découpe la table des libellés par domaine.
format.js met en forme nombres et dates en français, et personnes.js
compose le nom affiché qu'emploient les libellés figés. Rien ne persiste
d'une séance à l'autre.
Vérifié : 1773 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:39 -04:00
|
|
|
|
personne: nomAffiche(avant),
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
champs: changes,
|
[ADD] application: clock, translation, formatting, displayed name
The screens need one clock, one translation mechanism and one way to
format and name, or each component grows its own. horloge.js is the only
code that reads time and arms timers, injected so tests drive it.
traduction.js keys texts by domain, in two families of tables, raises on a
missing key and never shows a raw key; libelles/ splits the label table by
domain. format.js formats numbers and dates in French, and personnes.js
composes the displayed name the frozen labels use. Nothing persists
between sessions.
Checked: 1773 node and 43 browser tests.
--- FR ---
[ADD] application : horloge, traduction, mise en forme, nom affiché
Les écrans exigent une horloge, un mécanisme de traduction et une façon de
mettre en forme et de nommer, sans quoi chaque composant ferait les siens.
horloge.js est le seul code qui lit l'heure et arme des minuteries, injecté
pour que les épreuves le pilotent. traduction.js range les textes par
domaine, en deux familles de tables, lève sur une clé absente et ne montre
jamais une clé brute ; libelles/ découpe la table des libellés par domaine.
format.js met en forme nombres et dates en français, et personnes.js
compose le nom affiché qu'emploient les libellés figés. Rien ne persiste
d'une séance à l'autre.
Vérifié : 1773 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:39 -04:00
|
|
|
|
devenu: renomme ? nomAffiche(apres) : null,
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
}),
|
|
|
|
|
|
avertissements: [],
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Les comptes d'une exclusion ou d'une réintégration (§ 4.4) : les
|
|
|
|
|
|
// réservations de la personne, que l'exclusion suspend sans les retirer, et
|
|
|
|
|
|
// les titres que le geste cesse de pourvoir, ou pourvoit de nouveau.
|
|
|
|
|
|
function bilanExclusion(avant, apres, participant) {
|
|
|
|
|
|
const reservations = avant.reservations.filter((reservation) => reservation.participant === participant.id).length;
|
|
|
|
|
|
const titres = Math.abs(titresPourvus(avant) - titresPourvus(apres));
|
[ADD] application: clock, translation, formatting, displayed name
The screens need one clock, one translation mechanism and one way to
format and name, or each component grows its own. horloge.js is the only
code that reads time and arms timers, injected so tests drive it.
traduction.js keys texts by domain, in two families of tables, raises on a
missing key and never shows a raw key; libelles/ splits the label table by
domain. format.js formats numbers and dates in French, and personnes.js
composes the displayed name the frozen labels use. Nothing persists
between sessions.
Checked: 1773 node and 43 browser tests.
--- FR ---
[ADD] application : horloge, traduction, mise en forme, nom affiché
Les écrans exigent une horloge, un mécanisme de traduction et une façon de
mettre en forme et de nommer, sans quoi chaque composant ferait les siens.
horloge.js est le seul code qui lit l'heure et arme des minuteries, injecté
pour que les épreuves le pilotent. traduction.js range les textes par
domaine, en deux familles de tables, lève sur une clé absente et ne montre
jamais une clé brute ; libelles/ découpe la table des libellés par domaine.
format.js met en forme nombres et dates en français, et personnes.js
compose le nom affiché qu'emploient les libellés figés. Rien ne persiste
d'une séance à l'autre.
Vérifié : 1773 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:39 -04:00
|
|
|
|
return { participant: participant.id, personne: nomAffiche(participant), reservations, titres };
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* exclureParticipant {id} (§ 4.4) : ses réservations restent, suspendues ; ses
|
|
|
|
|
|
* places reviennent aux autres et ses titres ne sont plus pourvus, ce que
|
|
|
|
|
|
* RESERVATIONS_SUSPENDUES dit quand il en porte.
|
|
|
|
|
|
*/
|
|
|
|
|
|
function exclureParticipant(charge, { id }) {
|
|
|
|
|
|
exigerModifiable(charge);
|
|
|
|
|
|
const participant = participantDe(charge, id);
|
|
|
|
|
|
if (participant.exclu) throw refus('SANS_EFFET', { commande: 'exclureParticipant' });
|
|
|
|
|
|
const suivante = avecParticipant(charge, id, (copie) => ({ ...copie, exclu: true }));
|
|
|
|
|
|
const bilan = bilanExclusion(charge, suivante, participant);
|
|
|
|
|
|
return {
|
|
|
|
|
|
charge: suivante,
|
|
|
|
|
|
libelle: libelle('ENTREE_EXCLURE_PARTICIPANT', { personne: bilan.personne }),
|
|
|
|
|
|
avertissements: bilan.reservations > 0 ? [avertissement('RESERVATIONS_SUSPENDUES', bilan)] : [],
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
[ADD] application: observable session, events facade, places and seats
The screens of iteration 3 need state they can subscribe to and commands
that keep the plan sound. The session follows the Svelte store contract,
unblocks a plan, cancels a gesture, keeps the framing, and orders the
warnings of a gesture. The facade lists events, settles the working folder
and runs copy, rename, import and delete. Places cover reservations, titles,
designation and the retained plan: each gesture reads seats as the display
shows them, reserved people keep their table, and a person removed frees
her chair while every other seat stays.
Checked: 1973 node, 127 browser, 60 node-long tests; coverage floors kept.
--- FR ---
[ADD] application : séance observable, façade des événements, places
Les écrans de l'itération 3 exigent un état auquel s'abonner et des
commandes qui gardent le plan sain. La séance suit le contrat des magasins
de Svelte, débloque un plan, annule un geste, garde le cadrage et ordonne
les avertissements d'un geste. La façade liste les événements, règle le
dossier de travail et mène copie, renommage, import et suppression. Les
places couvrent réservations, titres, désignation et plan retenu : chaque
geste lit les sièges comme l'écran les montre, une personne réservée garde
sa table, et une personne retirée libère sa chaise sans déplacer les autres.
Vérifié : 1973 node, 127 navigateur, 60 node-long ; seuils de couverture.
Assisted-by: Claude Opus 5.5
2026-10-07 04:14:24 -04:00
|
|
|
|
/**
|
|
|
|
|
|
* reintegrerParticipant {id} (§ 4.4) : ses réservations et ses titres
|
|
|
|
|
|
* reviennent, RESERVATIONS_RETABLIES. Ses réservations suspendues se
|
|
|
|
|
|
* contrôlent comme une pose (controlerReintegration, § 5.9) : le premier
|
|
|
|
|
|
* refus se lève, SURRESERVATION ou PLACE_RESERVEE, les suivants dans
|
|
|
|
|
|
* details.autres.
|
|
|
|
|
|
*/
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
function reintegrerParticipant(charge, { id }) {
|
|
|
|
|
|
exigerModifiable(charge);
|
|
|
|
|
|
const participant = participantDe(charge, id);
|
|
|
|
|
|
if (!participant.exclu) throw refus('SANS_EFFET', { commande: 'reintegrerParticipant' });
|
[ADD] application: observable session, events facade, places and seats
The screens of iteration 3 need state they can subscribe to and commands
that keep the plan sound. The session follows the Svelte store contract,
unblocks a plan, cancels a gesture, keeps the framing, and orders the
warnings of a gesture. The facade lists events, settles the working folder
and runs copy, rename, import and delete. Places cover reservations, titles,
designation and the retained plan: each gesture reads seats as the display
shows them, reserved people keep their table, and a person removed frees
her chair while every other seat stays.
Checked: 1973 node, 127 browser, 60 node-long tests; coverage floors kept.
--- FR ---
[ADD] application : séance observable, façade des événements, places
Les écrans de l'itération 3 exigent un état auquel s'abonner et des
commandes qui gardent le plan sain. La séance suit le contrat des magasins
de Svelte, débloque un plan, annule un geste, garde le cadrage et ordonne
les avertissements d'un geste. La façade liste les événements, règle le
dossier de travail et mène copie, renommage, import et suppression. Les
places couvrent réservations, titres, désignation et plan retenu : chaque
geste lit les sièges comme l'écran les montre, une personne réservée garde
sa table, et une personne retirée libère sa chaise sans déplacer les autres.
Vérifié : 1973 node, 127 navigateur, 60 node-long ; seuils de couverture.
Assisted-by: Claude Opus 5.5
2026-10-07 04:14:24 -04:00
|
|
|
|
leverRefus(controlerReintegration(charge, id));
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
const suivante = avecParticipant(charge, id, (copie) => ({ ...copie, exclu: false }));
|
|
|
|
|
|
const bilan = bilanExclusion(charge, suivante, participant);
|
|
|
|
|
|
return {
|
|
|
|
|
|
charge: suivante,
|
|
|
|
|
|
libelle: libelle('ENTREE_REINTEGRER_PARTICIPANT', { personne: bilan.personne }),
|
|
|
|
|
|
avertissements: bilan.reservations > 0 ? [avertissement('RESERVATIONS_RETABLIES', bilan)] : [],
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* supprimerParticipant {id} : la personne et ses réservations partent dans la
|
|
|
|
|
|
* même entrée ; les titres restent à leurs places, les propositions et le
|
|
|
|
|
|
* retenu restent, en dérive (§ 9) ; le compteur ne recule pas.
|
|
|
|
|
|
*/
|
|
|
|
|
|
function supprimerParticipant(charge, { id }) {
|
|
|
|
|
|
exigerModifiable(charge);
|
|
|
|
|
|
const participant = participantDe(charge, id);
|
|
|
|
|
|
const suivante = structuredClone(charge);
|
|
|
|
|
|
suivante.participants = suivante.participants.filter((candidat) => candidat.id !== id);
|
|
|
|
|
|
suivante.reservations = suivante.reservations.filter((reservation) => reservation.participant !== id);
|
|
|
|
|
|
const reservations = charge.reservations.length - suivante.reservations.length;
|
|
|
|
|
|
const details = {
|
|
|
|
|
|
participant: id,
|
[ADD] application: clock, translation, formatting, displayed name
The screens need one clock, one translation mechanism and one way to
format and name, or each component grows its own. horloge.js is the only
code that reads time and arms timers, injected so tests drive it.
traduction.js keys texts by domain, in two families of tables, raises on a
missing key and never shows a raw key; libelles/ splits the label table by
domain. format.js formats numbers and dates in French, and personnes.js
composes the displayed name the frozen labels use. Nothing persists
between sessions.
Checked: 1773 node and 43 browser tests.
--- FR ---
[ADD] application : horloge, traduction, mise en forme, nom affiché
Les écrans exigent une horloge, un mécanisme de traduction et une façon de
mettre en forme et de nommer, sans quoi chaque composant ferait les siens.
horloge.js est le seul code qui lit l'heure et arme des minuteries, injecté
pour que les épreuves le pilotent. traduction.js range les textes par
domaine, en deux familles de tables, lève sur une clé absente et ne montre
jamais une clé brute ; libelles/ découpe la table des libellés par domaine.
format.js met en forme nombres et dates en français, et personnes.js
compose le nom affiché qu'emploient les libellés figés. Rien ne persiste
d'une séance à l'autre.
Vérifié : 1773 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:39 -04:00
|
|
|
|
personne: nomAffiche(participant),
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
reservations,
|
|
|
|
|
|
titres: titresPourvus(charge) - titresPourvus(suivante),
|
|
|
|
|
|
};
|
|
|
|
|
|
return {
|
|
|
|
|
|
charge: suivante,
|
|
|
|
|
|
libelle: libelle('ENTREE_SUPPRIMER_PARTICIPANT', { personne: details.personne }),
|
|
|
|
|
|
avertissements: reservations > 0 ? [avertissement('RESERVATIONS_RETIREES', details)] : [],
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
[FIX] storage, session: strict UTF-8, lost journal, lock, exclusions
The whole-iteration review found ways to lose or corrupt data. A state
saved in another encoding opened with its accents turned to U+FFFD and was
then overwritten; decoding is now strict, the state opens read-only, and a
gesture from its secours first moves it to the trash. A journal decodes line
by line, so one bad byte drops only the lines after it. A vanished journal
is rewritten whole, never appended to. Deleting refuses an event another
session holds; switching to writing rereads the disk; an import that
changes exclu says which reservations it suspends.
Checked: 1504 node, 35 browser, 29 node-long; depot.js, journal.js 100 %.
--- FR ---
[FIX] stockage, séance : UTF-8 strict, journal perdu, verrou, exclusions
La revue d'ensemble a trouvé des façons de perdre ou d'abîmer des données.
Un état enregistré dans un autre encodage s'ouvrait, ses accents changés en
U+FFFD, puis s'écrasait ; le décodage est strict, l'état s'ouvre en lecture
seule, et un geste depuis son secours le range d'abord à la corbeille. Le
journal se décode ligne par ligne : un octet fautif n'écarte que la suite.
Un journal disparu se réécrit en entier, jamais par ajout. Supprimer refuse
un événement qu'une autre séance tient ; passer en écriture relit le disque ;
un import qui change exclu dit quelles réservations il suspend.
Vérifié : 1504 node, 35 navigateur, 29 node-long ; depot.js, journal.js 100 %.
Assisted-by: Claude Opus 5.5
2026-10-07 01:20:42 -04:00
|
|
|
|
// Les avertissements de chaque personne qu'un import exclut ou réintègre
|
|
|
|
|
|
// (§ 4.4) : celle que la charge suivante porte sous le même identifiant,
|
|
|
|
|
|
// exclu changé, et qui tient une réservation. Chacune a le bilan que
|
|
|
|
|
|
// donnerait sa commande seule, exclureParticipant ou reintegrerParticipant,
|
|
|
|
|
|
// appliquée à la charge d'avant : RESERVATIONS_SUSPENDUES ou
|
|
|
|
|
|
// RESERVATIONS_RETABLIES, par identifiant croissant.
|
|
|
|
|
|
function avertissementsDExclusion(avant, suivante) {
|
|
|
|
|
|
const exclusDApres = new Map(suivante.participants.map(({ id, exclu }) => [id, exclu]));
|
|
|
|
|
|
return avant.participants
|
|
|
|
|
|
.filter(({ id, exclu }) => exclusDApres.has(id) && exclusDApres.get(id) !== exclu)
|
|
|
|
|
|
.sort((a, b) => a.id - b.id)
|
|
|
|
|
|
.map((participant) => {
|
|
|
|
|
|
const seule = avecParticipant(avant, participant.id, (copie) => ({ ...copie, exclu: !copie.exclu }));
|
|
|
|
|
|
const code = participant.exclu ? 'RESERVATIONS_RETABLIES' : 'RESERVATIONS_SUSPENDUES';
|
|
|
|
|
|
return avertissement(code, bilanExclusion(avant, seule, participant));
|
|
|
|
|
|
})
|
|
|
|
|
|
.filter(({ details }) => details.reservations > 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
/**
|
|
|
|
|
|
* importerParticipants {apercu, mode} (§ 10.1) : l'aperçu appliqué selon le
|
|
|
|
|
|
* mode (appliquerImport), en une seule entrée. Le refus du CSV — un aperçu
|
|
|
|
|
|
* refusé ou ambigu — devient le refus de la commande, sous le même code. Le
|
|
|
|
|
|
* résumé suit le rendu, pour le rapport d'import et le CSV des refus. Selon
|
|
|
|
|
|
* l'état : sans avertissement en brouillon et proposé ; en retenu,
|
|
|
|
|
|
* l'avertissement de dérive IMPORT_EN_RETENU, quel que soit le mode, sur le
|
|
|
|
|
|
* signal resume.derive d'appliquerImport ; refusé en bloqué, PLAN_BLOQUE.
|
[FIX] storage, session: strict UTF-8, lost journal, lock, exclusions
The whole-iteration review found ways to lose or corrupt data. A state
saved in another encoding opened with its accents turned to U+FFFD and was
then overwritten; decoding is now strict, the state opens read-only, and a
gesture from its secours first moves it to the trash. A journal decodes line
by line, so one bad byte drops only the lines after it. A vanished journal
is rewritten whole, never appended to. Deleting refuses an event another
session holds; switching to writing rereads the disk; an import that
changes exclu says which reservations it suspends.
Checked: 1504 node, 35 browser, 29 node-long; depot.js, journal.js 100 %.
--- FR ---
[FIX] stockage, séance : UTF-8 strict, journal perdu, verrou, exclusions
La revue d'ensemble a trouvé des façons de perdre ou d'abîmer des données.
Un état enregistré dans un autre encodage s'ouvrait, ses accents changés en
U+FFFD, puis s'écrasait ; le décodage est strict, l'état s'ouvre en lecture
seule, et un geste depuis son secours le range d'abord à la corbeille. Le
journal se décode ligne par ligne : un octet fautif n'écarte que la suite.
Un journal disparu se réécrit en entier, jamais par ajout. Supprimer refuse
un événement qu'une autre séance tient ; passer en écriture relit le disque ;
un import qui change exclu dit quelles réservations il suspend.
Vérifié : 1504 node, 35 navigateur, 29 node-long ; depot.js, journal.js 100 %.
Assisted-by: Claude Opus 5.5
2026-10-07 01:20:42 -04:00
|
|
|
|
* Une personne que l'import exclut ou réintègre le dit ensuite comme sa
|
|
|
|
|
|
* commande le dirait (avertissementsDExclusion).
|
|
|
|
|
|
*
|
|
|
|
|
|
* Un import dont la charge ne change pas, mais qui refuse des lignes, n'est
|
|
|
|
|
|
* pas sans effet : ses refus sont à lire et à réexporter. Il rend la charge
|
|
|
|
|
|
* reçue, un libellé null — aucune entrée ne s'écrit —, aucun avertissement,
|
|
|
|
|
|
* et son résumé.
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
*/
|
|
|
|
|
|
function importerParticipants(charge, { apercu, mode }) {
|
|
|
|
|
|
exigerModifiable(charge);
|
|
|
|
|
|
let resultat;
|
|
|
|
|
|
try {
|
|
|
|
|
|
resultat = appliquerImport(charge, apercu, mode);
|
|
|
|
|
|
} catch (erreur) {
|
|
|
|
|
|
if (erreur instanceof ErreurCsv) throw new ErreurCommande(erreur.code, { ...erreur.details, remede: null });
|
|
|
|
|
|
throw erreur;
|
|
|
|
|
|
}
|
[FIX] storage, session: strict UTF-8, lost journal, lock, exclusions
The whole-iteration review found ways to lose or corrupt data. A state
saved in another encoding opened with its accents turned to U+FFFD and was
then overwritten; decoding is now strict, the state opens read-only, and a
gesture from its secours first moves it to the trash. A journal decodes line
by line, so one bad byte drops only the lines after it. A vanished journal
is rewritten whole, never appended to. Deleting refuses an event another
session holds; switching to writing rereads the disk; an import that
changes exclu says which reservations it suspends.
Checked: 1504 node, 35 browser, 29 node-long; depot.js, journal.js 100 %.
--- FR ---
[FIX] stockage, séance : UTF-8 strict, journal perdu, verrou, exclusions
La revue d'ensemble a trouvé des façons de perdre ou d'abîmer des données.
Un état enregistré dans un autre encodage s'ouvrait, ses accents changés en
U+FFFD, puis s'écrasait ; le décodage est strict, l'état s'ouvre en lecture
seule, et un geste depuis son secours le range d'abord à la corbeille. Le
journal se décode ligne par ligne : un octet fautif n'écarte que la suite.
Un journal disparu se réécrit en entier, jamais par ajout. Supprimer refuse
un événement qu'une autre séance tient ; passer en écriture relit le disque ;
un import qui change exclu dit quelles réservations il suspend.
Vérifié : 1504 node, 35 navigateur, 29 node-long ; depot.js, journal.js 100 %.
Assisted-by: Claude Opus 5.5
2026-10-07 01:20:42 -04:00
|
|
|
|
const { ajoutes, misAJour, retires, derive, refusees } = resultat.resume;
|
|
|
|
|
|
if (refusees.length > 0 && serialiserCharge(resultat.charge) === serialiserCharge(charge)) {
|
|
|
|
|
|
return { charge, libelle: null, avertissements: [], resume: resultat.resume };
|
|
|
|
|
|
}
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
return {
|
|
|
|
|
|
charge: resultat.charge,
|
|
|
|
|
|
libelle: libelle('ENTREE_IMPORTER_PARTICIPANTS', { mode, ajoutes, misAJour, retires }),
|
[FIX] storage, session: strict UTF-8, lost journal, lock, exclusions
The whole-iteration review found ways to lose or corrupt data. A state
saved in another encoding opened with its accents turned to U+FFFD and was
then overwritten; decoding is now strict, the state opens read-only, and a
gesture from its secours first moves it to the trash. A journal decodes line
by line, so one bad byte drops only the lines after it. A vanished journal
is rewritten whole, never appended to. Deleting refuses an event another
session holds; switching to writing rereads the disk; an import that
changes exclu says which reservations it suspends.
Checked: 1504 node, 35 browser, 29 node-long; depot.js, journal.js 100 %.
--- FR ---
[FIX] stockage, séance : UTF-8 strict, journal perdu, verrou, exclusions
La revue d'ensemble a trouvé des façons de perdre ou d'abîmer des données.
Un état enregistré dans un autre encodage s'ouvrait, ses accents changés en
U+FFFD, puis s'écrasait ; le décodage est strict, l'état s'ouvre en lecture
seule, et un geste depuis son secours le range d'abord à la corbeille. Le
journal se décode ligne par ligne : un octet fautif n'écarte que la suite.
Un journal disparu se réécrit en entier, jamais par ajout. Supprimer refuse
un événement qu'une autre séance tient ; passer en écriture relit le disque ;
un import qui change exclu dit quelles réservations il suspend.
Vérifié : 1504 node, 35 navigateur, 29 node-long ; depot.js, journal.js 100 %.
Assisted-by: Claude Opus 5.5
2026-10-07 01:20:42 -04:00
|
|
|
|
avertissements: [
|
|
|
|
|
|
...(derive ? [avertissement('IMPORT_EN_RETENU', {})] : []),
|
|
|
|
|
|
...avertissementsDExclusion(charge, resultat.charge),
|
|
|
|
|
|
],
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
resume: resultat.resume,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** changerEtat {etat} (§ 9) : un état du plan, sans rien détruire ; bloque se pose de chacun. */
|
|
|
|
|
|
function changerEtat(charge, { etat }) {
|
|
|
|
|
|
if (!ETATS.includes(etat)) throw new TypeError(`etat : ${ETATS.join(', ')} attendu, reçu ${JSON.stringify(etat)}`);
|
|
|
|
|
|
exigerModifiable(charge);
|
|
|
|
|
|
if (etat === charge.evenement.etat) throw refus('SANS_EFFET', { commande: 'changerEtat' });
|
|
|
|
|
|
if (etat === 'propose' && charge.propositions.length === 0) {
|
|
|
|
|
|
throw refus('AUCUNE_PROPOSITION', {}, { geste: 'ouvrirSection', section: 'generation' });
|
|
|
|
|
|
}
|
|
|
|
|
|
if (etat === 'retenu' && charge.retenu === null) {
|
|
|
|
|
|
throw refus('AUCUN_RETENU', {}, { geste: 'ouvrirSection', section: 'propositions' });
|
|
|
|
|
|
}
|
|
|
|
|
|
return { charge: avecEtat(charge, etat), libelle: libelle('ENTREE_CHANGER_ETAT', { etat }), avertissements: [] };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** debloquer {} (§ 8.5, § 9) : l'état revient à celui que le contenu impose. */
|
|
|
|
|
|
function debloquer(charge) {
|
|
|
|
|
|
if (charge.evenement.etat !== 'bloque') throw refus('PLAN_NON_BLOQUE');
|
|
|
|
|
|
const etat = etatDeduit(charge);
|
|
|
|
|
|
return { charge: avecEtat(charge, etat), libelle: libelle('ENTREE_DEBLOQUER', { etat }), avertissements: [] };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
[ADD] application: observable session, events facade, places and seats
The screens of iteration 3 need state they can subscribe to and commands
that keep the plan sound. The session follows the Svelte store contract,
unblocks a plan, cancels a gesture, keeps the framing, and orders the
warnings of a gesture. The facade lists events, settles the working folder
and runs copy, rename, import and delete. Places cover reservations, titles,
designation and the retained plan: each gesture reads seats as the display
shows them, reserved people keep their table, and a person removed frees
her chair while every other seat stays.
Checked: 1973 node, 127 browser, 60 node-long tests; coverage floors kept.
--- FR ---
[ADD] application : séance observable, façade des événements, places
Les écrans de l'itération 3 exigent un état auquel s'abonner et des
commandes qui gardent le plan sain. La séance suit le contrat des magasins
de Svelte, débloque un plan, annule un geste, garde le cadrage et ordonne
les avertissements d'un geste. La façade liste les événements, règle le
dossier de travail et mène copie, renommage, import et suppression. Les
places couvrent réservations, titres, désignation et plan retenu : chaque
geste lit les sièges comme l'écran les montre, une personne réservée garde
sa table, et une personne retirée libère sa chaise sans déplacer les autres.
Vérifié : 1973 node, 127 navigateur, 60 node-long ; seuils de couverture.
Assisted-by: Claude Opus 5.5
2026-10-07 04:14:24 -04:00
|
|
|
|
// Le plan du moteur rangé par siège quand les sièges sont attribués (§ 4,
|
|
|
|
|
|
// § 5.3, § 8.9) : chaque liste de table de chaque tour par repartirSieges —
|
|
|
|
|
|
// la personne que sa réservation active, à un siège, couvrant ce tour à
|
|
|
|
|
|
// cette table, y prend ce rang —, les null de fin retirés. Le moteur ignore
|
|
|
|
|
|
// les sièges : sans ce rangement, la liste garde l'ordre du moteur,
|
|
|
|
|
|
// croissant par identifiant. Les places sont celles de la configuration sur
|
|
|
|
|
|
// laquelle le moteur a calculé, qui n'assied jamais plus : une table réduite
|
|
|
|
|
|
// ou supprimée depuis ne change pas le rangement, et la proposition
|
|
|
|
|
|
// s'enregistre avec sa dérive.
|
|
|
|
|
|
function rangerParSiege(charge, plan, configuration) {
|
|
|
|
|
|
const places = new Map(configuration.tables.map(({ id, capacite: sieges }) => [id, sieges]));
|
|
|
|
|
|
const assises = reservationsActives(charge).filter(({ siege }) => siege !== null);
|
|
|
|
|
|
return {
|
|
|
|
|
|
...plan,
|
|
|
|
|
|
tours: plan.tours.map((listes, rang) =>
|
|
|
|
|
|
listes.map((liste, i) => {
|
|
|
|
|
|
const idTable = plan.tables[i];
|
|
|
|
|
|
const siegeDe = new Map();
|
|
|
|
|
|
for (const reservation of assises) {
|
|
|
|
|
|
if (reservation.table === idTable && couvre(reservation, rang + 1) && !siegeDe.has(reservation.participant)) {
|
|
|
|
|
|
siegeDe.set(reservation.participant, reservation.siege);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
const rangee = repartirSieges(liste, siegeDe, places.get(idTable) ?? 0);
|
|
|
|
|
|
while (rangee.length > 0 && rangee.at(-1) === null) rangee.pop();
|
|
|
|
|
|
return rangee;
|
|
|
|
|
|
}),
|
|
|
|
|
|
),
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
/**
|
|
|
|
|
|
* enregistrerGeneration {propositions, configuration, produitVersion} (§ 5.7,
|
|
|
|
|
|
* § 8.9) : les propositions du moteur, mises en forme de fichier
|
|
|
|
|
|
* (versFichier) sous les identifiants qui suivent prochainsIds.proposition —
|
|
|
|
|
|
* la proposition d'identifiant k du moteur prend prochainsIds.proposition +
|
|
|
|
|
|
* k − 1 —, s'ajoutent aux précédentes, et le compteur passe au-delà de la
|
[ADD] application: observable session, events facade, places and seats
The screens of iteration 3 need state they can subscribe to and commands
that keep the plan sound. The session follows the Svelte store contract,
unblocks a plan, cancels a gesture, keeps the framing, and orders the
warnings of a gesture. The facade lists events, settles the working folder
and runs copy, rename, import and delete. Places cover reservations, titles,
designation and the retained plan: each gesture reads seats as the display
shows them, reserved people keep their table, and a person removed frees
her chair while every other seat stays.
Checked: 1973 node, 127 browser, 60 node-long tests; coverage floors kept.
--- FR ---
[ADD] application : séance observable, façade des événements, places
Les écrans de l'itération 3 exigent un état auquel s'abonner et des
commandes qui gardent le plan sain. La séance suit le contrat des magasins
de Svelte, débloque un plan, annule un geste, garde le cadrage et ordonne
les avertissements d'un geste. La façade liste les événements, règle le
dossier de travail et mène copie, renommage, import et suppression. Les
places couvrent réservations, titres, désignation et plan retenu : chaque
geste lit les sièges comme l'écran les montre, une personne réservée garde
sa table, et une personne retirée libère sa chaise sans déplacer les autres.
Vérifié : 1973 node, 127 navigateur, 60 node-long ; seuils de couverture.
Assisted-by: Claude Opus 5.5
2026-10-07 04:14:24 -04:00
|
|
|
|
* dernière. Sièges attribués, chaque liste se range d'abord par siège
|
|
|
|
|
|
* (rangerParSiege). Un brouillon passe à proposé.
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
*/
|
|
|
|
|
|
function enregistrerGeneration(charge, { propositions, configuration, produitVersion }) {
|
|
|
|
|
|
exigerModifiable(charge);
|
|
|
|
|
|
if (!Array.isArray(propositions)) throw new TypeError('propositions : liste attendue');
|
|
|
|
|
|
if (propositions.length === 0) throw refus('SANS_EFFET', { commande: 'enregistrerGeneration' });
|
|
|
|
|
|
const rangs = propositions.map(({ id }) => id);
|
|
|
|
|
|
if (!rangs.every((id) => Number.isSafeInteger(id) && id >= 1) || new Set(rangs).size !== rangs.length) {
|
|
|
|
|
|
throw new TypeError('propositions : identifiants du moteur entiers, ≥ 1 et distincts attendus');
|
|
|
|
|
|
}
|
|
|
|
|
|
const decalage = charge.prochainsIds.proposition - 1;
|
|
|
|
|
|
const options = { produitVersion, attribuerSieges: charge.reglages.attribuerSieges, decalage };
|
|
|
|
|
|
let fichiers;
|
|
|
|
|
|
try {
|
[ADD] application: observable session, events facade, places and seats
The screens of iteration 3 need state they can subscribe to and commands
that keep the plan sound. The session follows the Svelte store contract,
unblocks a plan, cancels a gesture, keeps the framing, and orders the
warnings of a gesture. The facade lists events, settles the working folder
and runs copy, rename, import and delete. Places cover reservations, titles,
designation and the retained plan: each gesture reads seats as the display
shows them, reserved people keep their table, and a person removed frees
her chair while every other seat stays.
Checked: 1973 node, 127 browser, 60 node-long tests; coverage floors kept.
--- FR ---
[ADD] application : séance observable, façade des événements, places
Les écrans de l'itération 3 exigent un état auquel s'abonner et des
commandes qui gardent le plan sain. La séance suit le contrat des magasins
de Svelte, débloque un plan, annule un geste, garde le cadrage et ordonne
les avertissements d'un geste. La façade liste les événements, règle le
dossier de travail et mène copie, renommage, import et suppression. Les
places couvrent réservations, titres, désignation et plan retenu : chaque
geste lit les sièges comme l'écran les montre, une personne réservée garde
sa table, et une personne retirée libère sa chaise sans déplacer les autres.
Vérifié : 1973 node, 127 navigateur, 60 node-long ; seuils de couverture.
Assisted-by: Claude Opus 5.5
2026-10-07 04:14:24 -04:00
|
|
|
|
fichiers = propositions.map((proposition) => {
|
|
|
|
|
|
const rangee = options.attribuerSieges ? { ...proposition, plan: rangerParSiege(charge, proposition.plan, configuration) } : proposition;
|
|
|
|
|
|
return versFichier(rangee, configuration, options);
|
|
|
|
|
|
});
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
} catch (erreur) {
|
|
|
|
|
|
if (erreur instanceof ErreurStockage && erreur.code === 'COMPTEUR_SATURE') {
|
|
|
|
|
|
throw refus('COMPTEUR_SATURE', erreur.details);
|
|
|
|
|
|
}
|
|
|
|
|
|
throw erreur;
|
|
|
|
|
|
}
|
|
|
|
|
|
const ids = fichiers.map(({ id }) => id);
|
|
|
|
|
|
const premier = Math.min(...ids);
|
|
|
|
|
|
const dernier = Math.max(...ids);
|
|
|
|
|
|
const suivante = structuredClone(charge);
|
|
|
|
|
|
suivante.propositions.push(...fichiers);
|
|
|
|
|
|
suivante.prochainsIds.proposition = dernier + 1;
|
|
|
|
|
|
if (suivante.evenement.etat === 'brouillon') suivante.evenement.etat = 'propose';
|
|
|
|
|
|
return {
|
|
|
|
|
|
charge: suivante,
|
|
|
|
|
|
libelle: libelle('ENTREE_ENREGISTRER_GENERATION', { n: fichiers.length, premier, dernier }),
|
|
|
|
|
|
avertissements: [],
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* retenirProposition {id} (§ 5.7, § 9) : le placement de la proposition,
|
|
|
|
|
|
* copié, devient le retenu, que les retouches modifieront sans toucher la
|
|
|
|
|
|
* proposition ; le plan passe à retenu.
|
|
|
|
|
|
*/
|
|
|
|
|
|
function retenirProposition(charge, { id }) {
|
|
|
|
|
|
exigerModifiable(charge);
|
|
|
|
|
|
const proposition = charge.propositions.find((candidate) => candidate.id === id);
|
|
|
|
|
|
if (proposition === undefined) throw refus('PROPOSITION_INCONNUE', { id });
|
|
|
|
|
|
const { siegesAttribues, tables, capacites, tours, participants, placement } = structuredClone(proposition);
|
|
|
|
|
|
const suivante = avecEtat(charge, 'retenu');
|
|
|
|
|
|
suivante.retenu = { proposition: id, siegesAttribues, tables, capacites, tours, participants, placement };
|
|
|
|
|
|
return { charge: suivante, libelle: libelle('ENTREE_RETENIR_PROPOSITION', { id }), avertissements: [] };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* effacerPropositions {} (§ 5.7) : la liste se vide, la proposition d'où vient
|
|
|
|
|
|
* le retenu exceptée ; le compteur ne recule pas. Un plan proposé sans retenu
|
|
|
|
|
|
* revient à brouillon.
|
|
|
|
|
|
*/
|
|
|
|
|
|
function effacerPropositions(charge) {
|
|
|
|
|
|
exigerModifiable(charge);
|
|
|
|
|
|
const origine = charge.retenu?.proposition ?? null;
|
|
|
|
|
|
const gardees = charge.propositions.filter(({ id }) => id === origine);
|
|
|
|
|
|
const n = charge.propositions.length - gardees.length;
|
|
|
|
|
|
if (n === 0) throw refus('SANS_EFFET', { commande: 'effacerPropositions' });
|
|
|
|
|
|
const suivante = structuredClone(charge);
|
|
|
|
|
|
suivante.propositions = suivante.propositions.filter(({ id }) => id === origine);
|
|
|
|
|
|
if (suivante.retenu === null && suivante.evenement.etat === 'propose') suivante.evenement.etat = 'brouillon';
|
|
|
|
|
|
return {
|
|
|
|
|
|
charge: suivante,
|
|
|
|
|
|
libelle: libelle('ENTREE_EFFACER_PROPOSITIONS', { n, gardee: gardees.length > 0 ? origine : null }),
|
|
|
|
|
|
avertissements: [],
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
[ADD] application: clock, translation, formatting, displayed name
The screens need one clock, one translation mechanism and one way to
format and name, or each component grows its own. horloge.js is the only
code that reads time and arms timers, injected so tests drive it.
traduction.js keys texts by domain, in two families of tables, raises on a
missing key and never shows a raw key; libelles/ splits the label table by
domain. format.js formats numbers and dates in French, and personnes.js
composes the displayed name the frozen labels use. Nothing persists
between sessions.
Checked: 1773 node and 43 browser tests.
--- FR ---
[ADD] application : horloge, traduction, mise en forme, nom affiché
Les écrans exigent une horloge, un mécanisme de traduction et une façon de
mettre en forme et de nommer, sans quoi chaque composant ferait les siens.
horloge.js est le seul code qui lit l'heure et arme des minuteries, injecté
pour que les épreuves le pilotent. traduction.js range les textes par
domaine, en deux familles de tables, lève sur une clé absente et ne montre
jamais une clé brute ; libelles/ découpe la table des libellés par domaine.
format.js met en forme nombres et dates en français, et personnes.js
compose le nom affiché qu'emploient les libellés figés. Rien ne persiste
d'une séance à l'autre.
Vérifié : 1773 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:39 -04:00
|
|
|
|
/**
|
|
|
|
|
|
* Un registre réuni de plusieurs : chaque nom une fois, dans l'ordre des
|
|
|
|
|
|
* registres reçus, puis de chacun.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {...Map<string, Function>} registres
|
|
|
|
|
|
* @returns {Map<string, Function>}
|
|
|
|
|
|
* @throws {RangeError} un nom porté par deux registres, qu'il nomme
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function reunirCommandes(...registres) {
|
|
|
|
|
|
const reunies = new Map();
|
|
|
|
|
|
for (const registre of registres) {
|
|
|
|
|
|
for (const [nom, commande] of registre) {
|
|
|
|
|
|
if (reunies.has(nom)) throw new RangeError(`commandes : ${JSON.stringify(nom)} est dans deux registres`);
|
|
|
|
|
|
reunies.set(nom, commande);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return reunies;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
/**
|
|
|
|
|
|
* Le registre des commandes, par nom : chacune (charge, arguments) →
|
|
|
|
|
|
* { charge, libelle, avertissements }, et importerParticipants y ajoute le
|
[ADD] application: clock, translation, formatting, displayed name
The screens need one clock, one translation mechanism and one way to
format and name, or each component grows its own. horloge.js is the only
code that reads time and arms timers, injected so tests drive it.
traduction.js keys texts by domain, in two families of tables, raises on a
missing key and never shows a raw key; libelles/ splits the label table by
domain. format.js formats numbers and dates in French, and personnes.js
composes the displayed name the frozen labels use. Nothing persists
between sessions.
Checked: 1773 node and 43 browser tests.
--- FR ---
[ADD] application : horloge, traduction, mise en forme, nom affiché
Les écrans exigent une horloge, un mécanisme de traduction et une façon de
mettre en forme et de nommer, sans quoi chaque composant ferait les siens.
horloge.js est le seul code qui lit l'heure et arme des minuteries, injecté
pour que les épreuves le pilotent. traduction.js range les textes par
domaine, en deux familles de tables, lève sur une clé absente et ne montre
jamais une clé brute ; libelles/ découpe la table des libellés par domaine.
format.js met en forme nombres et dates en français, et personnes.js
compose le nom affiché qu'emploient les libellés figés. Rien ne persiste
d'une séance à l'autre.
Vérifié : 1773 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:39 -04:00
|
|
|
|
* résumé de l'import. Il réunit les commandes de ce module, celles du
|
|
|
|
|
|
* mobilier et celles des places.
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
*
|
|
|
|
|
|
* @typedef {{code: string, details: Object}} Avertissement
|
|
|
|
|
|
* @typedef {{charge: Object, libelle: string, avertissements: Avertissement[]}} Rendu
|
|
|
|
|
|
* @type {Map<string, (charge: Object, args: Object) => Rendu>}
|
|
|
|
|
|
*/
|
[ADD] application: clock, translation, formatting, displayed name
The screens need one clock, one translation mechanism and one way to
format and name, or each component grows its own. horloge.js is the only
code that reads time and arms timers, injected so tests drive it.
traduction.js keys texts by domain, in two families of tables, raises on a
missing key and never shows a raw key; libelles/ splits the label table by
domain. format.js formats numbers and dates in French, and personnes.js
composes the displayed name the frozen labels use. Nothing persists
between sessions.
Checked: 1773 node and 43 browser tests.
--- FR ---
[ADD] application : horloge, traduction, mise en forme, nom affiché
Les écrans exigent une horloge, un mécanisme de traduction et une façon de
mettre en forme et de nommer, sans quoi chaque composant ferait les siens.
horloge.js est le seul code qui lit l'heure et arme des minuteries, injecté
pour que les épreuves le pilotent. traduction.js range les textes par
domaine, en deux familles de tables, lève sur une clé absente et ne montre
jamais une clé brute ; libelles/ découpe la table des libellés par domaine.
format.js met en forme nombres et dates en français, et personnes.js
compose le nom affiché qu'emploient les libellés figés. Rien ne persiste
d'une séance à l'autre.
Vérifié : 1773 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:39 -04:00
|
|
|
|
export const COMMANDES = reunirCommandes(
|
|
|
|
|
|
new Map([
|
|
|
|
|
|
['ajouterParticipant', ajouterParticipant],
|
|
|
|
|
|
['modifierParticipant', modifierParticipant],
|
|
|
|
|
|
['exclureParticipant', exclureParticipant],
|
|
|
|
|
|
['reintegrerParticipant', reintegrerParticipant],
|
|
|
|
|
|
['supprimerParticipant', supprimerParticipant],
|
|
|
|
|
|
['importerParticipants', importerParticipants],
|
|
|
|
|
|
['changerEtat', changerEtat],
|
|
|
|
|
|
['debloquer', debloquer],
|
|
|
|
|
|
['enregistrerGeneration', enregistrerGeneration],
|
|
|
|
|
|
['retenirProposition', retenirProposition],
|
|
|
|
|
|
['effacerPropositions', effacerPropositions],
|
|
|
|
|
|
]),
|
|
|
|
|
|
COMMANDES_MOBILIER,
|
|
|
|
|
|
COMMANDES_PLACES,
|
|
|
|
|
|
);
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
|
[ADD] outputs: dated placement, draft rule, page footer, machine paper
A badge or a printed page must say which placement it shows and whether it
is a draft. The retained plan and each proposal carry an optional
timestamp, set when their placement changes and kept when a command rebuilds
it unchanged, with or without an instant; format 1 is unchanged and the
shipped demonstrations stay byte-identical. Anything but the retained plan
under a retained or locked state prints as a draft, proposals included. The
footer names event, date, round, timestamp and visible version.
Checked: red first; 3085 node and 639 browser tests green from the index.
--- FR ---
[ADD] sorties : placement daté, règle du brouillon, pied, papier
Un badge ou une page imprimée doit dire quel placement il montre et s'il
est un brouillon. Le retenu et chaque proposition portent un horodatage
facultatif, posé quand leur placement change et gardé quand une commande le
reconstruit à l'identique, avec ou sans instant ; le format 1 ne change pas
et les démonstrations livrées restent identiques à l'octet. Tout ce qui
n'est pas le retenu sous l'état retenu ou bloqué s'imprime en brouillon,
propositions comprises. Le pied nomme événement, date, tour, horodatage et
version visible.
Vérifié : rouge d'abord ; 3085 node et 639 navigateur verts depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:50:31 -04:00
|
|
|
|
// Le retenu sans son horodatage : une copie de premier niveau, que la
|
|
|
|
|
|
// comparaison des placements lit sans le modifier.
|
|
|
|
|
|
function sansHorodatage(retenu) {
|
|
|
|
|
|
const { horodatage, ...reste } = retenu;
|
|
|
|
|
|
return reste;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Le texte canonique du retenu, horodatage retiré, sous une charge vidée de
|
|
|
|
|
|
// tout le reste : deux retenus de même texte décrivent le même placement,
|
|
|
|
|
|
// quel que soit l'ordre de leurs clés ou des listes que la canonisation
|
|
|
|
|
|
// range. La copie canonique d'un retenu ne lit que lui.
|
|
|
|
|
|
const VIDE = { participants: [], tables: [], reservations: [], titres: [], propositions: [] };
|
|
|
|
|
|
const texteDuPlacement = (charge, retenu) => serialiserCharge({ ...charge, ...VIDE, retenu: sansHorodatage(retenu) });
|
|
|
|
|
|
|
|
|
|
|
|
// La charge rendue datée de horodatage (§ 8.9) : le retenu quand retenuNeuf,
|
|
|
|
|
|
// et chaque proposition dont l'identifiant manque à la charge reçue. Les
|
|
|
|
|
|
// objets datés sont neufs : ni la charge rendue ni la reçue ne changent.
|
|
|
|
|
|
function dater(recue, rendue, horodatage, retenuNeuf) {
|
|
|
|
|
|
const anciennes = new Set(recue.propositions.map(({ id }) => id));
|
|
|
|
|
|
return {
|
|
|
|
|
|
...rendue,
|
|
|
|
|
|
propositions: rendue.propositions.map((p) => (anciennes.has(p.id) ? p : { ...p, horodatage })),
|
|
|
|
|
|
retenu: retenuNeuf ? { ...rendue.retenu, horodatage } : rendue.retenu,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
/**
|
|
|
|
|
|
* Applique la commande nom à la charge : ce qu'elle rend, avertissements []
|
|
|
|
|
|
* quand elle n'en donne pas. Un geste dont la charge rendue s'écrit comme la
|
|
|
|
|
|
* charge reçue ne change rien, et n'est pas une entrée (§ 8.2) : SANS_EFFET.
|
[FIX] storage, session: strict UTF-8, lost journal, lock, exclusions
The whole-iteration review found ways to lose or corrupt data. A state
saved in another encoding opened with its accents turned to U+FFFD and was
then overwritten; decoding is now strict, the state opens read-only, and a
gesture from its secours first moves it to the trash. A journal decodes line
by line, so one bad byte drops only the lines after it. A vanished journal
is rewritten whole, never appended to. Deleting refuses an event another
session holds; switching to writing rereads the disk; an import that
changes exclu says which reservations it suspends.
Checked: 1504 node, 35 browser, 29 node-long; depot.js, journal.js 100 %.
--- FR ---
[FIX] stockage, séance : UTF-8 strict, journal perdu, verrou, exclusions
La revue d'ensemble a trouvé des façons de perdre ou d'abîmer des données.
Un état enregistré dans un autre encodage s'ouvrait, ses accents changés en
U+FFFD, puis s'écrasait ; le décodage est strict, l'état s'ouvre en lecture
seule, et un geste depuis son secours le range d'abord à la corbeille. Le
journal se décode ligne par ligne : un octet fautif n'écarte que la suite.
Un journal disparu se réécrit en entier, jamais par ajout. Supprimer refuse
un événement qu'une autre séance tient ; passer en écriture relit le disque ;
un import qui change exclu dit quelles réservations il suspend.
Vérifié : 1504 node, 35 navigateur, 29 node-long ; depot.js, journal.js 100 %.
Assisted-by: Claude Opus 5.5
2026-10-07 01:20:42 -04:00
|
|
|
|
* Un rendu au libellé null ne change rien non plus, mais porte ce qu'il y a
|
|
|
|
|
|
* à lire — l'import dont seules des lignes refusées auraient changé la
|
|
|
|
|
|
* liste : il passe tel quel, et rien ne s'écrit.
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
*
|
[ADD] outputs: dated placement, draft rule, page footer, machine paper
A badge or a printed page must say which placement it shows and whether it
is a draft. The retained plan and each proposal carry an optional
timestamp, set when their placement changes and kept when a command rebuilds
it unchanged, with or without an instant; format 1 is unchanged and the
shipped demonstrations stay byte-identical. Anything but the retained plan
under a retained or locked state prints as a draft, proposals included. The
footer names event, date, round, timestamp and visible version.
Checked: red first; 3085 node and 639 browser tests green from the index.
--- FR ---
[ADD] sorties : placement daté, règle du brouillon, pied, papier
Un badge ou une page imprimée doit dire quel placement il montre et s'il
est un brouillon. Le retenu et chaque proposition portent un horodatage
facultatif, posé quand leur placement change et gardé quand une commande le
reconstruit à l'identique, avec ou sans instant ; le format 1 ne change pas
et les démonstrations livrées restent identiques à l'octet. Tout ce qui
n'est pas le retenu sous l'état retenu ou bloqué s'imprime en brouillon,
propositions comprises. Le pied nomme événement, date, tour, horodatage et
version visible.
Vérifié : rouge d'abord ; 3085 node et 639 navigateur verts depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:50:31 -04:00
|
|
|
|
* horodatage, l'instant du geste que la séance lit à son horloge, date le
|
|
|
|
|
|
* placement (§ 8.9, § 11.8). Un retenu que la commande rend inchangé à
|
|
|
|
|
|
* l'horodatage près garde celui du reçu, présent ou absent, même quand elle
|
|
|
|
|
|
* l'a reconstruit sans lui ; SANS_EFFET se juge ensuite, avant toute
|
|
|
|
|
|
* datation : un geste sans effet ne le devient pas par son instant. Puis le
|
|
|
|
|
|
* retenu qui change, ou qui apparaît, prend horodatage, et chaque proposition
|
|
|
|
|
|
* dont l'identifiant manque à la charge reçue aussi ; les autres gardent le
|
|
|
|
|
|
* leur. Corriger un nom ne date rien : l'horodatage date le placement, non
|
|
|
|
|
|
* la page. horodatage null, le défaut, ne pose ni ne retire rien : un
|
|
|
|
|
|
* retenu reconstruit à l'identique garde l'horodatage reçu, et aucun autre
|
|
|
|
|
|
* n'est posé.
|
|
|
|
|
|
*
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
* @param {string} nom
|
|
|
|
|
|
* @param {Object} charge
|
|
|
|
|
|
* @param {Object} [args]
|
[ADD] outputs: dated placement, draft rule, page footer, machine paper
A badge or a printed page must say which placement it shows and whether it
is a draft. The retained plan and each proposal carry an optional
timestamp, set when their placement changes and kept when a command rebuilds
it unchanged, with or without an instant; format 1 is unchanged and the
shipped demonstrations stay byte-identical. Anything but the retained plan
under a retained or locked state prints as a draft, proposals included. The
footer names event, date, round, timestamp and visible version.
Checked: red first; 3085 node and 639 browser tests green from the index.
--- FR ---
[ADD] sorties : placement daté, règle du brouillon, pied, papier
Un badge ou une page imprimée doit dire quel placement il montre et s'il
est un brouillon. Le retenu et chaque proposition portent un horodatage
facultatif, posé quand leur placement change et gardé quand une commande le
reconstruit à l'identique, avec ou sans instant ; le format 1 ne change pas
et les démonstrations livrées restent identiques à l'octet. Tout ce qui
n'est pas le retenu sous l'état retenu ou bloqué s'imprime en brouillon,
propositions comprises. Le pied nomme événement, date, tour, horodatage et
version visible.
Vérifié : rouge d'abord ; 3085 node et 639 navigateur verts depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:50:31 -04:00
|
|
|
|
* @param {{horodatage?: string|null}} [options] horodatage de la forme
|
|
|
|
|
|
* d'estHorodatage, ou null
|
[FIX] storage, session: strict UTF-8, lost journal, lock, exclusions
The whole-iteration review found ways to lose or corrupt data. A state
saved in another encoding opened with its accents turned to U+FFFD and was
then overwritten; decoding is now strict, the state opens read-only, and a
gesture from its secours first moves it to the trash. A journal decodes line
by line, so one bad byte drops only the lines after it. A vanished journal
is rewritten whole, never appended to. Deleting refuses an event another
session holds; switching to writing rereads the disk; an import that
changes exclu says which reservations it suspends.
Checked: 1504 node, 35 browser, 29 node-long; depot.js, journal.js 100 %.
--- FR ---
[FIX] stockage, séance : UTF-8 strict, journal perdu, verrou, exclusions
La revue d'ensemble a trouvé des façons de perdre ou d'abîmer des données.
Un état enregistré dans un autre encodage s'ouvrait, ses accents changés en
U+FFFD, puis s'écrasait ; le décodage est strict, l'état s'ouvre en lecture
seule, et un geste depuis son secours le range d'abord à la corbeille. Le
journal se décode ligne par ligne : un octet fautif n'écarte que la suite.
Un journal disparu se réécrit en entier, jamais par ajout. Supprimer refuse
un événement qu'une autre séance tient ; passer en écriture relit le disque ;
un import qui change exclu dit quelles réservations il suspend.
Vérifié : 1504 node, 35 navigateur, 29 node-long ; depot.js, journal.js 100 %.
Assisted-by: Claude Opus 5.5
2026-10-07 01:20:42 -04:00
|
|
|
|
* @returns {{charge: Object, libelle: string|null, avertissements: Array<{code: string, details: Object}>}}
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
* @throws {ErreurCommande} le refus de la commande, ou SANS_EFFET
|
[ADD] outputs: dated placement, draft rule, page footer, machine paper
A badge or a printed page must say which placement it shows and whether it
is a draft. The retained plan and each proposal carry an optional
timestamp, set when their placement changes and kept when a command rebuilds
it unchanged, with or without an instant; format 1 is unchanged and the
shipped demonstrations stay byte-identical. Anything but the retained plan
under a retained or locked state prints as a draft, proposals included. The
footer names event, date, round, timestamp and visible version.
Checked: red first; 3085 node and 639 browser tests green from the index.
--- FR ---
[ADD] sorties : placement daté, règle du brouillon, pied, papier
Un badge ou une page imprimée doit dire quel placement il montre et s'il
est un brouillon. Le retenu et chaque proposition portent un horodatage
facultatif, posé quand leur placement change et gardé quand une commande le
reconstruit à l'identique, avec ou sans instant ; le format 1 ne change pas
et les démonstrations livrées restent identiques à l'octet. Tout ce qui
n'est pas le retenu sous l'état retenu ou bloqué s'imprime en brouillon,
propositions comprises. Le pied nomme événement, date, tour, horodatage et
version visible.
Vérifié : rouge d'abord ; 3085 node et 639 navigateur verts depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:50:31 -04:00
|
|
|
|
* @throws {TypeError} une commande inconnue, un argument de forme fausse, un
|
|
|
|
|
|
* horodatage ni null ni de la forme d'estHorodatage
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
*/
|
[ADD] outputs: dated placement, draft rule, page footer, machine paper
A badge or a printed page must say which placement it shows and whether it
is a draft. The retained plan and each proposal carry an optional
timestamp, set when their placement changes and kept when a command rebuilds
it unchanged, with or without an instant; format 1 is unchanged and the
shipped demonstrations stay byte-identical. Anything but the retained plan
under a retained or locked state prints as a draft, proposals included. The
footer names event, date, round, timestamp and visible version.
Checked: red first; 3085 node and 639 browser tests green from the index.
--- FR ---
[ADD] sorties : placement daté, règle du brouillon, pied, papier
Un badge ou une page imprimée doit dire quel placement il montre et s'il
est un brouillon. Le retenu et chaque proposition portent un horodatage
facultatif, posé quand leur placement change et gardé quand une commande le
reconstruit à l'identique, avec ou sans instant ; le format 1 ne change pas
et les démonstrations livrées restent identiques à l'octet. Tout ce qui
n'est pas le retenu sous l'état retenu ou bloqué s'imprime en brouillon,
propositions comprises. Le pied nomme événement, date, tour, horodatage et
version visible.
Vérifié : rouge d'abord ; 3085 node et 639 navigateur verts depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:50:31 -04:00
|
|
|
|
export function appliquerCommande(nom, charge, args = {}, { horodatage = null } = {}) {
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
const commande = COMMANDES.get(nom);
|
|
|
|
|
|
if (commande === undefined) throw new TypeError(`commande inconnue : ${JSON.stringify(nom)}`);
|
[ADD] outputs: dated placement, draft rule, page footer, machine paper
A badge or a printed page must say which placement it shows and whether it
is a draft. The retained plan and each proposal carry an optional
timestamp, set when their placement changes and kept when a command rebuilds
it unchanged, with or without an instant; format 1 is unchanged and the
shipped demonstrations stay byte-identical. Anything but the retained plan
under a retained or locked state prints as a draft, proposals included. The
footer names event, date, round, timestamp and visible version.
Checked: red first; 3085 node and 639 browser tests green from the index.
--- FR ---
[ADD] sorties : placement daté, règle du brouillon, pied, papier
Un badge ou une page imprimée doit dire quel placement il montre et s'il
est un brouillon. Le retenu et chaque proposition portent un horodatage
facultatif, posé quand leur placement change et gardé quand une commande le
reconstruit à l'identique, avec ou sans instant ; le format 1 ne change pas
et les démonstrations livrées restent identiques à l'octet. Tout ce qui
n'est pas le retenu sous l'état retenu ou bloqué s'imprime en brouillon,
propositions comprises. Le pied nomme événement, date, tour, horodatage et
version visible.
Vérifié : rouge d'abord ; 3085 node et 639 navigateur verts depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:50:31 -04:00
|
|
|
|
if (horodatage !== null && !estHorodatage(horodatage)) {
|
|
|
|
|
|
throw new TypeError(`horodatage : AAAA-MM-JJTHH:MM:SS±HH:MM ou null attendu, reçu ${JSON.stringify(horodatage)}`);
|
|
|
|
|
|
}
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
const rendu = commande(charge, args ?? {});
|
[FIX] storage, session: strict UTF-8, lost journal, lock, exclusions
The whole-iteration review found ways to lose or corrupt data. A state
saved in another encoding opened with its accents turned to U+FFFD and was
then overwritten; decoding is now strict, the state opens read-only, and a
gesture from its secours first moves it to the trash. A journal decodes line
by line, so one bad byte drops only the lines after it. A vanished journal
is rewritten whole, never appended to. Deleting refuses an event another
session holds; switching to writing rereads the disk; an import that
changes exclu says which reservations it suspends.
Checked: 1504 node, 35 browser, 29 node-long; depot.js, journal.js 100 %.
--- FR ---
[FIX] stockage, séance : UTF-8 strict, journal perdu, verrou, exclusions
La revue d'ensemble a trouvé des façons de perdre ou d'abîmer des données.
Un état enregistré dans un autre encodage s'ouvrait, ses accents changés en
U+FFFD, puis s'écrasait ; le décodage est strict, l'état s'ouvre en lecture
seule, et un geste depuis son secours le range d'abord à la corbeille. Le
journal se décode ligne par ligne : un octet fautif n'écarte que la suite.
Un journal disparu se réécrit en entier, jamais par ajout. Supprimer refuse
un événement qu'une autre séance tient ; passer en écriture relit le disque ;
un import qui change exclu dit quelles réservations il suspend.
Vérifié : 1504 node, 35 navigateur, 29 node-long ; depot.js, journal.js 100 %.
Assisted-by: Claude Opus 5.5
2026-10-07 01:20:42 -04:00
|
|
|
|
if (rendu.libelle === null) return { ...rendu, avertissements: rendu.avertissements ?? [] };
|
[ADD] outputs: dated placement, draft rule, page footer, machine paper
A badge or a printed page must say which placement it shows and whether it
is a draft. The retained plan and each proposal carry an optional
timestamp, set when their placement changes and kept when a command rebuilds
it unchanged, with or without an instant; format 1 is unchanged and the
shipped demonstrations stay byte-identical. Anything but the retained plan
under a retained or locked state prints as a draft, proposals included. The
footer names event, date, round, timestamp and visible version.
Checked: red first; 3085 node and 639 browser tests green from the index.
--- FR ---
[ADD] sorties : placement daté, règle du brouillon, pied, papier
Un badge ou une page imprimée doit dire quel placement il montre et s'il
est un brouillon. Le retenu et chaque proposition portent un horodatage
facultatif, posé quand leur placement change et gardé quand une commande le
reconstruit à l'identique, avec ou sans instant ; le format 1 ne change pas
et les démonstrations livrées restent identiques à l'octet. Tout ce qui
n'est pas le retenu sous l'état retenu ou bloqué s'imprime en brouillon,
propositions comprises. Le pied nomme événement, date, tour, horodatage et
version visible.
Vérifié : rouge d'abord ; 3085 node et 639 navigateur verts depuis l'index.
Assisted-by: Claude Opus 5.5
2026-10-09 04:50:31 -04:00
|
|
|
|
let suivante = rendu.charge;
|
|
|
|
|
|
const { retenu } = suivante;
|
|
|
|
|
|
const memePlacement =
|
|
|
|
|
|
retenu !== null &&
|
|
|
|
|
|
charge.retenu !== null &&
|
|
|
|
|
|
texteDuPlacement(charge, charge.retenu) === texteDuPlacement(charge, retenu);
|
|
|
|
|
|
if (memePlacement) {
|
|
|
|
|
|
const { horodatage: recu } = charge.retenu;
|
|
|
|
|
|
suivante = { ...suivante, retenu: recu === undefined ? sansHorodatage(retenu) : { ...sansHorodatage(retenu), horodatage: recu } };
|
|
|
|
|
|
}
|
|
|
|
|
|
if (serialiserCharge(suivante) === serialiserCharge(charge)) throw refus('SANS_EFFET', { commande: nom });
|
|
|
|
|
|
if (horodatage !== null) suivante = dater(charge, suivante, horodatage, retenu !== null && !memePlacement);
|
|
|
|
|
|
return { ...rendu, charge: suivante, avertissements: rendu.avertissements ?? [] };
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
[FIX] storage, session: strict UTF-8, lost journal, lock, exclusions
The whole-iteration review found ways to lose or corrupt data. A state
saved in another encoding opened with its accents turned to U+FFFD and was
then overwritten; decoding is now strict, the state opens read-only, and a
gesture from its secours first moves it to the trash. A journal decodes line
by line, so one bad byte drops only the lines after it. A vanished journal
is rewritten whole, never appended to. Deleting refuses an event another
session holds; switching to writing rereads the disk; an import that
changes exclu says which reservations it suspends.
Checked: 1504 node, 35 browser, 29 node-long; depot.js, journal.js 100 %.
--- FR ---
[FIX] stockage, séance : UTF-8 strict, journal perdu, verrou, exclusions
La revue d'ensemble a trouvé des façons de perdre ou d'abîmer des données.
Un état enregistré dans un autre encodage s'ouvrait, ses accents changés en
U+FFFD, puis s'écrasait ; le décodage est strict, l'état s'ouvre en lecture
seule, et un geste depuis son secours le range d'abord à la corbeille. Le
journal se décode ligne par ligne : un octet fautif n'écarte que la suite.
Un journal disparu se réécrit en entier, jamais par ajout. Supprimer refuse
un événement qu'une autre séance tient ; passer en écriture relit le disque ;
un import qui change exclu dit quelles réservations il suspend.
Vérifié : 1504 node, 35 navigateur, 29 node-long ; depot.js, journal.js 100 %.
Assisted-by: Claude Opus 5.5
2026-10-07 01:20:42 -04:00
|
|
|
|
/**
|
|
|
|
|
|
* La forme nommée d'un placement de la charge (§ 8.9, point 4), pour la
|
|
|
|
|
|
* lecture et le signalement d'un défaut : une lecture, ni commande ni
|
|
|
|
|
|
* entrée. cible désigne { proposition: id } ou { retenu: true }. Rend
|
|
|
|
|
|
* { lignes, fautes } : fautes, celles que le placement porte (fautes) ;
|
|
|
|
|
|
* lignes, sa forme nommée (formeNommee), une ligne par identifiant écrit,
|
|
|
|
|
|
* ou null quand sa forme est fausse (faute FORME), qui ne se lit pas en
|
|
|
|
|
|
* lignes. Une proposition fautive n'est plus dans la charge qu'examiner
|
|
|
|
|
|
* rend ; le retenu y reste, fautif ou non. La charge n'est pas modifiée.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {Object} charge
|
|
|
|
|
|
* @param {{proposition: number}|{retenu: true}} cible
|
|
|
|
|
|
* @returns {{lignes: Array<Object>|null, fautes: Array<Object>}}
|
|
|
|
|
|
* @throws {ErreurCommande} PROPOSITION_INCONNUE {id} ; AUCUN_RETENU, remède
|
|
|
|
|
|
* la section des propositions
|
|
|
|
|
|
* @throws {TypeError} une cible d'une autre forme
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function formeNommeeDe(charge, cible) {
|
|
|
|
|
|
const cles = cible !== null && typeof cible === 'object' ? clesRangees(cible) : null;
|
|
|
|
|
|
let placement;
|
|
|
|
|
|
if (cles?.length === 1 && cles[0] === 'proposition' && Number.isSafeInteger(cible.proposition)) {
|
|
|
|
|
|
placement = charge.propositions.find(({ id }) => id === cible.proposition);
|
|
|
|
|
|
if (placement === undefined) throw refus('PROPOSITION_INCONNUE', { id: cible.proposition });
|
|
|
|
|
|
} else if (cles?.length === 1 && cles[0] === 'retenu' && cible.retenu === true) {
|
|
|
|
|
|
placement = charge.retenu;
|
|
|
|
|
|
if (placement === null) throw refus('AUCUN_RETENU', {}, { geste: 'ouvrirSection', section: 'propositions' });
|
|
|
|
|
|
} else {
|
|
|
|
|
|
throw new TypeError(`cible : {proposition: id} ou {retenu: true} attendu, reçu ${JSON.stringify(cible)}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
const lues = fautes(placement);
|
|
|
|
|
|
const lisible = !lues.some(({ code }) => code === 'FORME');
|
|
|
|
|
|
return { lignes: lisible ? formeNommee(placement, charge) : null, fautes: lues };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
[ADD] application: session, named commands, French label table
The session holds one event, opens it read-only and writes only after
"Modifier" takes the lock (§ 8.4, § 8.8); calls run one after another, so a
double click writes two entries in order. Every change is a pure named
command with a frozen French label (§ 8.2); undo, redo and return write
entries and never truncate the journal (§ 8.3). A failed write — a gesture,
or the lock on a removed key — breaks the medium until the event is written
elsewhere (§ 8.6). The label table covers every code storage, CSV and the
session emit, and its test sweeps the sources so a new code needs a text.
Checked: 1416 node tests; depot.js at 100 % with the identifier lister keeps.
--- FR ---
[ADD] application : séance, commandes nommées, table des libellés
La séance tient un événement, l'ouvre en lecture et n'écrit qu'après que
« Modifier » a pris le verrou (§ 8.4, § 8.8) ; les appels passent l'un après
l'autre : un double clic écrit deux entrées, dans l'ordre. Toute modification
est une commande nommée pure au libellé français figé (§ 8.2) ; défaire,
refaire et revenir écrivent des entrées sans tronquer le journal (§ 8.3). Une
écriture refusée — un geste, ou le verrou sur une clé retirée — rompt le
support jusqu'à l'écriture ailleurs (§ 8.6). La table des libellés couvre
chaque code du stockage, du CSV et de la séance ; son épreuve balaie les
sources, et un code neuf exige un texte.
Vérifié : 1416 épreuves node ; depot.js à 100 %, lister gardant l'identifiant.
Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 23:17:36 -04:00
|
|
|
|
/**
|
|
|
|
|
|
* La charge d'un événement neuf (§ 8.1) et le libellé de sa création. Chaque
|
|
|
|
|
|
* champ se contrôle selon sa règle avant que l'identifiant ne se tire :
|
|
|
|
|
|
* NOM_REQUIS, DATE_INVALIDE {date}, SIEGES_INVALIDES {sieges},
|
|
|
|
|
|
* TOURS_INVALIDES {tours}. Le nom entre en NFC, ses blancs de bord retirés ;
|
|
|
|
|
|
* une date vide vaut null.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {{nom: string, date?: string|null, siegesParDefaut: number, tours: number}} evenement
|
|
|
|
|
|
* @param {() => string} identifiant tiré une fois, après les contrôles
|
|
|
|
|
|
* @returns {{charge: Object, libelle: string}}
|
|
|
|
|
|
* @throws {ErreurCommande}
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function chargeNeuve({ nom, date = null, siegesParDefaut, tours }, identifiant) {
|
|
|
|
|
|
const regle = (cle) => regleDuChamp(EVENEMENT, cle);
|
|
|
|
|
|
const nomLu = saisie(nom, 'nom');
|
|
|
|
|
|
const dateLue = saisie(date, 'date');
|
|
|
|
|
|
if (premiereFaute(nomLu, regle('nom')) !== null) throw refus('NOM_REQUIS');
|
|
|
|
|
|
if (premiereFaute(dateLue, regle('date')) !== null) throw refus('DATE_INVALIDE', { date });
|
|
|
|
|
|
if (premiereFaute(siegesParDefaut, regle('siegesParDefaut')) !== null) {
|
|
|
|
|
|
throw refus('SIEGES_INVALIDES', { sieges: siegesParDefaut });
|
|
|
|
|
|
}
|
|
|
|
|
|
if (premiereFaute(tours, regle('tours')) !== null) throw refus('TOURS_INVALIDES', { tours });
|
|
|
|
|
|
const charge = creerCharge({ id: identifiant(), nom: nomLu, date: dateLue, siegesParDefaut, tours });
|
|
|
|
|
|
return { charge, libelle: libelle('ENTREE_CREATION', { nom: nomLu }) };
|
|
|
|
|
|
}
|