[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
|
|
|
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
|
|
|
|
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
|
|
|
|
|
|
|
|
|
// Les textes des codes du moteur qui n'ont pas leur table ailleurs, et les
|
[ADD] application: generation controller, worker, evaluation table
Generating must never block the screen or record a plan that breaks a
rule. The controller refuses before launching, in a fixed order: an
out-of-range setting named by the engine's own check, the session's
refusals, then what makes the run useless. It runs the search in a worker
or in the thread, records only propositions whose invariants hold, skips
plans already present, and keeps the last duration for the session only.
The evaluation table ranks propositions on the minimum of encounters and
names the reservations a proposition does not honour.
Checked: 2112 node and 60 node-long tests; coverage floors kept.
--- FR ---
[ADD] application : génération, travailleur, table d'évaluation
Générer ne doit jamais bloquer l'écran ni enregistrer un plan qui enfreint
une règle. Le contrôleur refuse avant tout lancement, dans un ordre fixe :
un réglage hors de son domaine, nommé par la règle même du moteur, les
refus de la séance, puis ce qui rend le calcul vain. Il lance la recherche
dans un travailleur ou dans le fil, n'enregistre que les propositions dont
les invariants tiennent, écarte les plans déjà présents, et garde la
dernière durée pour la seule séance. La table d'évaluation classe les
propositions sur le minimum de rencontres et nomme les réservations
qu'une proposition ne tient pas.
Vérifié : 2112 node et 60 node-long ; seuils de couverture tenus.
Assisted-by: Claude Opus 5.5
2026-10-07 06:09:45 -04:00
|
|
|
// libellés de ses critères (§ 5, § 14.6) : les codes d'une
|
|
|
|
|
// ErreurConfiguration, ceux des violations de verifierInvariants et de
|
|
|
|
|
// verifierIndicateurs, et moteur.critere.<nom> pour chaque nom de CRITERES.
|
|
|
|
|
// Un code garde un seul texte : CAPACITE et TOURS, que le moteur lève sous
|
|
|
|
|
// deux formes de détails — ErreurConfiguration et violation —, se
|
|
|
|
|
// distinguent par les détails qu'ils portent. Une réservation que le moteur
|
|
|
|
|
// désigne sans identifiant l'est par son rang à partir de 0, que le texte
|
|
|
|
|
// affiche à partir de 1 ; une personne qu'une violation d'indicateur désigne
|
|
|
|
|
// par son index se nomme par participant quand l'appelant l'a traduit, par
|
|
|
|
|
// son rang sinon. INCONNU, aux mêmes détails que la faute de placement du
|
|
|
|
|
// stockage, a son texte dans libelles/stockage.js.
|
|
|
|
|
import { capitale, compte, nomDans } from './aides.js';
|
|
|
|
|
|
|
|
|
|
// Les cas d'une table que le plan ne rattache pas à l'instance, par detail.
|
|
|
|
|
const CAS_DE_TABLE = new Map([
|
|
|
|
|
['inconnue', (table) => `Le plan déclare la table d'identifiant ${table}, que l'événement n'a pas.`],
|
|
|
|
|
['doublon', (table) => `Le plan déclare plus d'une fois la table d'identifiant ${table}.`],
|
|
|
|
|
['absente', (table) => `Le plan ne déclare pas la table d'identifiant ${table}.`],
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// La personne d'une violation d'indicateur : son numéro, ou son rang dans
|
|
|
|
|
// la population quand l'appelant ne l'a pas traduit.
|
|
|
|
|
const personneDe = ({ index, participant }) =>
|
|
|
|
|
participant === undefined || participant === null ? `la personne de rang ${index + 1}` : `la personne n° ${participant}`;
|
|
|
|
|
|
|
|
|
|
// Un rang de réservation, compté à partir de 0 par le moteur.
|
|
|
|
|
const reservationDe = (rang) => `la réservation n° ${rang + 1}`;
|
[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
|
|
|
|
|
|
|
|
export const TABLE = new Map([
|
|
|
|
|
// Une salle trop petite : avertissement d'un geste qui fait croître le
|
|
|
|
|
// manque (A2), refus de la génération (A6) ; un seul texte pour les deux.
|
|
|
|
|
[
|
|
|
|
|
'PLACES_MANQUANTES',
|
|
|
|
|
({ placesManquantes }) =>
|
|
|
|
|
`Il manque ${compte(placesManquantes, 'place', 'places')} à chaque tour pour asseoir toutes les personnes : ` +
|
|
|
|
|
'ajouter des tables ou des sièges, ou exclure des personnes, rend la génération possible.',
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'SURRESERVATION',
|
|
|
|
|
({ table, tour, reservees, capacite }) =>
|
|
|
|
|
`La table d'identifiant ${table} reçoit ${compte(reservees, 'personne réservée', 'personnes réservées')} ` +
|
|
|
|
|
`au tour ${tour}, pour ${compte(capacite, 'place', 'places')}.`,
|
|
|
|
|
],
|
[ADD] application: generation controller, worker, evaluation table
Generating must never block the screen or record a plan that breaks a
rule. The controller refuses before launching, in a fixed order: an
out-of-range setting named by the engine's own check, the session's
refusals, then what makes the run useless. It runs the search in a worker
or in the thread, records only propositions whose invariants hold, skips
plans already present, and keeps the last duration for the session only.
The evaluation table ranks propositions on the minimum of encounters and
names the reservations a proposition does not honour.
Checked: 2112 node and 60 node-long tests; coverage floors kept.
--- FR ---
[ADD] application : génération, travailleur, table d'évaluation
Générer ne doit jamais bloquer l'écran ni enregistrer un plan qui enfreint
une règle. Le contrôleur refuse avant tout lancement, dans un ordre fixe :
un réglage hors de son domaine, nommé par la règle même du moteur, les
refus de la séance, puis ce qui rend le calcul vain. Il lance la recherche
dans un travailleur ou dans le fil, n'enregistre que les propositions dont
les invariants tiennent, écarte les plans déjà présents, et garde la
dernière durée pour la seule séance. La table d'évaluation classe les
propositions sur le minimum de rencontres et nomme les réservations
qu'une proposition ne tient pas.
Vérifié : 2112 node et 60 node-long ; seuils de couverture tenus.
Assisted-by: Claude Opus 5.5
2026-10-07 06:09:45 -04:00
|
|
|
|
|
|
|
|
// --- La configuration (normaliser, § 4, § 5.2) ------------------------------
|
|
|
|
|
// CAPACITE {table, capacite} : une capacité refusée ; {table, tour} : une
|
|
|
|
|
// liste du plan plus longue que sa table.
|
|
|
|
|
[
|
|
|
|
|
'CAPACITE',
|
|
|
|
|
({ table, capacite, tour }) =>
|
|
|
|
|
capacite === undefined
|
|
|
|
|
? `Au tour ${tour}, la table d'identifiant ${table} reçoit plus de personnes qu'elle n'a de places.`
|
|
|
|
|
: `La table d'identifiant ${table} a une capacité de ${capacite} : une table compte au moins 2 places.`,
|
|
|
|
|
],
|
|
|
|
|
// TOURS {tours} : un nombre de tours refusé ; {} : un plan qui n'a pas un
|
|
|
|
|
// tour par tour de l'événement.
|
|
|
|
|
[
|
|
|
|
|
'TOURS',
|
|
|
|
|
({ tours }) =>
|
|
|
|
|
tours === undefined
|
|
|
|
|
? "Le plan ne porte pas autant de tours que l'événement."
|
|
|
|
|
: `Le nombre de tours, ${tours}, n'est pas un entier d'au moins 1.`,
|
|
|
|
|
],
|
|
|
|
|
['PARTICIPANT_DOUBLON', ({ participant }) => `La personne n° ${participant} figure plus d'une fois dans la liste.`],
|
|
|
|
|
['TABLE_DOUBLON', ({ table }) => `La table d'identifiant ${table} figure plus d'une fois.`],
|
|
|
|
|
[
|
|
|
|
|
'RESERVATION_INCONNUE',
|
|
|
|
|
({ reservation, participant, table }) =>
|
|
|
|
|
participant === undefined
|
|
|
|
|
? `${capitale(reservationDe(reservation))} désigne la table d'identifiant ${table}, que l'événement n'a pas.`
|
|
|
|
|
: `${capitale(reservationDe(reservation))} désigne la personne n° ${participant}, que la liste n'a pas.`,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'RESERVATION_TOUR',
|
|
|
|
|
({ reservation, tour }) => `${capitale(reservationDe(reservation))} désigne le tour ${tour}, que l'événement n'a pas.`,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'RESERVATION_CONFLIT',
|
|
|
|
|
({ participant, tour, tables }) =>
|
|
|
|
|
`La personne n° ${participant} est réservée au tour ${tour} à deux tables, ` +
|
|
|
|
|
`d'identifiants ${tables[0]} et ${tables[1]}.`,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'ENUMERATION_TROP_GRANDE',
|
|
|
|
|
({ participant, limite }) =>
|
|
|
|
|
`Le plafond a priori de la personne n° ${participant} demanderait d'énumérer plus de ${limite} itinéraires.`,
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
// --- Le plan confronté à la configuration (indexerPlan) -------------------------
|
|
|
|
|
[
|
|
|
|
|
'PLAN_TOURS',
|
|
|
|
|
({ attendu, tours, reserves }) =>
|
|
|
|
|
`Le plan porte ${compte(tours, 'tour', 'tours')} et ${compte(reserves, 'réserve', 'réserves')}, ` +
|
|
|
|
|
`pour ${compte(attendu, 'tour', 'tours')} dans l'événement.`,
|
|
|
|
|
],
|
|
|
|
|
['PLAN_TABLE_INCONNUE', ({ table }) => `Le plan déclare la table d'identifiant ${table}, que l'événement n'a pas.`],
|
|
|
|
|
['PLAN_TABLE_DOUBLON', ({ table }) => `Le plan déclare plus d'une fois la table d'identifiant ${table}.`],
|
|
|
|
|
['PLAN_TABLE_ABSENTE', ({ table }) => `Le plan ne déclare pas la table d'identifiant ${table}.`],
|
|
|
|
|
[
|
|
|
|
|
'PLAN_LISTES',
|
|
|
|
|
({ tour, listes, tables }) =>
|
|
|
|
|
`Au tour ${tour}, le plan porte ${compte(listes, 'liste', 'listes')} pour ${compte(tables, 'table', 'tables')}.`,
|
|
|
|
|
],
|
|
|
|
|
['PLAN_INCONNU', ({ participant, tour }) => `Au tour ${tour}, le plan place la personne n° ${participant}, que la liste n'a pas.`],
|
|
|
|
|
['PLAN_EXCLU_PLACE', ({ participant, tour }) => `Au tour ${tour}, le plan place la personne n° ${participant}, qui est exclue.`],
|
|
|
|
|
['PLAN_DOUBLE_PLACE', ({ participant, tour }) => `Au tour ${tour}, le plan place plus d'une fois la personne n° ${participant}.`],
|
|
|
|
|
['PLAN_NON_ASSIS', ({ participant, tour }) => `Au tour ${tour}, le plan ne place nulle part la personne n° ${participant}.`],
|
|
|
|
|
|
|
|
|
|
// --- Les violations de verifierInvariants (§ 14.12) -----------------------------
|
|
|
|
|
[
|
|
|
|
|
'TABLE_INCONNUE',
|
|
|
|
|
({ table, tour, detail }) =>
|
|
|
|
|
detail === 'listes'
|
|
|
|
|
? `Au tour ${tour}, le plan n'a pas une liste par table déclarée.`
|
|
|
|
|
: nomDans(CAS_DE_TABLE, detail, 'cas de table')(table),
|
|
|
|
|
],
|
|
|
|
|
['DOUBLE_PLACE', ({ participant, tour }) => `Au tour ${tour}, la personne n° ${participant} est placée plus d'une fois.`],
|
|
|
|
|
['EXCLU_PLACE', ({ participant, tour }) => `Au tour ${tour}, la personne n° ${participant} est placée alors qu'elle est exclue.`],
|
|
|
|
|
['NON_ASSIS', ({ participant, tour }) => `Au tour ${tour}, la personne n° ${participant} n'est assise à aucune table.`],
|
|
|
|
|
[
|
|
|
|
|
'RESERVATION',
|
|
|
|
|
({ participant, table, tour }) =>
|
|
|
|
|
`Au tour ${tour}, la personne n° ${participant}, réservée à la table d'identifiant ${table}, est placée ailleurs.`,
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
// --- Les violations de verifierIndicateurs (§ 12.3) -----------------------------
|
|
|
|
|
[
|
|
|
|
|
'PLAFOND_A_PRIORI',
|
|
|
|
|
(details) => `Le plafond a priori de ${personneDe(details)} dépasse le nombre des autres personnes : le calcul est faux.`,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'ORDRE_PLAFONDS',
|
|
|
|
|
(details) => `Le plafond réalisé de ${personneDe(details)} dépasse son plafond a priori : le calcul est faux.`,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'PLAFOND_REALISE',
|
|
|
|
|
(details) => `Le plafond réalisé de ${personneDe(details)} dépasse le nombre des autres personnes : le calcul est faux.`,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'DEPASSEMENT',
|
|
|
|
|
(details) => `Les rencontres de ${personneDe(details)} dépassent son plafond réalisé : le calcul est faux.`,
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
// --- Les critères du classement (§ 5.7) -----------------------------------------
|
|
|
|
|
['moteur.critere.ecartAuPlafondAPrioriMax', 'écart au plafond a priori'],
|
|
|
|
|
['moteur.critere.excedentCollisions', 'excédent de collisions'],
|
|
|
|
|
['moteur.critere.collisionsCumulees', 'collisions cumulées'],
|
|
|
|
|
['moteur.critere.rencontresRepetees', 'rencontres répétées'],
|
|
|
|
|
['moteur.critere.redondance', "redondance d'appartenance"],
|
[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
|
|
|
]);
|