The operator plans, the room is not tracked live: removing a person from the retained plan must free her chair and leave every other seat where it is. With seats assigned, a table list may now hold null before its last occupant, seat 1 included, and never end on it; without assignment no list holds null. Validation, canonical form, patches and the named form handle it, and the delivered demos keep their bytes. Checked: 1549 node and 58 node-long tests; depot.js, journal.js at 100 %. --- FR --- [ADD] stockage : chaise vide dans une liste de table aux sièges attribués L'opérateur planifie, la salle ne se suit pas en direct : retirer une personne du plan retenu doit libérer sa chaise et laisser tout autre siège à sa place. Sièges attribués, une liste de table peut porter null avant son dernier occupant, siège 1 compris, jamais à la fin ; sans attribution, aucune liste ne porte null. Contrôle, forme canonique, correctifs et forme nommée le traitent, et les démonstrations livrées gardent leurs octets. Vérifié : 1549 node et 58 node-long ; depot.js, journal.js à 100 %. Assisted-by: Claude Opus 5.5
4868 lines
292 KiB
Markdown
4868 lines
292 KiB
Markdown
# Gestion table tournante Libre — spécification
|
||
|
||
Exécutable livré : `gestion_table_tournante_libre_v<AAAA>_<MM>_<JJ>_<NN>.exe`
|
||
— un **gabarit**, jamais un nom figé : le nom porte la date de la livraison
|
||
(§ 18.2).
|
||
|
||
---
|
||
|
||
## 1. Objet
|
||
|
||
Un logiciel qui place les personnes d'un événement autour de tables et les fait
|
||
tourner sur plusieurs tours, de sorte que chacune rencontre le plus de monde
|
||
possible. Il propose **plusieurs placements comparables** plutôt qu'une réponse
|
||
unique, mesure chacun par les mêmes indicateurs, et dit à l'opérateur ce qu'une
|
||
configuration rend inévitable avant qu'il ne cherche en vain.
|
||
|
||
Il fonctionne **hors ligne, sans serveur**, et se livre comme un exécutable.
|
||
|
||
## 2. Contexte d'usage
|
||
|
||
L'opérateur organise un souper, un colloque, un banquet. Il travaille sur un
|
||
portable, dans une salle, souvent sans réseau fiable. Il prépare son plan la
|
||
veille et le retouche sur place quand des gens manquent ou s'ajoutent.
|
||
|
||
Les conséquences de ce contexte sont des exigences :
|
||
|
||
- **Aucune connexion n'est requise**, à aucun moment, pas même au premier
|
||
démarrage.
|
||
- **Aucune installation de serveur ni de base de données.** L'exécutable se
|
||
copie et se lance.
|
||
- **Rien ne sort de la machine.** Pas de télémétrie, pas de service distant.
|
||
- Les données se copient, se sauvegardent et se transmettent comme des
|
||
fichiers ordinaires.
|
||
- **L'interface est en français.** Une seule langue dans la première
|
||
itération ; le mécanisme de traduction est néanmoins posé dès le départ
|
||
(§ 14.6), parce que retrouver les chaînes après coup coûte dix fois plus.
|
||
|
||
### 2.1 Le parcours de l'opérateur
|
||
|
||
Cette section ne pose aucune exigence nouvelle : elle met **dans l'ordre où une
|
||
soirée se prépare** des exigences dispersées sur dix sections. Elle a un
|
||
consommateur nommé — `GUIDE-USAGE.md` (§ 16.1) suit ces douze étapes, et le
|
||
scénario de pilotage du § 19.3 les implémente, de sorte que le guide, la
|
||
spécification et le logiciel ne peuvent pas diverger sur l'ordre.
|
||
|
||
Le parcours est **un ordre de lecture, pas une séquence imposée**. Le logiciel
|
||
n'enchaîne aucun assistant et n'exige jamais de recommencer au début : un
|
||
assistant linéaire obligerait l'opérateur qui découvre une faute de saisie au
|
||
moment d'imprimer à ressortir de toutes les étapes pour y revenir, or corriger
|
||
est ce qu'il fait le plus (§ 10.2).
|
||
|
||
**Deux verrous existent pourtant, et ils sont ailleurs.** L'état **bloqué**
|
||
refuse toute modification, import compris (§ 9, § 10.1) ; le **mode lecture**,
|
||
qui est le mode d'ouverture par défaut, refuse tout geste modifiant (§ 8.4).
|
||
Aucun des deux ne dépend de l'étape atteinte.
|
||
|
||
**Trois garanties valent pour tout geste qui modifie** — les étapes 2 à 7, 9,
|
||
11, et la rétention de l'étape 10 — et ne sont pas répétées ensuite :
|
||
|
||
1. le geste achevé est une entrée d'historique **et** un état sur le disque,
|
||
jamais l'un sans l'autre (§ 8.2) ;
|
||
2. il se reprend par un retour à un instant, et le journal ne se tronque jamais
|
||
(§ 8.3) ;
|
||
3. s'il est refusé, le refus nomme sa cause et le geste qui la lève, **là où le
|
||
geste a eu lieu** : sur la place pour un geste du plan (§ 5.9), dans
|
||
l'aperçu d'import pour une ligne de CSV (§ 10.1), sur le champ pour une
|
||
saisie. Seul un geste qui détruit de la saisie ouvre une fenêtre à confirmer.
|
||
|
||
Les étapes 8 et 10 — lire le diagnostic, comparer — ne modifient rien et restent
|
||
vivantes en lecture (§ 8.4).
|
||
|
||
**1. Ouvrir ou créer l'événement.** *Obligatoire.* L'opérateur choisit dans la
|
||
liste (§ 8.1), crée, ou charge une démonstration (§ 15). **Ouvrir ne réécrit
|
||
jamais le fichier d'état** (§ 8.4) ; créer et charger une démonstration
|
||
écrivent, puisqu'ils produisent un événement neuf.
|
||
|
||
**1b. Passer en écriture.** *Obligatoire avant toute modification, reprenable.*
|
||
Le § 8.4 ouvre **tout** événement en lecture : les étapes 2 à 7, 9 et 11 sont
|
||
refusées tant que la commande « Modifier » n'a pas été exécutée. Elle est à
|
||
place fixe, et le logiciel retourne seul en lecture après inactivité. Ce retour
|
||
automatique tombe **entre** deux étapes et jamais pendant un geste, et le
|
||
reprendre ne coûte rien puisque tout geste achevé est déjà sur le disque.
|
||
|
||
**2. Importer la liste des participants.** *Facultative, reprenable.* Le
|
||
logiciel montre un aperçu avant tout import (§ 10.1), importe les lignes valides
|
||
et réexporte les refusées, et compte l'import entier comme une seule entrée
|
||
d'historique. Un second import met à jour sans effacer les champs absents, ou
|
||
remplace en annonçant d'abord ce qu'il détruit. L'import est **refusé en état
|
||
bloqué**, le message nommant la commande qui déverrouille.
|
||
|
||
**3. Compléter la liste à la main.** *Facultative, reprenable, y compris après
|
||
la génération.* Le logiciel n'exige que le nom (§ 10.2) et n'invente aucune
|
||
valeur manquante.
|
||
|
||
**4. Préparer les tables.** *Obligatoire.* L'opérateur fixe le nombre de sièges
|
||
par défaut, crée les tables, surcharge celles qui diffèrent (§ 6.1) et les
|
||
dispose sur le plan (§ 7.1). Le logiciel énonce la conséquence chiffrée d'un
|
||
changement de défaut avant de l'appliquer, n'écrase aucune surcharge, et refuse
|
||
en bloc une baisse qu'une seule table refuse (§ 6.1).
|
||
|
||
**5. Fixer le nombre de tours.** *Obligatoire.* Retirer un tour puis le remettre
|
||
retrouve les **ancrages** intacts, parce que la portée « tous les tours » n'est
|
||
jamais développée (§ 4.3). La protection s'arrête là, et le logiciel le dit
|
||
**avant** d'appliquer : retirer le tour *r* détruit les réservations de portée
|
||
« tour désigné » portant sur *r*, et les tours au-delà de *r* des placements
|
||
déjà engendrés. Les annoncer après coup ferait découvrir la perte à
|
||
l'impression.
|
||
|
||
**6. Désigner les animateurs, table par table.** *Facultative — une soirée sans
|
||
aucun animateur est une configuration valide, et la petite démonstration en est
|
||
une (§ 15.3). Reprenable : une désignation se retire, se déplace, s'ajoute après
|
||
la génération.*
|
||
|
||
L'opérateur désigne **lui-même quelle personne anime quelle table**. Il n'existe
|
||
aucune commande qui répartit les animateurs sur les tables (§ 5.8).
|
||
|
||
Un geste de désignation produit **deux faits sur la même place**, sous une seule
|
||
entrée d'historique : une **réservation** de portée « tous les tours » au nom de
|
||
la personne, et un **titre de place** (§ 4). Les deux ensemble font l'animateur ;
|
||
ni l'un ni l'autre seul. Le titre reste quand son titulaire part, et la table
|
||
affiche « titre *animateur* non pourvu » (§ 4.1). Le statut d'« ancré » du § 4.2
|
||
en découle comme un fait calculé : le logiciel ne porte aucune case « est
|
||
animateur ».
|
||
|
||
**« La place » se lit de deux façons, et le logiciel tranche** : c'est un
|
||
**siège** quand l'attribution des sièges est active (§ 5.3), la **table** sinon.
|
||
Dans le second cas, une table porte un **compte de titres**, pourvus ou non —
|
||
trois animateurs à une table sans sièges attribués, c'est trois titres sur la
|
||
table, dont deux peuvent rester non pourvus. Sans cette lecture, le refus « un
|
||
siège inexistant » du § 5.9 n'a pas de sens quand les sièges ne sont pas
|
||
attribués, et la liste des titres non pourvus ne sait pas compter.
|
||
|
||
**Zéro, un, ou plusieurs animateurs par table.** Le logiciel n'impose ni
|
||
plancher ni plafond propre à ce rôle. Des trois contrôles de capacité du § 5.9,
|
||
**un seul garde cette étape** : `k_t > c_t` est refusé — pas plus d'animateurs
|
||
qu'une table n'a de sièges. `k_t = c_t` est accepté et **signalé** : la table ne
|
||
tourne plus. `Σ (c_t − k_t) < n` ne se déclenche jamais ici, l'écart étant
|
||
invariant sous toute réservation (§ 5.9). Les autres refus du § 5.9 gardent
|
||
cette étape comme n'importe quelle autre pose : personne exclue, deux places sur
|
||
un même tour, dépôt sur une place déjà réservée à un autre.
|
||
|
||
Le logiciel n'assigne pas, il **montre le travail qui reste**, par deux listes
|
||
cliquables qui mènent à la place ou à la personne : la liste des **titres de
|
||
place non pourvus** (§ 4.1), et la liste des participants portant un **titre
|
||
pressenti** (§ 4) qui ne sont encore réservés nulle part.
|
||
|
||
**7. Placer des participants à la main.** *Facultative, reprenable.* Ce sont des
|
||
réservations ordinaires (§ 5.8). Le logiciel refuse de déposer quelqu'un sur une
|
||
place réservée à un autre ; **déplacer** une réservation est une commande
|
||
séparée (§ 5.9).
|
||
|
||
**8. Lire le diagnostic.** *Le logiciel le présente, l'opérateur n'a rien à
|
||
demander.* Avant toute recherche, il énonce ce que la configuration rend
|
||
inévitable, combien, et ce qui le rendrait évitable (§ 5.6), donne le **plafond
|
||
a priori** (§ 5.5), et donne le **plancher de l'écart d'itinéraire maximal**
|
||
quand la configuration en impose un. Sans ce dernier, le troisième chiffre de la
|
||
page de qualité envoie relancer vers un zéro que la configuration interdit.
|
||
|
||
**9. Générer.** *Nécessaire pour obtenir un placement, reprenable sans perte.*
|
||
La commande prend une **graine** et un **compte d'arrêt** ; chaque proposition
|
||
conserve sa graine dérivée, ce compte et la longueur d'historique qui l'ont
|
||
produite (§ 5.7, § 8.9). Le logiciel produit plusieurs propositions
|
||
comparables ; relancer **ajoute** des candidats, un classement unique les
|
||
ordonne tous. Les réservations des étapes 6 et 7 sont honorées, le moteur place
|
||
les autres autour (§ 5.2). Le logiciel nomme automatiquement l'instant qui
|
||
précède chaque génération (§ 8.3).
|
||
|
||
**10. Comparer, et retenir.** *Facultative en droit — l'impression d'un plan non
|
||
retenu sort en filigrane BROUILLON (§ 11.7) — nécessaire en fait pour distribuer
|
||
quoi que ce soit.* L'opérateur lit la page de qualité (§ 12), compare deux
|
||
propositions sur le même axe — celui du **manque** (§ 12.10) —, retient celle
|
||
qu'il préfère. Le logiciel sépare ce que le moteur a décidé de ce que les
|
||
réservations imposent (§ 5.4). Retenir fait passer le plan à l'état *Retenu*
|
||
(§ 9) ; les propositions non retenues restent disponibles.
|
||
|
||
**11. Ajuster à la main.** *Facultative, reprenable.* Le même geste qu'à
|
||
l'étape 7 déplace maintenant une personne réelle dans le placement retenu. **Le
|
||
geste est identique, l'état des données change, et l'interface le dit** (§ 5.8).
|
||
Les indicateurs et la page de qualité suivent l'ajustement (§ 12.9 : la page
|
||
lit, elle ne recalcule rien).
|
||
|
||
**12. Imprimer.** La **planche de badges** (§ 11.1), qui est le seul document
|
||
PDF, et au besoin les vues imprimables : liste d'accueil, feuille par table,
|
||
plan, rapport, page de qualité (§ 11.7). Le logiciel n'interdit pas d'imprimer
|
||
un plan qui n'est ni retenu ni bloqué ; il le marque **BROUILLON** en filigrane.
|
||
**Produire la planche n'est pas une modification** : elle reste disponible en
|
||
mode lecture et sur un plan bloqué. L'opérateur bloque ensuite le plan pour le
|
||
distribuer (§ 9).
|
||
|
||
**Ce qui doit avoir eu lieu.** Les étapes 2, 3, 5, 6, 7, 10 et 11 sont
|
||
facultatives ; 2, 3, 4, 5, 6, 7, 9 et 11 se reprennent indéfiniment, dans
|
||
n'importe quel ordre. Deux dépendances seulement : la 4 **et** au moins l'une
|
||
des étapes 2 ou 3 avant la 9 — le générateur refuse une configuration à zéro
|
||
participant ou à zéro table (§ 15.4) — et la 10 avant la 12 pour qu'une
|
||
impression ne sorte pas en brouillon.
|
||
|
||
## 3. Périmètre
|
||
|
||
**Dans le périmètre.** Saisir les participants, décrire la salle, générer et
|
||
comparer des placements, retoucher à la main, mesurer la qualité, imprimer ce
|
||
qu'il faut pour tenir la soirée.
|
||
|
||
**Hors périmètre, explicitement.** Les inscriptions en ligne, la billetterie,
|
||
le paiement, l'envoi de courriels, le travail à plusieurs sur un même plan, la
|
||
synchronisation entre postes, les comptes et les droits d'accès.
|
||
|
||
Un seul opérateur, une seule machine, un seul événement ouvert à la fois.
|
||
|
||
---
|
||
|
||
## 4. Les objets manipulés
|
||
|
||
**Événement** — un nom, une date, un nombre de sièges par défaut, et tout le
|
||
reste ci-dessous. Le logiciel en gère plusieurs (§ 8).
|
||
|
||
**Participant** — un **identifiant entier court**, unique dans l'événement et
|
||
jamais réutilisé ; un nom ; éventuellement un prénom, un courriel, des notes,
|
||
et une **appartenance** — l'étiquette qui sépare les gens qui se connaissent
|
||
déjà : entreprise, équipe, famille, école.
|
||
|
||
> L'identifiant est un petit entier et non un identifiant opaque. Le calcul de
|
||
> taille du § 8.6 repose dessus : sous la forme positionnelle du § 8.9, un
|
||
> identifiant de trente-six caractères remplace quatre octets par trente-neuf
|
||
> et **décuple** le poids des placements, qui sont la partie volumineuse du
|
||
> fichier.
|
||
|
||
Un participant porte aussi un **titre pressenti** — « animatrice », « hôte » —
|
||
qui est **purement informatif** : il s'imprime sur sa fiche, il n'attribue
|
||
aucun rôle, il ne pourvoit aucune place et il ne contraint rien. Il existe pour
|
||
qu'une liste reçue où les animateurs sont signalés ne perde pas cette
|
||
information à l'import.
|
||
|
||
Un participant peut être **exclu** sans être supprimé : il reste dans la liste,
|
||
il n'est plus placé, et il sort de toutes les statistiques.
|
||
|
||
**Table** — un **identifiant entier**, unique dans l'événement et jamais
|
||
réutilisé, exactement comme un participant ; un **numéro affiché** à partir
|
||
de 1 ; un nombre de sièges ; une forme (ronde ou carrée) ; une position sur le
|
||
plan en centimètres. **Le nombre de sièges d'une table ne change pas d'un tour à
|
||
l'autre** : c'est une propriété du mobilier.
|
||
|
||
> L'identifiant et le numéro affiché sont deux choses. La forme positionnelle du
|
||
> § 8.9 indexe les placements par **identifiant** : indexée par le rang d'une
|
||
> table, elle se tromperait de table dès qu'une table est supprimée, et le ferait
|
||
> en silence — trente-trois listes glissent d'un cran et tout le monde change de
|
||
> salle.
|
||
|
||
**Tour** — une des R rondes de la soirée. À chaque tour, chaque participant non
|
||
exclu est assis à exactement une table.
|
||
|
||
**Placement** — pour chaque tour, qui est à quelle table, et à quel siège si
|
||
les sièges sont attribués.
|
||
|
||
**Réservation** — **le seul objet** qui fixe quelqu'un quelque part. Elle lie
|
||
une personne à une place, et porte une **portée** : *un tour désigné*, ou *tous
|
||
les tours*. Elle contraint la génération, qui l'honore, et elle lui survit.
|
||
|
||
**Titre de place** — un libellé attaché à **une place**, jamais à une personne :
|
||
« ce siège est le siège de l'animateur ». Il ne nomme personne et vaut pour
|
||
toute la soirée.
|
||
|
||
### 4.1 Pourquoi le titre appartient à la place
|
||
|
||
Si le titre voyageait avec la personne, retirer celle-ci du plan emporterait le
|
||
rôle **sans bruit** : la table se retrouverait sans animateur et rien ne le
|
||
dirait avant le soir même. Attaché au siège, le titre reste quand son titulaire
|
||
part, et le logiciel affiche « table 3 : titre *animateur* non pourvu ».
|
||
|
||
**Un titre n'est pourvu que par une réservation.** Quelqu'un que le moteur
|
||
assoit sur une place titrée n'en devient pas titulaire : la place reste comptée
|
||
« non pourvue ». Dériver le rôle de la simple occupation ferait désigner
|
||
l'animateur par un tirage au sort, changeant à chaque relance.
|
||
|
||
Les trois combinaisons se produisent dans une préparation ordinaire :
|
||
|
||
| cas | ce que l'opérateur fait |
|
||
|---|---|
|
||
| titre sans réservation | « il faut un animateur à la table 5, on ne sait pas encore qui » — **titre non pourvu**, et la liste de ces titres, dite **liste des titres de place non pourvus**, est sa liste de travail (§ 2.1, étape 6) |
|
||
| réservation sans titre | asseoir quelqu'un près de la sortie parce qu'il part tôt |
|
||
| les deux | le cas courant de l'animateur |
|
||
|
||
### 4.2 Trois statuts, calculés et non déclarés
|
||
|
||
| statut | définition |
|
||
|---|---|
|
||
| **ancré** | une réservation fixe la même table à **chacun** des R tours |
|
||
| **mobile** | aucune réservation |
|
||
| **partiellement fixé** | le reste : quelques tours, ou des tables différentes |
|
||
|
||
Le statut se dérive **du fait**, jamais d'une case à cocher ni de la forme du
|
||
stockage : qu'il résulte d'une portée « tous les tours » ou de R réservations
|
||
de tour désigné sur la même table, le participant est ancré. Une case « est
|
||
animateur » posée à côté d'une réservation absente peut mentir ; un statut
|
||
calculé ne le peut pas.
|
||
|
||
Le logiciel **propose** de convertir R réservations de tour désigné en une
|
||
portée « tous les tours », et ne le fait pas seul : la conversion change ce qui
|
||
arrive quand un tour est ajouté.
|
||
|
||
### 4.3 La portée « tous les tours » est symbolique
|
||
|
||
Elle est stockée telle quelle, **jamais développée en R réservations**. La
|
||
conséquence tranche un cas limite : retirer le tour 4 puis le remettre retrouve
|
||
l'ancrage intact, parce qu'il n'a jamais été énuméré. Une portée matérialisée
|
||
obligerait à choisir entre détruire les lignes excédentaires — l'opérateur ne
|
||
retrouve pas son travail — et les garder en sommeil, c'est-à-dire porter un
|
||
état invisible.
|
||
|
||
### 4.4 L'exclusion l'emporte
|
||
|
||
Exclure une personne **suspend toutes ses réservations**, rend ses places aux
|
||
mobiles, rend les titres correspondants non pourvus, et le logiciel le dit. La
|
||
réintégration les rend. L'exclusion décrit une absence physique : aucune table
|
||
ne peut asseoir quelqu'un qui n'est pas là.
|
||
|
||
---
|
||
|
||
## 5. Le cœur : le placement
|
||
|
||
### 5.1 Le problème
|
||
|
||
Étant donné N participants, T tables de capacités c₁…c_T et R tours, répartir
|
||
les gens à chaque tour de façon à **maximiser le nombre de personnes distinctes
|
||
que chacun rencontre**, sous les contraintes que l'opérateur active.
|
||
|
||
### 5.2 L'instance réduite : ce que les réservations changent
|
||
|
||
Soit k le nombre d'ancrés, dont k_t à la table t. Le moteur résout une
|
||
**instance réduite** : n = N − k mobiles, des tables de capacité libre
|
||
c′_t = c_t − k_t, les mêmes R tours.
|
||
|
||
C'est la forme du problème initial **à une donnée près, et elle n'est pas
|
||
facultative** : chaque table transporte **l'ensemble des appartenances de ses
|
||
ancrés**. Un mobile d'appartenance X placé à une table où siège un ancré
|
||
d'appartenance X viole « séparer les appartenances » à **chacun des R tours**.
|
||
Réduire sans cette donnée produit un plan qui assoit un invité à côté d'un
|
||
collègue animateur tous les tours, **sans qu'aucun indicateur ne s'en
|
||
aperçoive**.
|
||
|
||
Moyennant cet ajout, le moteur n'a **qu'un seul chemin de code** : la
|
||
réservation est un prétraitement qui réduit les capacités, retire des
|
||
participants et annote les tables — jamais un second algorithme.
|
||
|
||
### 5.3 Les contraintes, activables une à une
|
||
|
||
| contrainte | ce qu'elle exige |
|
||
|---|---|
|
||
| **Séparer les appartenances** | deux personnes de la même appartenance ne partagent pas une table |
|
||
| **Nouveaux voisins à chaque tour** | deux personnes ne se retrouvent pas deux fois à la même table |
|
||
| **Nouvelle table à chaque tour** | *voir ci-dessous* |
|
||
| **Varier les appartenances rencontrées** | sur l'ensemble des tours, chacun rencontre le plus d'appartenances **différentes** possible |
|
||
| **Attribuer les sièges** | chacun reçoit un numéro de siège, pas seulement une table |
|
||
|
||
Ce sont des **objectifs**, pas des interdits : quand la configuration les rend
|
||
impossibles à satisfaire toutes, le moteur produit le meilleur compromis **et
|
||
dit lequel**, au lieu de refuser.
|
||
|
||
**« Nouvelle table à chaque tour », énoncée correctement.** La formulation
|
||
naïve — « personne ne revient à une table déjà occupée » — est fausse pour un
|
||
ancré dès le deuxième tour, et une exception « sauf les animateurs » laisserait
|
||
sans réponse le cas d'un participant ordinaire réservé au tour 1. L'énoncé
|
||
porte donc sur **ce que le moteur décide** :
|
||
|
||
> Le moteur n'assoit jamais un participant à une table où il l'a déjà assis, ni
|
||
> à une table où l'opérateur l'a placé.
|
||
|
||
Aucune exception n'est nécessaire. Pour un ancré, le moteur ne décide rien : la
|
||
règle est vraie sans rien exiger.
|
||
|
||
**« Varier les appartenances » n'est pas « séparer les appartenances ».** La
|
||
première porte sur **celle des autres**, **à travers les tours** ; la seconde
|
||
sur **la sienne**, **à l'intérieur d'un tour**. Elles s'activent séparément.
|
||
|
||
### 5.4 Les indicateurs
|
||
|
||
Tout placement proposé porte les mêmes chiffres, pour que la comparaison soit
|
||
une lecture et non un jugement :
|
||
|
||
| indicateur | définition |
|
||
|---|---|
|
||
| **personnes distinctes rencontrées** | sur l'ensemble des tours ; une personne revue compte une fois |
|
||
| **collisions cumulées** | occurrences (paire, tour) où deux personnes d'une même appartenance partagent une table — l'unité dans laquelle les planchers du § 5.6 se démontrent |
|
||
| **paires de même appartenance** | **paires distinctes** réunies au moins une fois ; jamais la même unité que la ligne précédente |
|
||
| **excédent de collisions** | collisions cumulées − paires distinctes : le nombre de **retrouvailles au-delà de la première** entre deux personnes d'une même appartenance. Nul quand aucune paire de collègues n'est réunie deux fois |
|
||
| **rencontres répétées** | paires réunies plus d'une fois |
|
||
| **nombre maximal de rencontres d'une même paire** | |
|
||
| **retours à une table déjà occupée** | *voir ci-dessous* |
|
||
| **appartenances distinctes rencontrées** — A(p) | nombre d'appartenances différentes portées par les personnes **affiliées** que p rencontre, **la sienne comprise** |
|
||
| **redondance d'appartenance** — r(p) | \|F(p)\| − A(p), où F(p) sont les affiliés rencontrés ; r = 0 signifie qu'aucune appartenance n'a été recroisée |
|
||
| **taux de diversité** — d(p) | A(p) / \|F(p)\| ; « — » quand \|F(p)\| = 0 |
|
||
| **manque** | plafond réalisé − rencontres (§ 5.5) — la grandeur qui compare deux propositions (§ 12.10) |
|
||
| **écart d'itinéraire** | plafond a priori − plafond réalisé (§ 5.5, § 12.10) |
|
||
|
||
**Les agrégats portent sur tous les participants placés.** Le minimum et la
|
||
moyenne se calculent sur l'ensemble, ancrés compris, et **deux lignes
|
||
secondaires** donnent la même mesure restreinte aux **mobiles** et aux
|
||
**ancrés**. Un participant partiellement fixé compte parmi les mobiles.
|
||
|
||
> Un ancré n'est pas structurellement désavantagé : à table de 8 avec un seul
|
||
> ancré et R = 4, son plafond vaut 28, exactement celui d'un mobile visitant
|
||
> quatre tables de 8. Le retirer du minimum cacherait un animateur réellement
|
||
> mal servi, qui est précisément la personne dont on veut connaître le sort.
|
||
> Les deux lignes secondaires existent pour que l'opérateur sache si le minimum
|
||
> global désigne un défaut qu'une relance corrige, ou la conséquence de son
|
||
> propre plan de réservation.
|
||
|
||
**Le minimum compte plus que la moyenne.** Il décrit le sort de la personne la
|
||
plus mal servie, qui est celle qui se plaindra.
|
||
|
||
**Les deux unités de collision ne se mélangent jamais.** Un chiffre de collision
|
||
cité quelque part — § 5.6, § 12.4, § 15.3, une étape du pilotage — **nomme son
|
||
unité**. Un plan qui garde les deux mêmes collègues ensemble aux quatre tours
|
||
mesure **4** collisions cumulées et **1** paire distincte : confondre les deux
|
||
fait afficher une mesure inférieure à son propre plancher.
|
||
|
||
**Une collision répétée coûte plus cher que la même quantité répartie, et c'est
|
||
l'excédent qui le dit.** Trois plans à comparer, tous en conflit :
|
||
|
||
| plan | collisions cumulées | paires distinctes | excédent |
|
||
|---|---|---|---|
|
||
| un collègue retrouvé **quatre fois** | 4 | 1 | **3** |
|
||
| deux collègues retrouvés **deux fois** chacun | 4 | 2 | **2** |
|
||
| six collègues retrouvés **une fois** chacun | 6 | 6 | **0** |
|
||
|
||
Le dernier est le moins mauvais, bien qu'il porte le **plus** de collisions
|
||
cumulées — passer la soirée avec le même collègue est pire que croiser six
|
||
collègues une fois. Ni les collisions cumulées ni les paires distinctes, prises
|
||
seules, ne rendent cet ordre : **les deux classent le premier plan en tête**,
|
||
c'est-à-dire exactement à l'envers. L'excédent le rend, et c'est pour cela qu'il
|
||
ouvre le classement du § 5.7.
|
||
|
||
Les trois restent **affichés** : ce sont trois conflits, et aucun ne se cache
|
||
derrière la grandeur qui ordonne.
|
||
|
||
**Quand une population est vide, le logiciel écrit « — », jamais zéro.** Un
|
||
ensemble vide n'est pas une mesure nulle.
|
||
|
||
**Ce que le moteur a décidé, et ce que les réservations imposent.** L'indicateur
|
||
« retours à une table » ne compte que les retours **choisis par le moteur** ;
|
||
ce que les réservations imposent est énoncé **à part et chiffré** — « 99
|
||
retours imposés par 33 ancrages ». Sinon l'indicateur affiche la même valeur
|
||
pour toute proposition et cesse de les séparer. La même lecture vaut pour les
|
||
rencontres répétées entre deux personnes réservées à la même table.
|
||
|
||
**Les indicateurs se mesurent sur le plan complet**, les N participants et les
|
||
R tours, comme si les réservations n'existaient pas. Des formes closes servent
|
||
au diagnostic préalable ; elles ne corrigent jamais après coup un chiffre
|
||
mesuré. Deux arithmétiques pour une même quantité sont le mode de défaillance
|
||
que le § 13.2 interdit.
|
||
|
||
### 5.5 Le plafond et la borne prouvée
|
||
|
||
Deux notions, **deux noms, jamais trois**.
|
||
|
||
Le **plafond** est une borne arithmétique, **individuelle**, toujours
|
||
calculable. La **borne prouvée** est l'affirmation plus forte qu'une
|
||
construction l'atteint. Elle seule autorise la mention « minimum atteint ».
|
||
|
||
**Une seule formule, paramétrée par l'itinéraire.** Écrire une formule par
|
||
statut garantit qu'elles se contrediront, et aucune ne couvrirait un
|
||
participant partiellement fixé. Pour une personne p et un itinéraire — la table
|
||
qu'elle occupe à chaque tour :
|
||
|
||
- D = les tables **distinctes** visitées, m_t = tours passés à la table t ;
|
||
- a_t = le nombre d'**ancrés** de la table t, p non compté ;
|
||
- v_t = c_t − 1 − a_t = les sièges qui changent d'occupant ;
|
||
- n_p = n − 1 si p est mobile ou partiellement fixé, n s'il est ancré.
|
||
|
||
```
|
||
plafond(p) = min( N − 1 , Σ a_t + min( n_p , Σ m_t · v_t ) )
|
||
t∈D t∈D
|
||
```
|
||
|
||
Les ancrés d'une table visitée sont rencontrés **une fois**, quel que soit le
|
||
nombre de tours passés là ; les sièges libres se renouvellent, mais ne peuvent
|
||
livrer plus de personnes qu'il n'existe de mobiles.
|
||
|
||
> Le terme `min(n_p, …)` n'est pas décoratif. Un ancré **ne rencontre jamais
|
||
> les ancrés des autres tables**. Sans ce terme, sur 4 tables de 5 avec un
|
||
> ancré chacune et 5 tours, la formule annonce 19 là où 16 est le vrai maximum :
|
||
> l'opérateur voit à perpétuité un animateur « à 84 % de son plafond » et
|
||
> relance une génération qui ne peut rien.
|
||
|
||
**Deux moments, deux bases, et un glossaire qui fait autorité.** Le plafond
|
||
dépend de l'itinéraire, et les deux plafonds ne se calculent pas sur la même
|
||
base :
|
||
|
||
| terme | définition | base | où il s'affiche |
|
||
|---|---|---|---|
|
||
| **plafond a priori** | maximisé sur les itinéraires admissibles | **capacités** | diagnostic § 5.6, tuile § 12.2 |
|
||
| **plafond réalisé** | calculé sur l'itinéraire de la proposition | **occupation** | à côté de chaque mesure |
|
||
| **borne prouvée** | l'affirmation qu'une construction atteint le plafond | — | mention « minimum atteint » |
|
||
| **manque** | plafond réalisé − rencontres | — | tuile 2, courbe de comparaison (§ 12.10) |
|
||
| **écart d'itinéraire** | plafond a priori − plafond réalisé | — | tuile 3, avec son plancher |
|
||
| **écart au plafond a priori** | manque + écart d'itinéraire | — | départage § 5.7, certificat |
|
||
|
||
Ces six lignes sont la **seule** définition faisant autorité, et l'expression
|
||
« l'écart », employée seule, ne paraît nulle part : trois quantités affichées
|
||
sous un même libellé font conclure à l'opérateur que le logiciel compte mal. La
|
||
règle « deux notions, deux noms, jamais trois » n'est pas affaiblie — aucun de
|
||
ces noms ne désigne une **borne**, et il n'existe toujours que deux bornes ; elle
|
||
est simplement tenue par un tableau plutôt que par la mémoire du rédacteur.
|
||
|
||
Sans le partage a priori / réalisé, le diagnostic annonce 28, la page de qualité
|
||
affiche 27, et l'opérateur conclut à une régression du moteur.
|
||
|
||
**Le plafond réalisé se calcule sur l'occupation, jamais sur la capacité.** Soit
|
||
I l'itinéraire de p — les couples (table, tour) où p est assis —, D les tables
|
||
distinctes de I, et o_{t,r} l'occupation de la table t au tour r :
|
||
|
||
```
|
||
plafond réalisé(p) = min( N − 1 , Σ a_t + min( n_p , Σ v_{t,r} ) )
|
||
t∈D (t,r)∈I
|
||
|
||
avec v_{t,r} = o_{t,r} − 1 − a_t
|
||
```
|
||
|
||
Le terme des ancrés reste une somme sur les tables **distinctes** — un ancré
|
||
rencontré à trois tours compte une fois ; seul le terme des sièges qui se
|
||
renouvellent devient une somme sur les **tours parcourus**. Sans cette
|
||
substitution, une personne assise à une table de 8 occupée par 7 porte un manque
|
||
de 1 qu'aucun placement ne retire, et le manque perd la seule propriété qui le
|
||
rend lisible : zéro veut dire *rien à reprendre*. Les plafonds annoncés au
|
||
§ 15.1 ne changent pas, la configuration principale étant exactement tendue —
|
||
l'occupation y vaut la capacité à chaque tour.
|
||
|
||
L'inégalité `plafond a priori ≥ plafond réalisé` n'est vraie que dans cet
|
||
ordre : pour un même itinéraire, l'occupation ne dépasse jamais la capacité.
|
||
|
||
**Deux chemins seulement concluent « minimum atteint ».**
|
||
|
||
1. *Par certificat* — une proposition **égale** le plafond. Ce chemin ne
|
||
suppose rien.
|
||
2. *Par famille connue* — l'instance appartient à une famille dont la
|
||
construction parfaite est démontrée. **Les conditions portent sur l'instance
|
||
réduite**, pas sur l'instance complète, et les deux sens se produisent.
|
||
|
||
**Un réduit parfait ne prouve pas le plafond complet**, et c'est vérifié par
|
||
énumération : 16 mobiles, 4 tables de 5 dont une place ancrée chacune, 5 tours.
|
||
Le réduit — 16 points, blocs de 4 — est un plan affine d'ordre 4, chaque mobile
|
||
rencontre bien les 15 autres. Son plafond vaut pourtant 19 et n'est pas
|
||
atteint : aucune affectation des blocs aux tables ne donne plus de **2** tables
|
||
distinctes au mobile le plus mal servi, soit 17 sur 19. La construction porte
|
||
sur le réduit ; le plafond sur le complet ; **l'affectation des blocs aux tables
|
||
est un troisième objet**, et c'est lui qui décide.
|
||
|
||
**Le certificat se lit contre le plafond a priori**, donc sur `manque + écart
|
||
d'itinéraire`, jamais sur le seul manque : une proposition qui range tout le
|
||
monde sur des itinéraires bas atteint son plafond réalisé partout et n'a rien
|
||
prouvé. Lire le certificat sur le manque seul ferait annoncer « minimum atteint »
|
||
à la proposition qui a le plus mal routé les gens. Conséquence directe, que le
|
||
guide d'usage écrit : **la grande démonstration ne décerne jamais ce
|
||
certificat**, son écart d'itinéraire maximal étant plancher à 1 (§ 12.10.4).
|
||
|
||
Quand aucun chemin ne conclut, le logiciel affiche l'**écart au plafond a
|
||
priori** et écrit **« atteignabilité inconnue »**, jamais « peut mieux faire » :
|
||
l'un invite à relancer, l'autre dit que relancer ne prouvera rien.
|
||
|
||
**La borne de diversité.** Soit G le nombre d'appartenances déclarées et g_p
|
||
l'effectif de celle de p :
|
||
|
||
```
|
||
A_max(p) = min( Φ(p) , G , (N − g_p) + 1 )
|
||
```
|
||
|
||
où Φ(p) est le plafond recalculé en ne comptant comme occupants que les
|
||
affiliés. Le « + 1 » compte sa propre appartenance, qu'un collègue rencontré
|
||
suffit à apporter : sans lui, un événement où tout le monde partage une
|
||
appartenance annoncerait A_max = 0 alors que chacun en rencontre une.
|
||
|
||
### 5.6 Le diagnostic, avant la recherche
|
||
|
||
Certaines configurations rendent un conflit **inévitable**, quel que soit
|
||
l'algorithme. Le logiciel les détecte **avant** de chercher et énonce ce qui est
|
||
inévitable, **combien**, et ce qui le rendrait évitable :
|
||
|
||
- plus de personnes d'une appartenance que de tables → « au moins 8 collisions
|
||
cumulées sur les 4 tours, portées par au moins 2 paires distinctes ». Le
|
||
plancher se démontre **en occurrences** ; le chiffre nomme donc son unité, et
|
||
le compte de paires distinctes l'accompagne (§ 5.4) ;
|
||
- **le plancher de l'excédent**, quand un groupe de g personnes subit un plancher
|
||
de C collisions cumulées et que `C > C(g, 2)` : les retrouvailles sont alors
|
||
forcées, et l'excédent ne descend pas sous `C − C(g, 2)`. Le diagnostic l'écrit
|
||
**quand il est strictement positif**, et se tait sinon — annoncer un plancher
|
||
nul laisserait croire qu'un plancher a été calculé là où il n'y en a pas ;
|
||
- redondance minimale `r_min(p) = max(0, |F(p)| − G)` → « au moins 6
|
||
appartenances recroisées par personne ; avec 3 tours au lieu de 4, aucune ne
|
||
le serait » ;
|
||
- **tous les animateurs de même appartenance** → chaque mobile accumule au
|
||
moins min(R, T) − 1 redondances qu'aucune recherche ne réduit. Remède : les
|
||
laisser sans appartenance déclarée, ou leur en donner des distinctes ;
|
||
- **taux d'ancrage par table** : une table dont tous les sièges sont ancrés ne
|
||
tourne plus ; une table à moitié ancrée réduit d'autant le brassage de tout
|
||
l'événement ;
|
||
- **le plancher de l'écart d'itinéraire maximal**, quand la configuration en
|
||
impose un (§ 12.10.4). Le diagnostic ne l'écrit que là où il sait le prouver,
|
||
et se tait ailleurs — **jamais 0 par défaut**. Sans lui, le troisième chiffre
|
||
de la page de qualité envoie relancer indéfiniment vers un zéro que la
|
||
configuration interdit ;
|
||
- **le manque de places**, `Σ (c_t − k_t) < n`. Ce n'est pas un refus de geste
|
||
mais une **propriété de la configuration** (§ 5.9) : le diagnostic la nomme
|
||
avec le nombre de places manquantes, et **la génération la refuse**.
|
||
|
||
Un opérateur à qui l'on dit « c'est impossible, voici pourquoi, voici le
|
||
remède » cesse de relancer en espérant mieux.
|
||
|
||
### 5.7 Plusieurs propositions, pas une
|
||
|
||
Une génération produit **plusieurs placements distincts et comparables**, en
|
||
nombre réglable. Les générations **s'accumulent** : relancer avec d'autres
|
||
réglages ajoute des candidats plutôt que d'effacer les précédents, et un
|
||
classement unique les ordonne tous. Une commande efface la liste en épargnant
|
||
celui qui est retenu.
|
||
|
||
**La commande prend une graine entière et un compte d'arrêt**, et une longueur
|
||
d'historique d'acceptation dont l'algorithme fixe la valeur par défaut. Chaque
|
||
proposition conserve sa graine dérivée, ce compte et cette longueur (§ 8.9).
|
||
C'est le support de « à graine et entrée égales, placement identique » ; sans
|
||
eux, une proposition retenue cesse d'être régénérable.
|
||
|
||
**L'identifiant entier d'une proposition vient de sa place dans la suite de
|
||
graines dérivées, jamais de son ordre d'achèvement.** Quand plusieurs
|
||
propositions se calculent en parallèle, leur ordre d'arrivée varie : un
|
||
identifiant attribué à l'achèvement ferait départager deux propositions à
|
||
égalité par la vitesse de la machine, et deux exécutions les classeraient dans
|
||
deux ordres.
|
||
|
||
**La recherche descend un scalaire ; le classement affiché est un ordre
|
||
explicite**, écrit dans l'interface. Confondre les deux ferait dépendre l'ordre
|
||
de poids internes que l'opérateur ne voit pas.
|
||
|
||
Ordre par défaut : **écart au plafond a priori** le plus faible (§ 5.5), puis
|
||
**excédent de collisions**, puis **collisions cumulées**, puis rencontres
|
||
répétées, puis **redondance d'appartenance comme départage**.
|
||
|
||
Le premier rang se justifie par l'asymétrie des gains — rencontrer une personne
|
||
de plus est un gain certain, rencontrer une appartenance de plus est un
|
||
raffinement. Mettre la diversité au-dessus ferait **refuser un contact neuf**
|
||
parce que l'appartenance du voisin est déjà croisée.
|
||
|
||
**Les deux rangs de collision sont dans cet ordre, et l'inverse serait faux.**
|
||
Le § 5.4 le démontre en trois plans : classer d'abord sur les collisions
|
||
cumulées — comme classer d'abord sur les paires distinctes — met en tête le plan
|
||
qui fait passer toute la soirée aux deux mêmes collègues, parce qu'il en porte le
|
||
moins. L'excédent d'abord, le volume ensuite : un plan qui répartit six collisions
|
||
sur six paires passe devant un plan qui en concentre quatre sur une seule.
|
||
|
||
**Les paires distinctes ne sont pas une clé de tri** ; elles restent une colonne
|
||
affichée, et se retrouvent de toute façon par soustraction. Trois clés pour deux
|
||
quantités indépendantes feraient un départage qui ne départage rien.
|
||
|
||
Quand le plafond a priori par personne n'est pas disponible (§ 17, point 4), le premier
|
||
critère est **sauté** et la page nomme celui qu'elle applique à sa place : un
|
||
classement qui se tait sur le critère qu'il n'a pas pu appliquer se lit comme un
|
||
classement complet.
|
||
|
||
Un départage ne se déclenchant presque jamais, les chiffres de diversité sont
|
||
affichés **en colonne pour chaque proposition**, et non pour la seule gagnante.
|
||
|
||
### 5.8 Le placement à la main
|
||
|
||
L'opérateur peut asseoir des gens **avant toute génération** : ce sont des
|
||
réservations. La génération les honore et place les autres autour. Une commande
|
||
transforme un placement partiel en placement complet, le générateur comblant les
|
||
vides.
|
||
|
||
Une fois un placement retenu, le même geste déplace une personne réelle. **Le
|
||
geste est identique ; ce qui change est l'état des données**, et l'interface le
|
||
dit, pour que l'opérateur sache lequel des deux il fait.
|
||
|
||
**Il n'existe aucune commande qui répartit les animateurs sur les tables.**
|
||
L'opérateur désigne lui-même quelle personne anime quelle table (§ 2.1,
|
||
étape 6). Une commande qui attribuerait un animateur par numéro de table le
|
||
ferait sous une **seule** entrée d'historique : revenir sur elle, parce que douze
|
||
attributions sur trente-trois sont fausses, emporterait aussi les vingt-et-une
|
||
qui étaient bonnes — le § 8.3 restitue un état, il ne défait pas un geste.
|
||
|
||
Ce qui la remplace : le logiciel **montre le travail qui reste**, par deux listes
|
||
cliquables qui mènent à la place ou à la personne — la liste des **titres de
|
||
place non pourvus** (§ 4.1) et la liste des participants portant un **titre
|
||
pressenti** (§ 4) qui ne sont encore réservés nulle part.
|
||
|
||
### 5.9 Ce que le logiciel refuse, au moment du geste
|
||
|
||
Ces contrôles s'exécutent **à la pose**, jamais à la génération : un échec de
|
||
génération que rien n'explique coûte une relance inutile et la confiance mise
|
||
dans l'outil.
|
||
|
||
- **k_t > c_t** — plus d'ancrés que de sièges : refusé.
|
||
- **k_t = c_t** — table entièrement gelée : accepté, **signalé**, parce que
|
||
c'est rarement l'intention.
|
||
- **Σ (c_t − k_t) < n** — il manque des places à chaque tour. **Celui-ci n'est
|
||
pas un refus de geste.** L'écart `Σ (c_t − k_t) − n` est **invariant sous
|
||
toute réservation** : une portée « tous les tours » retire la personne des
|
||
mobiles et son siège de la capacité libre en parts égales ; une portée « tour
|
||
désigné » rend la personne partiellement fixée, qui compte parmi les mobiles
|
||
(§ 5.4) et ne change ni `n` ni `k_t` ; déplacer une réservation décrémente un
|
||
`k_t` et en incrémente un autre. Aucun geste de réservation ne peut donc le
|
||
faire franchir. C'est une **propriété de la configuration** : le § 5.6
|
||
l'évalue au diagnostic et **la génération la refuse**, en nommant le nombre de
|
||
places manquantes. Les gestes qui la font franchir sont ceux qui augmentent
|
||
`n` ou diminuent `Σ c_t` — un import, une saisie, la réintégration d'un exclu
|
||
(§ 4.4), une baisse de capacité (§ 6.2), la suppression d'une table — et
|
||
**aucun n'est refusé pour cette raison** : refuser un import parce que la
|
||
salle est devenue trop petite empêcherait d'enregistrer qui s'est présenté, et
|
||
le § 9 pose déjà que la dérive avertit sans bloquer. Chacun **avertit**, avec
|
||
le nombre.
|
||
- Réserver plus de places qu'une table n'en a ; un siège inexistant ; la même
|
||
personne à deux places sur un même tour ; une personne exclue.
|
||
- Déposer quelqu'un sur une place réservée à un autre, ou l'échanger avec elle.
|
||
**Déplacer la réservation** est une commande séparée et explicite.
|
||
|
||
Un refus **nomme sa cause et propose le geste qui le lève**, et se manifeste
|
||
**sur la place concernée**, pas dans une boîte de dialogue. Seul un geste qui
|
||
détruit de la saisie ouvre une fenêtre à confirmer : une suite de modales
|
||
apprend à cliquer sans lire, et le premier refus qui comptait passe inaperçu.
|
||
|
||
### 5.10 Le coût d'évaluation
|
||
|
||
A(p) et r(p) demandent de parcourir les personnes rencontrées par chacun.
|
||
Recalculés intégralement à chaque candidat d'une recherche locale, ils dominent
|
||
le temps de génération. Le moteur les met à jour **de façon incrémentale**, sur
|
||
les seules tables modifiées. C'est une exigence de faisabilité : sans elle,
|
||
l'objectif existe dans l'interface et ne converge pas dans le temps que
|
||
l'opérateur accorde.
|
||
|
||
**La recherche s'arrête sur un compte, jamais sur une durée** — un nombre
|
||
d'itérations, ou un nombre de mouvements consécutifs sans amélioration — fixé
|
||
dans les réglages de génération et enregistré avec la proposition, à côté de sa
|
||
graine (§ 8.9). Le temps que l'opérateur accorde s'achète par la mise à jour
|
||
incrémentale, pas par un chronomètre. Une recherche bornée par une durée rendrait
|
||
le placement dépendant de la vitesse de la machine : à graine et entrée égales,
|
||
deux exécutions donneraient deux plans, et toute la reproductibilité des captures
|
||
d'écran (§ 19.4) tomberait avec. L'interdit de `performance.now` dans le moteur
|
||
(§ 14.7) n'est donc pas une règle d'hygiène : c'est elle qui rend vraie la phrase
|
||
« à graine égale, placement identique ».
|
||
|
||
**Le compte est un réglage, et son plafond d'usage est de trente minutes.** Il
|
||
est exposé dans les réglages de génération, avec son unité ; le logiciel affiche
|
||
à côté la **durée réellement observée du dernier calcul sur cette machine**, qui
|
||
est la seule façon d'étalonner un compte. Le réglage livré par défaut est celui
|
||
qui, sur la machine de référence, reste bien en deçà de ce plafond.
|
||
|
||
**Une génération longue s'annonce avant de partir.** À partir de la dernière
|
||
durée mesurée, le logiciel énonce l'ordre de grandeur attendu — « environ vingt
|
||
minutes à ce réglage » — et demande. Une recherche d'une demi-heure lancée par
|
||
mégarde est une demi-heure de soirée perdue, et aucun réglage par défaut ne
|
||
protège de cela.
|
||
|
||
**Pendant la recherche, l'application reste vivante.** Le calcul ne tient pas le
|
||
fil d'exécution de l'interface : le plan reste lisible, la page de qualité des
|
||
propositions déjà obtenues reste ouverte, et l'avancement est visible. Une
|
||
demi-heure de fenêtre figée se lit comme un plantage, et l'opérateur tue le
|
||
logiciel — ce qui perd exactement ce que la recherche avait trouvé. Le compteur
|
||
d'inactivité du § 8.4 est suspendu tant qu'un calcul tourne, donc le retour
|
||
automatique en lecture ne tombe jamais au milieu d'une génération.
|
||
|
||
**Une commande annule une génération en cours, et elle écarte le résultat
|
||
partiel.** L'annulation est immédiate — elle n'attend pas la fin d'une itération
|
||
longue. Une proposition issue d'une recherche tronquée porterait la même graine
|
||
et le même compte qu'une proposition complète, et ne s'en distinguerait dans aucun
|
||
fichier.
|
||
|
||
**Le recalcul complet est la mesure**, celui dont sortent les chiffres du § 5.4
|
||
et de la page de qualité ; **l'incrémental n'existe que dans la boucle de
|
||
recherche**, où il sert de score et ne s'affiche jamais. C'est l'exception nommée
|
||
au § 13.2. Sans ce partage, la recherche descend un score dérivé et converge vers
|
||
un plan que les indicateurs affichés classeront **sous** un plan qu'elle a
|
||
rejeté, et l'opérateur verra le classement contredire la proposition.
|
||
|
||
Le logiciel **éprouve l'incrémental contre le recalcul complet** sur chaque
|
||
configuration de démonstration, **à 1, 10, 100, 1 000 et 10 000 mouvements** —
|
||
non au seul terme : comparer à la fin laisse passer une dérive qui se compense,
|
||
et ne dit pas à quel ordre de grandeur le défaut affleure, qui est précisément le
|
||
chiffre que l'épreuve doit produire (§ 14.10).
|
||
|
||
---
|
||
|
||
## 6. Les sièges d'une table
|
||
|
||
### 6.1 Un défaut d'événement, des surcharges par table
|
||
|
||
L'événement porte un **nombre de sièges par défaut**. Toute table créée **suit**
|
||
ce défaut ; elle n'en garde pas une copie. Une table peut recevoir une
|
||
**surcharge** et cesse alors de le suivre.
|
||
|
||
| état d'une table | ce que cela veut dire |
|
||
|---|---|
|
||
| **suit le défaut** | sa capacité vaut le défaut et change avec lui |
|
||
| **surchargée** | sa capacité est fixée à la main, le défaut ne l'atteint plus |
|
||
|
||
*Réaligner* n'est pas un troisième état : c'est la commande qui retire une
|
||
surcharge. **L'interface montre l'état, pas seulement la valeur** — une table à
|
||
8 qui suit un défaut de 8 et une table surchargée à 8 affichent le même nombre
|
||
et réagissent différemment au prochain changement.
|
||
|
||
**Changer le défaut n'écrase aucune surcharge.** Une saisie manuelle table par
|
||
table disparaîtrait sinon sans trace, et l'écart ne se découvrirait qu'à
|
||
l'impression.
|
||
|
||
Le logiciel **énonce la conséquence chiffrée avant de l'appliquer** :
|
||
|
||
> « 9 tables suivent le défaut et passent de 8 à 10 places ; 4 tables
|
||
> surchargées (7, 7, 6, 12) ne changent pas. Total : 104 → 122 sièges pour 118
|
||
> participants. »
|
||
|
||
Deux boutons : appliquer, ou appliquer **et réaligner les 4 surchargées** — le
|
||
second geste est séparé parce qu'il détruit de la saisie.
|
||
|
||
**Un changement de défaut est une seule entrée d'historique**, quel que soit le
|
||
nombre de tables touchées. **Baisser le défaut** subit les refus du § 6.2, et
|
||
le logiciel les évalue **toutes avant d'en appliquer aucune** : si une seule
|
||
table refuse, l'opération entière est refusée, nommément, et rien ne change.
|
||
|
||
### 6.2 Ce que le logiciel refuse
|
||
|
||
- **Moins de 2 sièges.** Une table à 1 place est un participant qui ne
|
||
rencontre personne de la soirée — c'est ce que le logiciel existe pour éviter.
|
||
- Une capacité inférieure au nombre de réservations portées par la table.
|
||
- Une réduction qui retire un siège portant une réservation ou un titre — les
|
||
sièges disparaissent par numéro décroissant ; le refus les liste.
|
||
- Une capacité non entière, négative, ou saisie en texte.
|
||
|
||
Deux cas **avertissent sans refuser** : réduire sous le nombre de personnes
|
||
assises (les surnuméraires vont dans la réserve visible, nommément listées,
|
||
jamais effacées en silence) ; et dépasser le nombre de sièges au-delà duquel le
|
||
dessin cesse d'être lisible — seuil **mesuré** au sens du § 14.1.
|
||
|
||
---
|
||
|
||
## 7. Le plan de salle
|
||
|
||
C'est la surface de travail, du début à la fin. Il est dessiné en **SVG** et
|
||
l'opérateur agit directement dessus.
|
||
|
||
### 7.1 Les gestes
|
||
|
||
| geste | effet |
|
||
|---|---|
|
||
| cliquer une chaise | sélectionne la place — l'assigner, la réserver, lui donner un titre |
|
||
| glisser une personne sur un siège libre | la déplace |
|
||
| glisser une personne sur une autre | les échange |
|
||
| glisser une personne hors des tables | la retire du tour ; elle attend dans la **réserve** visible |
|
||
| glisser une table | change sa position |
|
||
| tirer la poignée d'une table | **change son nombre de places** |
|
||
| survoler une table en conflit | affiche le détail des règles violées |
|
||
| zoomer, déplacer la vue | change le cadrage |
|
||
|
||
Un **menu par personne** offre les mêmes actions sans glisser, pour le tactile
|
||
et le clavier.
|
||
|
||
**La poignée pilote le nombre de places, jamais une taille libre.** Chaque table
|
||
est dessinée à l'échelle de son nombre de sièges ; une taille libre permettrait
|
||
un dessin qui ment sur la capacité, et ce mensonge n'apparaîtrait sur aucun
|
||
indicateur. En tirant, l'opérateur fait défiler des nombres entiers ; les
|
||
chaises apparaissent et disparaissent sous le pointeur.
|
||
|
||
**La poignée refuse de descendre sous le nombre de sièges occupés, tous tours
|
||
confondus** — sinon un geste de géométrie désassoit deux personnes au troisième
|
||
tour, que l'opérateur ne regarde pas. Le logiciel nomme le nombre bloquant et le
|
||
tour concerné.
|
||
|
||
**La rotation d'une table carrée n'existe pas dans la première itération** :
|
||
elle introduirait une orientation dans la numérotation des sièges et dans la
|
||
position de la liste de noms. Le logiciel n'offre aucune poignée de rotation,
|
||
plutôt qu'une poignée qui dérange la numérotation.
|
||
|
||
### 7.2 Les coordonnées
|
||
|
||
C'est l'endroit où un plan interactif se casse, silencieusement, et seulement
|
||
quand on a zoomé.
|
||
|
||
**Le `<svg>` racine ne porte pas de `viewBox`**, ni bordure ni remplissage. Son
|
||
unité est donc le pixel CSS. La formule qui répartit la position du pointeur
|
||
dans la largeur d'un `viewBox` est fausse dès que les rapports diffèrent : le
|
||
moteur de rendu centre le dessin, laisse des bandes vides, et le plan répond
|
||
juste au centre et faux sur les bords. Supprimer la cause vaut mieux que
|
||
corriger l'effet.
|
||
|
||
**L'état de vue est trois nombres** : `k`, l'échelle en **pixels par
|
||
centimètre**, et `tx`, `ty` en pixels. Le plan vit dans un unique groupe
|
||
`<g transform="translate(tx,ty) scale(k)">` : zoomer écrit **un attribut sur un
|
||
élément**.
|
||
|
||
**La conversion est une fonction pure**, donc éprouvée sous `node` :
|
||
|
||
```
|
||
dessin_depuis_ecran(vue, cadre, xEcran, yEcran) =
|
||
{ x: (xEcran − cadre.gauche − vue.tx) / vue.k,
|
||
y: (yEcran − cadre.haut − vue.ty) / vue.k }
|
||
```
|
||
|
||
Trois invariants la rendent vraie, et **chacun est une épreuve** : pas de
|
||
`viewBox` ; ni bordure ni remplissage sur le `<svg>` ; **aucun ancêtre portant
|
||
une transformation CSS** — elle déplacerait le rectangle sans déplacer les
|
||
unités, et le plan répondrait faux partout.
|
||
|
||
Une épreuve en navigateur confronte la fonction pure à
|
||
`getScreenCTM().inverse()`. Ce n'est pas une seconde implémentation : c'est un
|
||
**oracle** fourni par le moteur de rendu.
|
||
|
||
**Le zoom se fait sous le curseur**, sinon le contenu fuit vers un coin et
|
||
l'opérateur poursuit la table qu'il visait.
|
||
|
||
**Les bornes du zoom sont relatives, jamais absolues** : `k` reste entre
|
||
`0,5 × k_ajusté` et 10 px/cm. Une borne basse absolue est une erreur
|
||
démontrable — sur un portable de 1 366 × 700, un plan de 36 m s'ajuste à
|
||
`k ≈ 0,19 px/cm`, et un plancher fixé à 0,2 rendrait l'ajustement à la fenêtre
|
||
**impossible sur la configuration de démonstration elle-même**.
|
||
|
||
**Le plan vide** — aucune table — n'a pas de rectangle englobant : `k_ajusté`
|
||
vaut 1 px/cm, l'origine est centrée. Sans cette règle, l'ajustement divise par
|
||
zéro et rend `k = Infinity`, que le `transform` accepte sans rien dessiner et
|
||
sans rien signaler.
|
||
|
||
**Un glissement se calcule en écart, jamais en position absolue** — sinon la
|
||
table saute au premier mouvement pour mettre son origine sous le pointeur.
|
||
|
||
Le logiciel **ne lit ni `getBBox()` ni `getScreenCTM()` dans un
|
||
`pointermove`** : ces appels forcent un recalcul de mise en page au milieu de
|
||
la boucle d'images.
|
||
|
||
### 7.3 Le pointeur et le tactile
|
||
|
||
**La capture du pointeur est obligatoire**, et elle porte sur la **racine
|
||
`<svg>`**, pas sur la table. Sans capture, un mouvement rapide sort le curseur,
|
||
les événements vont à ce qui est dessous, le `pointerup` n'arrive jamais et la
|
||
table reste collée au curseur. Et si le composant remplaçait l'élément capturant
|
||
pendant le glissement, la capture serait perdue — raison pour laquelle le
|
||
logiciel écrit l'attribut `transform` **directement** pendant le glissement et
|
||
ne confie le résultat au modèle qu'au `pointerup`.
|
||
|
||
**Pendant une capture, `ev.target` désigne l'élément capturant, pas ce qui est
|
||
sous le pointeur.** Le logiciel ne cherche donc **pas la cible d'un dépôt dans
|
||
le DOM** : il convertit le point en coordonnées de dessin et demande au module
|
||
de géométrie quel siège le contient. Le dépôt devient une fonction pure,
|
||
éprouvable sous `node`, qui ne peut pas diverger du dessin puisque c'est elle
|
||
qui place les chaises.
|
||
|
||
**`pointercancel` est une annulation, pas une fin** : le logiciel remet la table
|
||
où elle était. La touche d'échappement et le passage en lecture font de même.
|
||
|
||
**Le tactile.** `touch-action: none` sur le plan, sans quoi le navigateur
|
||
interprète le glissement comme un défilement avant de livrer le moindre
|
||
`pointermove` — la panne tactile la plus fréquente, et la plus déroutante
|
||
puisqu'à la souris tout marche.
|
||
|
||
**La taille des cibles dépend du zoom, et la règle doit le dire.** Deux sièges
|
||
voisins d'une table de 8 sont à 60 cm ; une cible de 44 px exige
|
||
`k ≥ 0,73 px/cm`, le plancher de 24 px exige `k ≥ 0,40` — tandis que
|
||
l'ajustement d'une salle entière donne `k ≈ 0,2 à 0,45`. **Au cadrage le plus
|
||
utile, un siège n'est pas pointable au doigt.** La règle est donc : sous le zoom
|
||
où la cible tomberait en deçà de 24 px, **les sièges cessent d'être des cibles
|
||
et la table devient la cible**, le siège se choisissant dans son panneau. Le
|
||
logiciel le montre en cessant de dessiner les chaises individuellement, plutôt
|
||
qu'en laissant l'opérateur manquer sa cible sans comprendre.
|
||
|
||
**Il n'y a pas de survol au doigt** : le détail d'un conflit est accessible par
|
||
survol, par focus clavier **et par pression longue** — un seul composant
|
||
d'infobulle, trois déclencheurs.
|
||
|
||
**Un seul écouteur sur la racine**, la cible étant identifiée par
|
||
`closest('[data-siege]')`. Les mouvements sont **regroupés par image**.
|
||
|
||
**Le clavier n'enfile pas 264 arrêts de tabulation** : le plan est **un seul
|
||
arrêt**, les flèches passent d'une table à l'autre, l'entrée descend dans ses
|
||
sièges, l'échappement remonte.
|
||
|
||
### 7.4 Le texte
|
||
|
||
`<text>` n'a **aucun retour à la ligne** : chaque ligne d'une liste est un
|
||
`<tspan>` à décalage vertical explicite. La largeur se **mesure** par
|
||
`getComputedTextLength()` sur la police rendue ; la hauteur de ligne se mesure
|
||
sur un gabarit portant capitales accentuées et jambages — « Ôjgq », « Æ »,
|
||
« W » — jamais une chaîne moyenne, jamais déduite de la taille de police.
|
||
|
||
Le logiciel **n'emploie pas `textLength`** : cet attribut comprime les glyphes
|
||
et produit un nom déformé plutôt qu'un nom tronqué.
|
||
|
||
**La troncature se décide par document, pas par support — et sur le badge, par
|
||
champ :**
|
||
|
||
| document | règle |
|
||
|---|---|
|
||
| le plan de salle, à l'écran comme à l'impression | **abrège** avec une marque, la largeur d'un bloc de noms étant la même contrainte des deux côtés |
|
||
| la fiche à l'écran, la feuille par table, la liste d'accueil | **ne tronquent jamais** : elles replient |
|
||
| le **badge** (§ 11.5) | **par champ** : le nom ne s'abrège jamais — il réduit de corps par paliers, puis se replie — tandis que l'appartenance s'abrège sur une seule ligne |
|
||
|
||
Un nom coupé sur la feuille que tient le personnel de salle ne sert à personne ;
|
||
un nom de deux cents caractères déployé sur un plan de 33 tables détruit la
|
||
page.
|
||
|
||
### 7.5 Les tables en conflit
|
||
|
||
Une table dont le placement viole une règle active se signale par une classe
|
||
posée à partir des indicateurs du moteur — **le plan ne recalcule rien, il
|
||
lit**.
|
||
|
||
Le signal **ne repose pas sur la seule couleur** : contour épaissi, trame, et
|
||
une **pastille portant le nombre de violations**. Une part notable des
|
||
opérateurs ne distingue pas un rouge d'un vert.
|
||
|
||
**Quand toutes les tables sont en conflit, le plan cesse de signaler quoi que ce
|
||
soit** — cas que le diagnostic sait prédire. La pastille chiffrée garde sa
|
||
valeur puisqu'elle ordonne les tables entre elles, et le bandeau énonce le
|
||
diagnostic plutôt que de laisser compter des contours rouges.
|
||
|
||
### 7.6 Le vocabulaire visuel des places
|
||
|
||
**Aucun état ne se distingue par la seule couleur** : chacun porte une forme.
|
||
Les marques sont **dessinées en SVG**, jamais écrites avec des caractères
|
||
emoji — leur présence et leur rendu dépendent des polices installées, ce qu'une
|
||
livraison hors ligne ne contrôle pas.
|
||
|
||
| information | canal |
|
||
|---|---|
|
||
| réservée à une personne / titre seul / ni l'un ni l'autre | **cadenas fermé** / **cadenas ouvert** / aucune marque |
|
||
| pourvue / à pourvoir | remplissage **plein** / **hachuré** |
|
||
| portée « tous les tours » | **anneau** autour de la chaise |
|
||
|
||
**La chaise porte au plus deux marques ; la liste porte le texte.** Trois
|
||
glyphes empilés sur une chaise deviennent illisibles au zoom par défaut. La
|
||
liste numérotée énonce l'état en toutes lettres — « 1. (réservée, tous les
|
||
tours) Nom — *animateur* » — et c'est elle qui fait foi.
|
||
|
||
### 7.7 L'échelle, et ce que le logiciel refuse d'affirmer
|
||
|
||
**L'unité du modèle est le centimètre réel**, dès le premier fichier. Le
|
||
diamètre d'une table ronde se déduit de son nombre de places :
|
||
|
||
```
|
||
diamètre = max(70, places × 60 / π) en centimètres
|
||
```
|
||
|
||
Soit 115 cm pour 6 places, 153 pour 8, 191 pour 10. **Le plancher n'est pas
|
||
cosmétique** : en deçà de six convives ce n'est plus le périmètre qui borne la
|
||
table mais le plateau, et la formule seule dessinerait des guéridons. Les deux
|
||
valeurs sont des **réglages par défaut, non des normes vérifiées** ; un diamètre
|
||
saisi à la main l'emporte.
|
||
|
||
**Les étiquettes ne sont pas à l'échelle, le mobilier l'est.** Une liste de dix
|
||
noms couvre plusieurs mètres carrés fictifs. **Le plan à l'échelle du bâtiment
|
||
est le plan listes masquées**, et le logiciel le dit sur chaque impression
|
||
portant les listes.
|
||
|
||
**Tant qu'aucune dimension de salle n'est saisie, le plan est exact dans ses
|
||
tables et arbitraire dans son cadre.** Le logiciel porte alors, sur le plan et
|
||
sur **chaque impression**, la mention « disposition relative — l'encombrement
|
||
dans la salle n'est pas vérifié ». Une mention imprimée coûte une ligne ; un
|
||
montage à refaire coûte une soirée.
|
||
|
||
Une fois les dimensions saisies, **le logiciel mesure et rapporte, il ne déclare
|
||
pas conforme.** Le mot « conforme » est absent de l'interface tant que les
|
||
dégagements de référence ne sont pas établis.
|
||
|
||
**Toute impression à échelle réduite porte une échelle graphique**, non la
|
||
seule mention « 1:100 » : une impression ajustée à la page change le facteur sans
|
||
prévenir, et le chiffre devient un mensonge tandis que la règle graphique reste
|
||
juste. La **planche de badges**, qui est à l'échelle 1, porte la sienne sur sa
|
||
**feuille de contrôle** (§ 11.3) : la planche ne laisse pas un millimètre libre,
|
||
et une graduation tracée sur un axe de coupe laisserait des repères de 8 mm sur
|
||
le carton fini — elle ne disparaîtrait pas à la coupe.
|
||
|
||
**Pourquoi poser l'unité maintenant**, alors que la première itération n'exige
|
||
aucun contrôle d'encombrement : le coût est nul aujourd'hui, et plus tard c'est
|
||
une migration silencieuse — tous les fichiers enregistrés porteraient des
|
||
coordonnées dans une unité arbitraire, que rien ne distingue des nouvelles.
|
||
|
||
---
|
||
|
||
## 8. Les événements : fichiers, enregistrement, historique
|
||
|
||
### 8.1 Un événement, un fichier, une liste
|
||
|
||
Chaque événement vit dans **son propre fichier d'état**, complet et autonome. Ce
|
||
fichier seul suffit à rouvrir, imprimer et transmettre ; l'historique vit à côté
|
||
et n'est jamais nécessaire pour ouvrir.
|
||
|
||
**Ce que le fichier porte :**
|
||
|
||
| | |
|
||
|---|---|
|
||
| participants | avec identifiant entier, titre pressenti, notes |
|
||
| tables | capacité **et son état** : `sieges: null` (suit le défaut) ou un entier (surcharge) |
|
||
| le **nombre de sièges par défaut** de l'événement | |
|
||
| tours, réglages, réservations avec leur portée, **titres de place** | |
|
||
| placements engendrés, celui qui est retenu, état du plan | |
|
||
| **positions en centimètres**, et l'unité déclarée | |
|
||
| **filiation** : nom et identifiant de la source, instant d'origine | |
|
||
|
||
**Ce que le fichier ne porte pas : ni le mode, ni le cadrage.** Une liste de ce
|
||
qui est dedans, sans son complément, laisse l'implémenteur ajouter.
|
||
|
||
Le logiciel ouvre sur une **liste des événements**, avec pour chacun le nom, la
|
||
date, l'état, le nombre de participants, la dernière modification et le chemin.
|
||
**Un répertoire vide n'affiche pas une liste vide** : il présente la commande de
|
||
création et les démonstrations — une liste vide sans issue est le premier écran
|
||
que voit un opérateur qui lance l'exécutable.
|
||
|
||
Le logiciel n'ouvre **qu'un événement à la fois**, et ferme le précédent sans
|
||
question : il n'y a rien à enregistrer.
|
||
|
||
### 8.2 L'enregistrement et l'entrée d'historique sont le même événement
|
||
|
||
Toute modification passe par une **commande nommée**. Quand elle s'achève, le
|
||
logiciel **ajoute une entrée** au journal, **puis écrit le fichier d'état**.
|
||
Jamais l'un sans l'autre.
|
||
|
||
> **un geste achevé ⇔ une entrée d'historique ⇔ un état enregistré**
|
||
|
||
Il n'existe donc aucun état sur le disque que l'opérateur ne puisse nommer, et
|
||
aucun travail « non enregistré ».
|
||
|
||
**L'ordre n'est pas indifférent : le journal d'abord.** Une panne entre les deux
|
||
laisse un journal en avance d'une entrée, cas dont le logiciel se relève seul en
|
||
comparant les numéros de révision. L'ordre inverse perdrait le geste sans trace.
|
||
|
||
**Ce qui achève une commande est la fin du geste, jamais un minuteur** : le
|
||
relâchement du pointeur, la sortie d'un champ, la validation d'un formulaire, la
|
||
fin d'un calcul. Un intervalle de temps ferait tomber la frontière **au milieu
|
||
d'un geste** — une personne enlevée d'un siège et pas encore posée.
|
||
|
||
**Le fichier de réglages locaux du § 8.5 échappe nommément à cette règle** : il
|
||
ne porte aucune entrée d'historique, et le logiciel **diffère** son écriture
|
||
plutôt que de l'écrire à chaque image d'un zoom. Lui appliquer la règle écrirait
|
||
un fichier par image de molette.
|
||
|
||
**Le coût d'un enregistrement complet est un seuil mesuré**, non un ordre de
|
||
grandeur posé de tête : la réécriture intégrale du fichier d'état **à chaque
|
||
entrée** est la contrainte réelle qui gouverne le format (§ 8.9). La mesure se
|
||
prend sur la grande démonstration, sur la plateforme livrée, et le test échoue
|
||
au-delà (§ 14.1 ; série `node:long` du § 14.4).
|
||
|
||
**Est une entrée exactement ce qui change le contenu du fichier.** Changer de
|
||
tour affiché, zoomer, mettre en évidence, masquer les listes **n'en sont pas**.
|
||
Déplacer une table **en est une** : sa position est une donnée du plan.
|
||
|
||
**Chaque entrée porte un libellé en français, figé à l'écriture** — « Déplacé
|
||
Camille Roy de la table 4 à la table 7, tour 2 ». Jamais reconstruit ensuite :
|
||
les objets nommés peuvent avoir disparu, et un historique qui affiche « déplacé
|
||
‹participant supprimé› » ne sert plus à rien.
|
||
|
||
**L'ordre des entrées est celui des lignes, jamais celui des horodatages.** Une
|
||
horloge reculée rendrait le tri non monotone.
|
||
|
||
### 8.3 Le retour arrière : on revient à un instant
|
||
|
||
**Le logiciel restitue un état, il ne défait pas un geste.** L'opérateur, lui,
|
||
lit une liste de gestes : chaque instant est présenté par le libellé qui l'a
|
||
produit.
|
||
|
||
Défaire en appliquant un inverse exigerait d'écrire et d'éprouver un inverse par
|
||
commande, et **un inverse faux corrompt le plan sans rien signaler**. Un état
|
||
restitué ne peut pas être faux : il a existé.
|
||
|
||
**Le journal ne se tronque jamais.** Revenir **ajoute une entrée** dont l'état
|
||
résultant est l'état visé. L'historique classique, qui efface la suite dès qu'on
|
||
modifie après avoir défait, perd exactement le travail qu'un opérateur pressé
|
||
vient de mettre de côté pour essayer autre chose.
|
||
|
||
**Le fil courant** se définit par un parcours arrière : le prédécesseur d'une
|
||
entrée ordinaire est celle qui la précède ; le prédécesseur d'une entrée de
|
||
retour est **l'entrée qu'elle vise**. *Défaire* vise le prédécesseur sur ce fil.
|
||
*Refaire* n'existe que tant que les dernières entrées sont des retours. La liste
|
||
**distingue le fil courant des fils abandonnés** : un opérateur qui croit
|
||
remonter son fil et atterrit sur un fil mort obtient un état légitime, jamais
|
||
celui qu'il visait.
|
||
|
||
**Les jalons.** L'opérateur peut **nommer l'instant courant**, et filtrer
|
||
l'historique sur les instants nommés. Nommer ne modifie pas le contenu : c'est
|
||
une **ligne de marque**, pas une entrée, sinon l'équivalence du § 8.2 serait
|
||
fausse. Le logiciel nomme automatiquement la création, chaque changement d'état,
|
||
et l'instant qui précède chaque génération.
|
||
|
||
### 8.4 Le mode lecture, par défaut
|
||
|
||
**Le logiciel ouvre tout événement en lecture.** Toujours, y compris celui qui
|
||
était en écriture à la fermeture précédente. Le mode n'est pas écrit dans le
|
||
fichier : c'est une posture de la séance, pas une propriété du plan.
|
||
|
||
**En lecture, aucun geste ne modifie quoi que ce soit**, et le fichier n'est pas
|
||
écrit. **Ouvrir un événement ne réécrit jamais son fichier d'état.**
|
||
|
||
**Ce qui reste vivant en lecture** : le sélecteur de tour, le zoom, le cadrage,
|
||
l'ajustement, le plein écran, la mise en évidence, l'affichage des listes, la
|
||
**page de qualité** — son export CSV des indicateurs et son impression
|
||
(§ 11.7) —, la comparaison des propositions, la lecture de l'historique,
|
||
l'impression, et la **production de la planche de badges**, qui n'écrit aucun
|
||
fichier d'événement (§ 11.6). C'est la condition pour que la protection
|
||
tienne : si naviguer exigeait de passer en écriture, l'opérateur y resterait en
|
||
permanence et le mode lecture ne protégerait plus rien.
|
||
|
||
**On en sort par un seul geste délibéré** — une commande « Modifier » à place
|
||
fixe — et le logiciel y retourne **seul après dix minutes d'inactivité**. Ce
|
||
retour ne coûte rien et ne perd rien, puisque tout geste achevé est déjà sur le
|
||
disque.
|
||
|
||
**Le compteur d'inactivité ne court pas pendant un geste** : il est suspendu
|
||
tant qu'un pointeur est enfoncé, qu'un champ contient une saisie non validée ou
|
||
qu'un calcul tourne. Le retour **automatique** ne peut donc jamais tomber
|
||
pendant un geste ; le retour **explicite**, lui, annule un glissement en cours
|
||
comme le ferait un `pointercancel`.
|
||
|
||
**Le mode est visible sur la surface de travail elle-même** — un cadre et un
|
||
libellé permanents autour du plan — et non dans un coin de barre d'outils :
|
||
l'opérateur regarde le plan.
|
||
|
||
S'y ajoute un **bandeau d'application présent sur tout écran**, qui porte la
|
||
version et la provenance de la construction (§ 18.6) et, **quand un plan est
|
||
ouvert**, le libellé de mode. Les deux ne font pas double emploi : le cadre
|
||
signale le mode là où la main agit ; le bandeau porte la version jusque sur la
|
||
liste des événements, qui est le premier écran d'un signalement sur deux.
|
||
|
||
**Le passage en écriture est une étape nommée du parcours** (§ 2.1, étape 1b),
|
||
et non un sous-entendu : huit des douze étapes sont modifiantes, et toutes sont
|
||
refusées tant qu'il n'a pas eu lieu.
|
||
|
||
**Fermer en mode écriture ne coûte rien**, et **le logiciel ne pose jamais la
|
||
question « enregistrer les modifications ? »** : un opérateur ne sait pas ce que
|
||
contient un tampon.
|
||
|
||
**Seconde protection, à l'intérieur du mode écriture** : un clic n'est pas un
|
||
glisser. Le déplacement ne commence qu'au-delà d'un seuil de mouvement
|
||
**mesuré** sur la tolérance réelle d'un pavé tactile, et s'abandonne par Échap.
|
||
|
||
### 8.5 Mode et état : deux axes, pas une énumération
|
||
|
||
Le mode **n'est pas un cinquième état**. L'état décrit **où en est le plan** et
|
||
voyage avec le fichier ; le mode décrit **ce que la main a le droit de faire** et
|
||
ne quitte pas la séance.
|
||
|
||
Il n'y a donc **jamais trois comportements à tenir, mais deux** — lecture et
|
||
écriture — dont l'un est atteint par deux chemins : le défaut d'ouverture, et
|
||
l'état bloqué qui le force en remplaçant « Modifier » par « Débloquer ».
|
||
|
||
**Bloqué et lecture se ressemblent et diffèrent en nature.** Bloqué est inscrit
|
||
dans le fichier, vaut pour toutes les séances, et ne se lève que par une
|
||
commande nommée : on bloque un plan pour le distribuer. Lecture est la posture
|
||
par défaut, à un clic de l'écriture. Les confondre donnerait l'un des deux
|
||
désastres : déverrouiller un plan distribué deviendrait aussi facile qu'un clic
|
||
involontaire, ou déplacer une personne dans un brouillon exigerait de
|
||
« débloquer ».
|
||
|
||
**Le cadrage se range avec le mode, hors du fichier** — dans un **fichier de
|
||
réglages locaux**, `reglages_locaux.json` à la racine du dossier de travail
|
||
(§ 8.6), distinct des fichiers d'événements. Écrire le zoom dans le fichier
|
||
d'état contredirait l'équivalence du § 8.2, et **zoomer sur un plan bloqué
|
||
écrirait dans le fichier d'un plan qui refuse toute modification**. Ce réglage
|
||
n'est ni transmis avec le fichier, ni restitué par un retour arrière.
|
||
|
||
Ce fichier porte **deux choses, et deux seulement** :
|
||
|
||
| réglage | portée |
|
||
|---|---|
|
||
| le **cadrage** | indexé par identifiant d'événement |
|
||
| le **format de papier** (§ 11.2) | global à la machine |
|
||
|
||
Le format de papier décrit le bac de l'imprimante, pas le plan : l'écrire dans le
|
||
fichier d'événement ferait voyager le bac d'un bureau avec le plan. Le logiciel
|
||
le demande à la première production d'une planche et ne le redemande plus — le
|
||
redemander chaque fois apprend à cliquer sans lire (§ 5.9). En mode portable
|
||
(§ 8.6), ce fichier suit la clé avec les événements.
|
||
|
||
Son écriture est **différée**, et elle échappe nommément à la règle « fin du
|
||
geste, jamais un minuteur » du § 8.2.
|
||
|
||
### 8.6 Les fichiers
|
||
|
||
**Emplacement.** Ces règles sont des propriétés de l'implémentation
|
||
**`electron`**, celle qui est livrée. Sous `web`, « à côté de l'exécutable »
|
||
ne désigne rien, et la plateforme l'annonce déjà au démarrage (§ 8.8).
|
||
|
||
Le logiciel détermine son **dossier de travail une fois par séance**, par une
|
||
règle écrite, et l'affiche :
|
||
|
||
1. Il calcule le dossier de l'exécutable **à partir du chemin que le lanceur
|
||
portable publie**, jamais à partir du chemin du processus en cours
|
||
d'exécution. Un exécutable portable est une archive auto-extractible : le
|
||
processus tourne depuis un dossier temporaire que le système efface, et un
|
||
`data/` créé là emporterait la soirée sans un message. **Que le lanceur
|
||
publie bien ce dossier, et sous quel nom, se vérifie dès la première
|
||
semaine**, au même titre que la construction depuis Linux (§ 16).
|
||
2. Si ce dossier se trouve sous un emplacement de **données applicatives par
|
||
utilisateur** — `AppData` et ce qu'il contient, un dossier temporaire —, la
|
||
règle **s'arrête ici** et le logiciel passe au point 5. Un installateur qui
|
||
rangerait l'exécutable sous `AppData` y ferait réussir l'épreuve d'écriture,
|
||
et le logiciel créerait son dossier de données exactement là où cette section
|
||
l'interdit. Le livrable est pour cette raison un **exécutable portable à
|
||
fichier unique, non un installateur** (§ 16).
|
||
3. Sinon, le logiciel **éprouve l'écriture** dans `data/` à côté de
|
||
l'exécutable, qu'il crée au besoin : il écrit un fichier témoin, le relit,
|
||
l'efface, et efface un témoin resté d'une séance précédente. Un contrôle de
|
||
droits ne répond pas à la question posée — les droits effectifs dépendent
|
||
d'héritages, d'appartenances de groupe, de stratégies d'antivirus, et un
|
||
support amovible en lecture seule présente un dossier d'apparence
|
||
inscriptible. Une écriture suivie d'une relecture répond.
|
||
4. L'épreuve réussit → `data/` est le dossier de travail. C'est le **mode
|
||
portable** : l'exécutable et les événements se déplacent ensemble, et la
|
||
sauvegarde est la copie d'un seul dossier.
|
||
5. L'épreuve échoue, ou le point 2 l'a interrompue → le dossier de travail est
|
||
un dossier nommé d'après le produit, dans les *Documents* de l'opérateur. Le
|
||
logiciel **dit pourquoi**, une fois, à la première ouverture, sans bloquer.
|
||
|
||
**Le dossier retenu est éprouvé à son tour, et c'est cette épreuve qui le
|
||
crée** : le dossier des *Documents* naît à la détermination, avant que la liste
|
||
des événements s'ouvre, et non à la première écriture. Aucun geste ne crée
|
||
ensuite le dossier où il écrit : un dossier de travail qui disparaît en cours de
|
||
séance ne renaît pas vide, en silence. Si l'épreuve échoue, le logiciel le dit
|
||
avant d'ouvrir la liste, puisqu'aucun geste ne s'y écrirait.
|
||
|
||
**Ce qui arrive ensuite est aussi réglé.**
|
||
|
||
- Si `data/` existe, **contient des événements** et cesse d'être inscriptible, le
|
||
logiciel **ne bascule pas en silence** : il nomme le dossier, dit ce qui
|
||
l'empêche, et demande. Basculer en silence ouvrirait sur une liste où le plan
|
||
de la veille a disparu, et l'opérateur conclurait à une perte.
|
||
- La même règle vaut **en cours de séance**, pas seulement au démarrage : une clé
|
||
se retire. Quand une écriture échoue, le logiciel nomme le fichier et le
|
||
dossier, et propose d'écrire ailleurs, dans un dossier qu'il crée au besoin
|
||
puisque l'opérateur l'a désigné. Il énonce alors que l'équivalence du
|
||
§ 8.2 est rompue **par le support**, au lieu de laisser croire que le dernier
|
||
geste est sur le disque.
|
||
- Si le dossier de travail est neuf et vide alors que l'autre emplacement
|
||
contient des événements, le logiciel le dit et propose de les importer (§ 8.7)
|
||
plutôt que d'ouvrir sur la commande de création (§ 8.1). L'import **copie** :
|
||
le logiciel annonce que les originaux restent où ils sont, sinon deux copies du
|
||
même événement dérivent sous le même identifiant interne.
|
||
- **Le mode portable affaiblit une garantie du § 8.8** sur un support amovible,
|
||
et le logiciel l'annonce.
|
||
|
||
**Dans les deux modes, le logiciel affiche le chemin du dossier de travail en
|
||
clair dans la liste des événements**, et une commande l'ouvre dans l'explorateur.
|
||
|
||
Ce que cette section défend n'est pas l'emplacement *Documents* : c'est que
|
||
l'opérateur puisse copier un événement sur une clé, le joindre à un courriel et
|
||
le restaurer depuis sa sauvegarde **sans le logiciel**. Cette propriété tient
|
||
dans les deux modes, parce qu'elle vient de la **forme** — des fichiers
|
||
ordinaires dans un dossier ordinaire, sans base de données ni format
|
||
propriétaire — et non du chemin.
|
||
|
||
Ce que le logiciel refuse, inchangé : **jamais de répertoire de données caché**,
|
||
jamais `AppData`, jamais un emplacement que l'opérateur ne peut pas nommer.
|
||
|
||
**Le profil de Chromium n'est pas un répertoire de données.** Chromium,
|
||
qu'emporte la coquille Electron, range par défaut son profil — cache,
|
||
préférences, stockage de la page — dans un dossier persistant des données
|
||
applicatives de l'utilisateur. Le logiciel n'y garde rien : la coquille pose ce
|
||
profil dans un dossier neuf du dossier temporaire du système, et l'efface à la
|
||
sortie ; chaque démarrage efface aussi ceux des séances terminées, que Chromium
|
||
a récrits après la sortie ou qu'une séance coupée a laissés. Un exécutable
|
||
portable ne laisse ainsi, hors du dossier de travail, rien qui survive à la
|
||
séance suivante.
|
||
|
||
**Deux fichiers par événement :**
|
||
|
||
| fichier | contenu | écriture |
|
||
|---|---|---|
|
||
| `<nom>.gtt.json` | l'état courant, complet | réécrit en entier à chaque entrée |
|
||
| `<nom>.gtt-journal.jsonl` | les entrées d'historique | **ajout en fin**, une ligne par entrée |
|
||
|
||
Les deux portent **l'identifiant interne de l'événement**, et c'est lui qui les
|
||
apparie — jamais leurs noms.
|
||
|
||
**Le nom du fichier dérive du nom de l'événement** — un dossier d'identifiants
|
||
opaques est inutilisable dans un explorateur. La dérivation retire les
|
||
caractères que Windows refuse, **refuse les noms réservés quelle que soit la
|
||
casse**, **compare les collisions sans égard à la casse** (le système de
|
||
livraison y est insensible, celui de développement non), **borne le chemin
|
||
complet sur le plus long chemin que l'événement écrit**, et **retombe sur un nom
|
||
générique** quand la dérivation ne laisse rien. Ce plus long chemin assemble les
|
||
plus longues parties qu'un fichier de l'événement peut porter : le dossier daté
|
||
de la corbeille (§ 8.7) au rang de collision le plus long — deux suppressions
|
||
dans la même seconde se départagent par `_2`, `_3`… jusqu'à `_99` —, le plus long
|
||
suffixe, celui de la génération de secours, `.gtt.json.precedent`, dix-neuf
|
||
caractères, et le suffixe `.ecriture` de l'écriture atomique (§ 8.8) :
|
||
`corbeille/AAAA-MM-JJ_HH-MM-SS_99/<nom>.gtt.json.precedent.ecriture`, soixante
|
||
et un caractères outre le nom. `.gtt-journal.jsonl` n'en compte que dix-huit, et
|
||
`.gtt.json` neuf : une borne posée sur l'état ou sur le journal laisserait
|
||
déborder tout le reste. **La dérivation reçoit la racine du dossier de travail
|
||
en paramètre**, celle-ci n'étant plus connue à l'écriture du code, et son test
|
||
l'exerce sur les deux issues. **L'autorité reste le nom inscrit dans le
|
||
fichier.**
|
||
|
||
**Pourquoi l'historique n'est pas dans le fichier d'état**, pour deux raisons
|
||
distinctes. *La taille* : un plan de 260 personnes sur 4 tours pèse de l'ordre
|
||
de 250 ko avec une vingtaine de propositions ; 500 entrées en instantanés
|
||
complets pèseraient **de l'ordre de 125 Mo** — d'où les **correctifs**, avec un
|
||
instantané tous les cinquante au plus, pour qu'aucune restitution ne demande
|
||
plus de cinquante applications. *Le couplage* : un journal illisible n'empêche
|
||
jamais d'ouvrir le plan ; transmettre un plan ne transmet pas quatre cents
|
||
gestes portant les noms des personnes retirées ; vider l'historique est le
|
||
remplacement d'un fichier.
|
||
|
||
> **Ces ordres de grandeur ne valent que sous la forme positionnelle du § 8.9.**
|
||
> Sous une forme nommée par place, le fichier d'état dépasse le mégaoctet — et ce
|
||
> n'est pas l'historique qui casse alors, c'est le § 8.2, qui réécrit le fichier
|
||
> **en entier** à chaque entrée, c'est-à-dire au relâchement de chaque pointeur.
|
||
|
||
**Une ligne illisible arrête la lecture** : le logiciel écarte cette entrée **et
|
||
tout ce qui suit**, et annonce le nombre. Reprendre après elle ferait suivre un
|
||
correctif à un instant qui n'est pas le sien.
|
||
|
||
**Taille bornée** : au moins les cinq cents dernières entrées, et au-delà
|
||
élagage sur **frontière d'instantané**. **Le plancher l'emporte sur le plafond**
|
||
— un historique qui affiche un instant qu'il ne sait plus reconstruire est pire
|
||
que pas d'historique.
|
||
|
||
### 8.7 Copier, renommer, importer, supprimer
|
||
|
||
**Copier** produit un nouvel événement avec une identité propre. Le logiciel
|
||
**demande le nom avant de créer** ; sans cela les noms s'empilent en « (copie)
|
||
(copie) ».
|
||
|
||
**L'historique ne se copie pas.** La copie démarre sur une entrée unique :
|
||
« Créée par copie de ‹nom›, à l'instant ‹libellé› ». Rejouer les entrées de
|
||
l'original promettrait un retour arrière qui ramènerait le contenu de
|
||
l'original — exactement ce dont l'opérateur voulait diverger. La **filiation**
|
||
est conservée comme un fait inscrit dans le fichier.
|
||
|
||
**La copie d'un plan bloqué n'est pas bloquée** : son état se déduit de son
|
||
contenu. Copier un plan bloqué est le geste même de « le plan distribué reste
|
||
figé, je travaille sur une variante ».
|
||
|
||
**Renommer** change les **deux** fichiers : le logiciel écrit d'abord la paire
|
||
complète sous les nouveaux noms, puis supprime l'ancienne — jamais l'inverse.
|
||
Une panne laisse alors deux paires, ce qui se voit et se répare, au lieu
|
||
d'aucune.
|
||
|
||
**Importer** un fichier venu d'ailleurs le **copie** dans le dossier de travail.
|
||
Travailler en place écrirait dans un dossier de téléchargements que l'opérateur
|
||
vide régulièrement.
|
||
|
||
**Supprimer** déplace tous les fichiers dans une `corbeille/` datée. Le logiciel
|
||
ne la vide jamais de lui-même et affiche ce qu'elle contient : un opérateur qui
|
||
croit avoir supprimé les coordonnées d'une personne les a en réalité déplacées.
|
||
|
||
### 8.8 Ce qui résiste à une panne
|
||
|
||
Ces garanties sont des propriétés de l'implémentation **`electron`**, celle qui
|
||
est livrée. L'implémentation `web`, de développement, n'offre ni renommage
|
||
atomique ni verrou, et **le logiciel l'annonce au démarrage sur cette
|
||
plateforme**.
|
||
|
||
- **Écriture atomique** : fichier temporaire dans le même répertoire, vidé sur
|
||
le disque, puis **renommé par-dessus**. Sous Windows ce renommage échoue tant
|
||
qu'un antivirus ou un agent de synchronisation tient la cible ouverte : le
|
||
logiciel réessaie, puis renonce en nommant le fichier, et **un renommage qui
|
||
échoue ne détruit jamais la cible**.
|
||
- **Une génération de secours** sous `.precedent`. Le renommage atomique protège
|
||
d'une panne ; il ne protège pas d'une écriture complète mais fautive.
|
||
- **À l'ouverture, le logiciel ne repart jamais de zéro en silence** : il nomme
|
||
le fichier illisible et propose la version précédente, puis le dernier instant
|
||
du journal. Créer un événement vide est la défaillance qui perd une soirée,
|
||
parce que l'enregistrement automatique la rend définitive en une seconde.
|
||
- **Version de format.** Un fichier **plus récent** s'ouvre en lecture seule
|
||
sans passage possible en écriture ; un fichier **plus ancien** se convertit au
|
||
premier passage en écriture, jamais à l'ouverture.
|
||
- **Une seule séance en écriture**, le verrou se prenant **à l'entrée en mode
|
||
écriture** et non à l'ouverture.
|
||
- **Contrôle de cohérence** : l'en-tête annonce ses comptes, sa révision, sa
|
||
**version de format** et `produit_version` (§ 18.6) ; l'analyse syntaxique
|
||
seule ne distingue pas un fichier complet d'un fichier plausible. Le contrôle
|
||
**s'étend aux placements** (§ 8.9) : longueurs déclarées contre listes
|
||
écrites, identifiants vérifiés en multiensemble par tour, réserve comprise.
|
||
- **Sérialisation canonique** : **ordre de clés fixé, et aucune valeur que le
|
||
sérialiseur dérive de l'horloge ou d'un tirage au moment d'écrire.** La règle
|
||
porte sur le **sérialiseur**, jamais sur la provenance de la donnée : une
|
||
valeur tirée ou horodatée **une fois**, puis devenue partie de l'état — graine
|
||
d'une proposition, `produit_version`, identifiant d'événement — est une donnée
|
||
ordinaire. Lue comme un interdit sur la provenance, cette règle interdirait
|
||
d'enregistrer la graine, et une proposition retenue cesserait d'être
|
||
régénérable. Lue comme un interdit sur le sérialiseur, elle garde tout son
|
||
effet : ce qu'elle empêche est l'horodatage glissé à chaque écriture, qui fait
|
||
enregistrer du bruit à chaque geste.
|
||
- **Le fichier se partage en en-tête et charge utile.** En-tête : comptes,
|
||
révision, version de format, `produit_version`. Charge utile : tout le reste.
|
||
La canonicité octet pour octet est exigée du **fichier entier**, pour un
|
||
binaire donné ; mais les deux comparaisons qui **traversent les
|
||
constructions** — l'empreinte des fichiers de démonstration (§ 15.5, point 5)
|
||
et l'aller-retour sur les fichiers livrés (§ 8.9) — portent sur la **charge
|
||
utile seule**. Sans cette restriction, `produit_version` fait échouer
|
||
l'empreinte à **chaque** livraison : le test exigerait alors de régénérer les
|
||
démonstrations à chaque incrément de version, c'est-à-dire exactement la dérive
|
||
que le § 15.5 existe pour empêcher, et le geste conscient qu'il réclame
|
||
deviendrait mécanique, donc aveugle.
|
||
- **Le journal note la construction qui change** : quand `produit_version`
|
||
diffère de celle de l'entrée précédente, l'entrée de journal la porte. Le coût
|
||
est d'un champ par remplacement d'exécutable ; sans lui, un fichier transmis
|
||
pour diagnostic annonce la construction qui a écrit **en dernier**, et rien ne
|
||
dit laquelle a produit l'état suspect.
|
||
- **Sur un support amovible, la garantie d'écriture atomique est annoncée comme
|
||
affaiblie** : le renommage par-dessus n'y a pas la même valeur que sur le
|
||
volume système, et le support peut disparaître entre l'écriture et le
|
||
renommage. Le logiciel l'annonce comme il annonce déjà les limites de la
|
||
plateforme `web`.
|
||
|
||
|
||
### 8.9 La forme des placements dans le fichier
|
||
|
||
**« En clair » veut dire non chiffré, pas lisible à l'œil.** Le fichier reste du
|
||
texte, ouvrable dans un éditeur, sans clé ni outil.
|
||
|
||
**Le logiciel ne comprime pas.** Trois raisons, dont une seule suffirait. Le
|
||
journal est en **ajout de ligne en fin** (§ 8.6) et une archive comprimée ne
|
||
s'étend pas ainsi. Un octet abîmé dans un flux comprimé emporte tout ce qui suit,
|
||
là où une forme texte permet d'écarter une proposition et d'ouvrir le reste. Et
|
||
les **correctifs** du § 8.6 reposent sur la localité : une personne déplacée
|
||
touche deux entrées de deux listes, tandis qu'une recompression réécrit le flux
|
||
entier et rend le correctif aussi gros que l'instantané.
|
||
|
||
> La compression ne détruirait pas la canonicité du § 8.8 — un compresseur à
|
||
> niveau fixé est déterministe. L'argument est ailleurs, et il est celui
|
||
> ci-dessus.
|
||
|
||
**La compacité ne s'applique qu'aux placements.** Le fichier se partage en deux
|
||
régions, et la frontière suit une seule question : *ce qui se perd ici se
|
||
retrouve-t-il ?*
|
||
|
||
| région | forme | pourquoi |
|
||
|---|---|---|
|
||
| participants, tables, tours, réservations, titres de place, filiation | **champs nommés**, un objet par chose | c'est de la saisie d'opérateur : perdue, elle est perdue, et elle doit se lire à l'œil quand un fichier s'abîme. Elle est petite — de l'ordre de 40 ko pour 260 personnes avec courriels |
|
||
| placements engendrés | **forme positionnelle** | c'est la partie volumineuse, et la seule qui se **recalcule** : une proposition perdue coûte une relance |
|
||
|
||
#### La forme positionnelle
|
||
|
||
Une proposition porte un **identifiant entier séquentiel**, et les propositions
|
||
sont rangées **en liste ordonnée** — jamais en objet indexé par une clé, dont
|
||
l'ordre n'est pas garanti par la forme. L'identifiant vient de la place dans la
|
||
suite de graines dérivées (§ 5.7), et ne dérive ni de l'horloge ni d'un tirage.
|
||
Il se compte à partir d'un compteur du fichier, `prochainsIds.proposition`, qui
|
||
dépasse tout identifiant de proposition jamais attribué, celui d'où vient le
|
||
placement retenu compris : une génération numérote ses propositions dans
|
||
l'ordre de ses graines à partir de lui, puis le porte au-delà de la dernière. Le
|
||
compteur ne recule jamais — ni quand une commande efface les propositions
|
||
(§ 5.7), ni quand une main l'abaisse dans le fichier, l'ouverture le relevant
|
||
au-delà de chaque identifiant qu'elle lit. Les générations s'accumulent ainsi
|
||
sans qu'un identifiant se répète ni revienne, et le retenu désigne toujours la
|
||
même proposition d'origine.
|
||
|
||
Chaque proposition déclare, **une fois** :
|
||
|
||
- la **liste ordonnée des identifiants de table** sur laquelle elle est bâtie ;
|
||
- la **liste des capacités** correspondantes ;
|
||
- le **nombre de tours** ;
|
||
- l'**ensemble des identifiants de participants** qu'elle place ;
|
||
- le drapeau `siegesAttribues` : ses **sièges sont-ils attribués** (§ 5.3),
|
||
l'ordre d'une liste de table étant alors celui des sièges ;
|
||
- sa **graine dérivée** (§ 5.7), le **compte d'arrêt** et la **longueur de
|
||
l'historique** d'acceptation de la recherche qui l'a produite : les trois
|
||
réglages qui la déterminent.
|
||
|
||
Ces trois champs sont le support de « à graine et entrée égales, placement
|
||
identique » (§ 5.10) : à configuration égale et pour une même version du moteur
|
||
(`produit_version`, § 8.8), la régénération d'une proposition à partir d'eux
|
||
seuls rend son plan, quel que soit son rang dans sa génération. Sans eux, cette
|
||
phrase n'existe que dans un scénario de pilotage, et une proposition retenue
|
||
cesse d'être régénérable. La longueur de l'historique a une valeur par défaut,
|
||
mais la proposition porte celle qui l'a produite : régénérer sous une valeur
|
||
supposée rendrait une autre proposition, sans le dire. Les trois champs ne
|
||
déterminent le plan que pour l'algorithme de recherche qui l'a produit, dont
|
||
l'ordre des tirages et les règles d'acceptation font partie du résultat : une
|
||
proposition produite par une autre version du moteur n'est pas garantie
|
||
régénérable.
|
||
|
||
Puis, **par tour** : la liste des tables, chacune étant la liste des
|
||
**identifiants entiers** de ses occupants **dans l'ordre des sièges** ; et la
|
||
**réserve**, la liste des identifiants assis nulle part à ce tour. Le numéro de
|
||
siège n'est pas stocké : il est la position dans la liste. Quand les sièges sont
|
||
attribués, une chaise vide avant le dernier occupant, siège 1 compris, s'écrit
|
||
`null` à sa position, et une liste ne finit jamais par `null` : retirer une
|
||
personne libère sa chaise, et aucun autre siège ne change — le placement se
|
||
prépare, il ne suit pas la salle.
|
||
Sans attribution, aucune liste ne porte `null`.
|
||
|
||
**Quand les sièges ne sont pas attribués** (§ 5.3), l'ordre à l'intérieur d'une
|
||
table ne porte aucune information : le logiciel l'écrit **trié par identifiant
|
||
croissant**. Sans cette règle, deux états identiques produisent deux fichiers
|
||
différents, et le § 8.8 est violé par la seule forme du stockage. **C'est le
|
||
drapeau de la proposition qui décide**, jamais le réglage courant de
|
||
l'événement : changer le réglage n'efface pas l'ordre des sièges d'une
|
||
proposition déjà produite. Le placement retenu a la même forme et son propre
|
||
drapeau, avec l'identifiant de la proposition dont il vient. L'ensemble des
|
||
participants et chaque réserve se trient par identifiant croissant dans tous
|
||
les cas : ce sont des ensembles.
|
||
|
||
```
|
||
# illustration, annotée ; le fichier livré est du JSON strict
|
||
propositions: [
|
||
{ id: 3,
|
||
graine: 48271, arret: 20000, historique: 1000,
|
||
tables: [7, 12, 2, ...], identifiants de table (§ 4)
|
||
capacites: [8, 8, 7, ...],
|
||
tours: 4,
|
||
participants: [1, 2, 3, ..., 260],
|
||
placement: [
|
||
{ sieges: [[12,45,7,101,3,88,56,9], [21,64,...], ...], tour 1
|
||
reserve: [] },
|
||
{ sieges: [[12,33,...], ...], tour 2
|
||
reserve: [177] },
|
||
...
|
||
] } ]
|
||
```
|
||
|
||
#### Ce que cela économise
|
||
|
||
Sur la grande démonstration — 260 participants, 4 tours, une vingtaine de
|
||
propositions (§ 15.1), soit 1 040 places par proposition :
|
||
|
||
| forme | par proposition | vingt propositions |
|
||
|---|---|---|
|
||
| un enregistrement nommé par place (`tour`, `table`, `siege`, `participant`) | de l'ordre de 50 ko | **de l'ordre de 1 Mo** |
|
||
| positionnelle, en-têtes compris | de l'ordre de 5 ko | **de l'ordre de 100 ko** |
|
||
|
||
Un facteur de l'ordre de **dix**. Trois conséquences sur des chiffres écrits
|
||
ailleurs :
|
||
|
||
1. **Les ordres de grandeur du § 8.6 présupposent cette forme.** Les « 250 ko »
|
||
d'un fichier d'état sont atteints par la forme positionnelle, et dépassés d'un
|
||
ordre de grandeur par la forme nommée, qui conduit au mégaoctet.
|
||
2. **Le coût d'un identifiant opaque est décuplé, non quadruplé** (§ 4) : sous la
|
||
forme positionnelle, un identifiant de trente-six caractères remplace quatre
|
||
octets par trente-neuf.
|
||
3. **La contrainte réelle est le § 8.2, pas l'historique.** Avec un instantané tous
|
||
les cinquante, cinq cents entrées coûtent une dizaine d'instantanés — gros, pas
|
||
rédhibitoire, même sous la forme nommée. Ce que la forme nommée casse est
|
||
ailleurs : le § 8.2 **réécrit le fichier d'état en entier à chaque entrée
|
||
d'historique**, c'est-à-dire au relâchement de chaque pointeur. Écrire, vider
|
||
sur le disque et renommer 140 ko est une chose ; faire de même avec plus d'un
|
||
mégaoctet, sur un support qu'un antivirus inspecte à chaque fichier temporaire
|
||
créé, en est une autre — et la première victime serait l'équivalence « un geste
|
||
achevé ⇔ un état enregistré », qu'on serait tenté de relâcher par un minuteur,
|
||
ce que le § 8.2 interdit nommément. **Le coût d'un enregistrement se mesure**
|
||
sur la grande démonstration, sur la plateforme livrée, et la mesure est écrite
|
||
en commentaire avec ce qu'elle mesure (§ 14.1). Un seuil posé de tête ici ne
|
||
vaudrait rien.
|
||
|
||
#### Ce que la compacité coûte, et ce qui le paie
|
||
|
||
**Une forme positionnelle ne se diagnostique pas à l'œil.** Un tableau de 260
|
||
entiers ne dit pas qui est assis où, et un décalage d'une position déplace tout le
|
||
monde d'un siège sans qu'aucune syntaxe ne soit fautive.
|
||
|
||
Quatre exigences rendent ce coût acceptable, et aucune n'est facultative.
|
||
|
||
1. **Les longueurs déclarées s'éprouvent contre les listes écrites.** Chaque liste
|
||
de tour a autant de listes de table que la proposition déclare de tables, et
|
||
chaque liste de table la longueur que la proposition déclare. Ce contrôle est
|
||
**interne** : il compare le fichier à lui-même. C'est le contrôle de cohérence
|
||
du § 8.8 étendu aux placements — l'analyse syntaxique seule ne distingue pas un
|
||
placement complet d'un placement plausible.
|
||
2. **Les identifiants se vérifient comme un multiensemble, réserve comprise.** À
|
||
chaque tour, chaque identifiant déclaré par la proposition apparaît **exactement
|
||
une fois** dans l'union des listes de table et de la réserve. L'égalité
|
||
d'ensembles ne suffirait pas : sans la réserve stockée et sans le « exactement
|
||
une fois », un identifiant perdu passerait pour quelqu'un qui attend hors des
|
||
tables, cas que le § 12.6 prévoit. Le multiensemble attrape le décalage de
|
||
flux — il duplique un identifiant et en perd un autre —, l'identifiant inconnu,
|
||
et la personne assise deux fois.
|
||
**Ce qu'il n'attrape pas, et le logiciel ne prétend pas le contraire** : une
|
||
permutation à l'intérieur d'une table, ou l'échange du contenu de deux tables de
|
||
même capacité, conservent le multiensemble et les longueurs. Rien dans le
|
||
fichier ne les distingue d'un placement légitime ; ils se voient sur le plan.
|
||
3. **Fautif et périmé sont deux choses, et le logiciel ne les confond pas.**
|
||
Fautif = le fichier se contredit lui-même : une longueur fausse, un identifiant
|
||
dupliqué, un identifiant absent de l'ensemble déclaré. Périmé = la proposition
|
||
est cohérente mais ne décrit plus la configuration courante : une table
|
||
supprimée, une capacité changée, une personne exclue depuis (§ 4.4), un tour
|
||
retiré.
|
||
Une proposition **fautive** est nommée, comptée, et l'événement s'ouvre sans
|
||
elle. Une proposition **périmée** n'est pas écartée : c'est la **dérive** du
|
||
§ 9, qui avertit et ne bloque pas, et le logiciel dit laquelle et pourquoi.
|
||
Écarter au chargement une proposition périmée détruirait le travail de
|
||
l'opérateur à la suite d'une exclusion, geste qui ne demandait rien de tel.
|
||
Le logiciel ne refuse jamais le fichier entier à cause d'une proposition : la
|
||
saisie, qui est la partie irremplaçable, est dans l'autre région et n'est pas
|
||
touchée.
|
||
> La règle du journal est **l'inverse**, et pour une raison qui n'existe pas
|
||
> ici : une ligne de journal illisible arrête la lecture et emporte tout ce qui
|
||
> suit (§ 8.6), parce qu'un correctif suivrait sinon un instant qui n'est pas le
|
||
> sien. Les propositions sont indépendantes les unes des autres ; chacune tombe
|
||
> seule.
|
||
Le placement **retenu** fait exception : s'il est fautif, le logiciel le dit en
|
||
première ligne et propose la génération de secours `.precedent` (§ 8.8), parce
|
||
que c'est le seul placement dont la perte se voit le soir même.
|
||
4. **Une commande rend n'importe quelle proposition sous forme nommée**, pour
|
||
lecture et pour signalement d'un défaut. L'outil de diagnostic n'a pas à être le
|
||
format de stockage — c'est en confondant les deux qu'on paie la lisibilité sur
|
||
chaque enregistrement, à chaque écriture.
|
||
|
||
#### Ce que les épreuves exigent
|
||
|
||
- **Aller-retour** : charger, enregistrer, recharger, comparer structure à
|
||
structure, **sur chacun des fichiers de démonstration livrés**. La comparaison
|
||
porte sur la **charge utile**, l'en-tête exclu (§ 8.8). Le balayage refuse de
|
||
passer s'il n'examine aucun fichier (§ 14.2). Sur la grande démonstration, cette
|
||
épreuve est dans la série `node:long` (§ 14.4).
|
||
- **Canonicité** : enregistrer deux fois le même état produit deux fichiers
|
||
**identiques octet pour octet**, y compris quand les sièges ne sont pas
|
||
attribués — c'est là que la règle du tri croissant s'éprouve.
|
||
- **Corruption délibérée** : le test abîme un fichier de quatre façons — une
|
||
longueur fausse, un identifiant dupliqué, un identifiant inconnu, un identifiant
|
||
manquant sans entrée de réserve — et exige que le logiciel nomme chacune et
|
||
écarte la seule proposition fautive. Un test qui ne charge que des fichiers bien
|
||
formés ne peut pas échouer et ne prouve rien (§ 14.2).
|
||
- **Péremption délibérée** : le test exclut une personne, supprime une table,
|
||
réduit une capacité, et exige que les propositions touchées soient **signalées
|
||
comme dérive et conservées**, jamais écartées. C'est le test qui empêche la
|
||
correction suivante de reclasser le périmé en fautif.
|
||
- **Coût d'enregistrement** : le test mesure la durée d'un enregistrement complet
|
||
sur la grande démonstration et échoue au-delà du seuil mesuré du § 8.2. Il écrit
|
||
sur un système de fichiers réel, donc il est dans `node:long`.
|
||
|
||
---
|
||
|
||
## 9. Les états d'un plan
|
||
|
||
| état | ce qu'il signifie |
|
||
|---|---|
|
||
| **Brouillon** | on prépare |
|
||
| **Proposé** | des placements existent, aucun n'est retenu |
|
||
| **Retenu** | un placement est en vigueur |
|
||
| **Bloqué** | rien ne peut plus changer |
|
||
|
||
**Brouillon, proposé et retenu n'interdisent rien.** Ils ordonnent le travail,
|
||
se posent en cliquant l'état, et passer de l'un à l'autre **ne détruit jamais**
|
||
de données.
|
||
|
||
**Bloqué refuse toute modification** — tours, options, tables, participants,
|
||
import, et jusqu'à la position d'une table à l'écran. La lecture et l'impression
|
||
restent ouvertes : on bloque un plan précisément pour le distribuer.
|
||
|
||
**La dérive avertit, elle ne bloque pas.** Si les participants ou le mobilier
|
||
changent après qu'un placement a été retenu, le logiciel le signale et laisse
|
||
l'opérateur décider : lui seul sait si la personne arrivée en retard doit être
|
||
placée.
|
||
|
||
**Une proposition rendue incohérente par un changement de configuration est une
|
||
dérive, jamais une corruption** (§ 8.9) : elle est conservée, signalée, et le
|
||
logiciel dit laquelle et pourquoi. L'écarter au chargement détruirait le travail
|
||
de l'opérateur à la suite d'une exclusion, geste qui ne demandait rien de tel.
|
||
|
||
---
|
||
|
||
## 10. La liste des participants
|
||
|
||
### 10.1 L'import CSV
|
||
|
||
**L'aperçu est obligatoire.** Il n'existe pas de bouton « importer
|
||
directement » : l'aperçu est la seule garde contre une détection d'encodage ou
|
||
de séparateur qui se trompe sans rien faire échouer, et une garde qu'on peut
|
||
sauter ne garde rien.
|
||
|
||
**Les colonnes s'associent par leur en-tête, jamais par leur position** — un
|
||
tableur où une colonne a été déplacée importerait sinon les noms dans les
|
||
appartenances sans rien signaler.
|
||
|
||
| en-tête | obligatoire | contenu |
|
||
|---|---|---|
|
||
| `nom` | **oui** | |
|
||
| `prenom` | non | |
|
||
| `appartenance` | non | synonymes : `organisation`, `entreprise`, `équipe` |
|
||
| `courriel` | non | |
|
||
| `titre_pressenti` | non | synonymes : `titre`, `rôle` — **informatif, ne pourvoit aucune place** |
|
||
| `exclu` | non | liste fermée ; vide vaut `non` |
|
||
| `notes` | non | texte libre, jamais interprété |
|
||
|
||
Les en-têtes se reconnaissent **sans tenir compte de la casse, des accents ni
|
||
des espaces**. **Deux colonnes reconnues sous le même nom** ne se départagent
|
||
pas toutes seules : le logiciel n'associe ni l'une ni l'autre et demande.
|
||
|
||
**La valeur de `exclu`** appartient à une liste fermée : `oui`, `o`, `vrai`,
|
||
`1` ou `x` pour une exclusion ; `non`, `n`, `faux`, `0` ou vide pour aucune ;
|
||
sans égard à la casse, aux accents ni aux espaces. Toute autre valeur **refuse
|
||
la ligne**. Traiter l'inconnu comme `non` placerait dans la salle une personne
|
||
qui s'est désistée, et la chaise vide ne se verrait que le soir même.
|
||
|
||
**L'encodage se tranche en quatre temps, dans cet ordre :** marque d'ordre
|
||
d'octets UTF-8 ; marque UTF-16 ; **un octet nul dans les quatre premiers
|
||
kibioctets sans marque** → refus global avec le remède nommé
|
||
(`UTF16_SANS_MARQUE`) ; sinon décodage UTF-8 strict, et repli sur windows-1252
|
||
en cas d'échec.
|
||
|
||
**Une marque déclare l'encodage, et rien ne la dément.** Des octets qui ne sont
|
||
pas l'UTF-8 ou l'UTF-16 qu'elle annonce — une séquence invalide, un nombre
|
||
impair d'octets, un substitut isolé — refusent le fichier (`UTF8_INVALIDE`,
|
||
`UTF16_INVALIDE`) : un repli sur windows-1252 changerait chaque accent de la
|
||
partie valide en caractères parasites, « Benoît » en « Benoît », et un décodage
|
||
indulgent sèmerait des caractères de remplacement dans les noms. Quel que soit
|
||
le temps qui a décodé, **un texte qui porte le caractère nul est refusé**
|
||
(`CARACTERE_NUL`) — un collage aussi, qui ne porte pas d'octets : le refus
|
||
rattrape un octet nul au-delà des quatre kibioctets, et un fichier que sa marque
|
||
fait lire sans erreur sous un autre encodage, la marque de l'UTF-32 LE
|
||
commençant par celle de l'UTF-16 LE. Chaque refus d'encodage nomme le même
|
||
remède : réenregistrer le fichier en UTF-8.
|
||
|
||
> L'étape 3 n'est pas une précaution de principe : **un texte UTF-16 sans
|
||
> marque se décode sans erreur dans l'un ou l'autre des temps suivants**. En
|
||
> ASCII, il est de l'UTF-8 valide, chaque octet nul s'y décodant comme le
|
||
> caractère nul ; accentué, il cesse de l'être et retombe sur windows-1252, qui
|
||
> accepte tout octet. Dans les deux cas, les noms arrivent entrelardés de
|
||
> caractères nuls, qui ne font échouer aucun calcul. Un ordre à trois temps
|
||
> produirait ce défaut muet, qui ne se découvre que sur le plan imprimé.
|
||
|
||
**Le séparateur** se choisit entre `;`, `,` et la tabulation, en **analysant**
|
||
les vingt premiers enregistrements : le candidat retenu termine sans guillemet
|
||
ouvert, donne le même nombre de champs partout, **et ce nombre dépasse 1**. À
|
||
égalité, celui qui donne **le plus d'en-têtes reconnus**. Quand aucun candidat
|
||
ne convient et que la première ligne entière est un en-tête reconnu, **le
|
||
fichier n'a qu'une colonne** — une liste de noms — et chaque ligne est un seul
|
||
champ : un nom qui porte une virgule reste entier. Sinon, le fichier est
|
||
refusé, et le refus nomme ce qui écarte le séparateur.
|
||
|
||
> Les deux dernières conditions ne sont pas des raffinements. Sur un fichier
|
||
> séparé par des virgules, `;` donne lui aussi un nombre de champs parfaitement
|
||
> constant : **un**. Une règle qui ne retiendrait que la constance importerait
|
||
> chaque ligne entière dans la colonne `nom`.
|
||
|
||
**Les appartenances se réconcilient sur la même forme normalisée que la clé de
|
||
doublon** — espaces réduits, casse et accents ignorés ; la première orthographe
|
||
rencontrée s'affiche. **Le rapport d'import nomme chaque groupe d'orthographes
|
||
fondues et son nombre de lignes** : une fusion silencieuse est aussi fausse
|
||
qu'une séparation silencieuse. Une appartenance vide vaut **absence**, jamais
|
||
une appartenance nommée « ».
|
||
|
||
> Sans cette réconciliation, deux orthographes d'une même organisation
|
||
> deviennent deux appartenances : « séparer les appartenances » cesse de séparer
|
||
> deux collègues, G est gonflé, et A_max avec lui. Le défaut est muet — aucun
|
||
> indicateur ne s'en aperçoit.
|
||
|
||
**La ligne invalide : importer le reste.** Le logiciel importe les lignes
|
||
valides et **réexporte les refusées** en un CSV reprenant les colonnes
|
||
d'origine, augmenté de `ligne` et `motif` — deux colonnes qu'aucun en-tête ne
|
||
reconnaît, donc le fichier corrigé se réimporte tel quel. `ligne` porte le
|
||
numéro de l'enregistrement, en-tête compris : celui qu'un tableur affiche, un
|
||
champ cité sur plusieurs lignes du texte n'y comptant qu'une fois. Sur 260
|
||
lignes, un refus global coûterait l'import entier pour une faute en ligne 213.
|
||
|
||
Hors de l'encodage et du séparateur, quatre cas restent des refus **globaux** :
|
||
aucun en-tête reconnaissable, aucune colonne associée à `nom`, zéro ligne
|
||
valide, et **un guillemet resté ouvert** jusqu'à la fin du texte
|
||
(`GUILLEMET_OUVERT`, avec l'enregistrement où il s'ouvre) — tout ce qui le suit
|
||
tiendrait dans un seul champ, et deux personnes se fondraient en une.
|
||
|
||
**L'import entier est une seule entrée d'historique.**
|
||
|
||
**Les doublons** se repèrent sur le triplet (nom, prénom, appartenance)
|
||
normalisé, et le logiciel **ne fusionne jamais tout seul** : deux personnes
|
||
portent le même nom, et la fusion silencieuse fait disparaître un convive qui se
|
||
présentera quand même.
|
||
|
||
**Mettre à jour n'efface pas** : un champ vide dans le fichier laisse la valeur
|
||
existante. Un fichier ne portant que les noms effacerait sinon tous les
|
||
courriels, d'un geste qui s'annonce comme une mise à jour.
|
||
|
||
**Remplacer annonce ce qu'il détruit**, réservations et titres compris :
|
||
« 260 personnes seront supprimées » laisse croire qu'on ne perd que des noms.
|
||
|
||
**Importer selon l'état** : sans avertissement en brouillon et proposé ; avec
|
||
l'avertissement de dérive en retenu ; **refusé** en bloqué, le message nommant
|
||
la commande qui déverrouille.
|
||
|
||
### 10.2 La saisie
|
||
|
||
Seul `nom` est obligatoire : refuser une personne faute de courriel pousse
|
||
l'opérateur à inventer une valeur, qui ne se distingue plus ensuite d'une vraie.
|
||
|
||
Trois mécanismes rendent tenable la saisie de 260 personnes :
|
||
|
||
1. **Le collage en bloc**, passé **au même analyseur** que l'import — un second
|
||
analyseur « simplifié » divergerait sur les accents et les guillemets, et les
|
||
tests des deux resteraient verts.
|
||
2. **L'appartenance collante** : le champ conserve sa dernière valeur. Saisir
|
||
les sept personnes d'une organisation, c'est taper sept noms.
|
||
3. **La grille éditable**, au clavier, avec tri et filtre. Corriger est ce que
|
||
l'opérateur fait le plus, la veille et sur place.
|
||
|
||
**La liste s'exporte en CSV dans la forme exacte qu'il importe.** L'aller-retour
|
||
est éprouvé par un test qui importe, exporte et compare champ par champ : il
|
||
fait du fichier une sauvegarde, et de l'export le gabarit que personne n'a
|
||
besoin de documenter.
|
||
|
||
### 10.3 Les fichiers d'exemple livrés
|
||
|
||
| fichier | emploi |
|
||
|---|---|
|
||
| `exemples/participants_demo_petite.csv` | la **liste des participants** de la petite démonstration |
|
||
| `exemples/participants_demo_grande.csv` | celle de la grande |
|
||
| `exemples/participants_cas_limites.csv` | éprouve l'analyseur ; n'alimente aucune démonstration |
|
||
| `exemples/participants_cas_limites_cp1252.csv` | même contenu, windows-1252, séparé par des virgules |
|
||
|
||
**Un CSV ne reproduit pas une configuration** : un import de participants ne
|
||
crée ni tables, ni tours, ni réservations, ni titres. Les deux premiers fichiers
|
||
sont donc **produits par l'export CSV** à partir des fichiers de démonstration
|
||
livrés, **jamais saisis à la main** — la dérive entre l'exemple et la
|
||
démonstration devient impossible plutôt que surveillée.
|
||
|
||
Le fichier de cas limites exerce, ligne par ligne : une colonne non reconnue,
|
||
une personne sans appartenance, une exclusion, un champ cité contenant le
|
||
séparateur, un champ cité sur deux lignes, un doublon, une personne sans prénom,
|
||
une **paire d'orthographes d'une même appartenance à fondre** (§ 10.1), et une
|
||
ligne refusée faute de nom. Il **alimente aussi deux captures d'écran** du guide
|
||
(§ 19.3) — l'aperçu d'import et son rapport —, qui sont les deux écrans où une
|
||
fusion d'orthographes et un refus de ligne se montrent ensemble.
|
||
|
||
Tous sont en **UTF-8 avec marque d'ordre d'octets, séparés par `;`** — la forme
|
||
qu'un tableur francophone sous Windows rouvre sans manipulation — sauf le
|
||
dernier. Noms, organisations et courriels sont **inventés** ; les courriels
|
||
emploient le domaine réservé `.test`.
|
||
|
||
---
|
||
|
||
## 11. Les sorties
|
||
|
||
### 11.1 Une seule sortie PDF : la planche de badges
|
||
|
||
Le logiciel produit **un seul document PDF** : la planche de badges à découper,
|
||
distribués à l'accueil et portés autour du cou. Toute autre sortie se lit à
|
||
l'écran et s'imprime par le navigateur ou par l'hôte `electron` (§ 11.7).
|
||
|
||
**Il le produit en mémoire, par une bibliothèque embarquée.** L'argument est
|
||
celui du § 14.4, et il est le seul qui tienne : une bibliothèque embarquée
|
||
s'exécute sous `node`, donc **dans le cycle « à chaque modification »**. Un test
|
||
de quelques secondes affirme que la boîte de page mesure 210 × 297 mm à moins de
|
||
0,1 mm près, que la planche porte quatre cellules de 105 × 148 mm, que les
|
||
filets de coupe tombent à 105, 148 et 296 mm, que l'ordre des badges est celui
|
||
qu'annonce l'imposition choisie, et que les caractères accentués se retrouvent
|
||
**dans la couche de texte** — pas dans une image.
|
||
|
||
Le chemin d'impression, lui, exige de démarrer l'application et un moteur de
|
||
rendu : il appartient au niveau « navigateur », éprouvé **avant chaque
|
||
livraison** et non à chaque modification. *Un contrôle qui ne tourne qu'avant une
|
||
livraison ne tourne pas*, et la justesse du badge est la seule justesse de sortie
|
||
qui soit **géométrique** : une liste mal paginée se réimprime, soixante badges
|
||
coupés trop court sont refaits à la main pendant que la file s'allonge.
|
||
|
||
> **Ce que l'hôte `electron` sait faire, et qui ne suffit pas.** L'hôte
|
||
> d'impression de la plateforme de livraison accepte un format de page, des
|
||
> marges et un facteur d'échelle fixés par le programme, sans boîte de dialogue :
|
||
> affirmer que « le logiciel ne lit pas les marges » serait faux sur la
|
||
> plateforme livrée. Ce qui manque n'est pas la maîtrise, c'est **l'épreuve** —
|
||
> elle passe par un rendu, donc par un moteur de navigateur que le cycle court
|
||
> n'a pas. La plateforme `web`, elle, n'offre que la boîte de dialogue du
|
||
> navigateur et son « ajuster à la page ».
|
||
|
||
**Ce que cette réduction retire du chemin critique.** La bibliothèque n'ingère
|
||
plus de SVG : ni `<use>`, ni `<symbol>`, ni `<tspan>` à décalage explicite, ni
|
||
plan vectoriel. Le seul point de l'architecture de sortie qui reposait sur une
|
||
supposition **disparaît**, et avec lui le test permanent qui l'éprouvait. Ce qui
|
||
reste exigé d'elle est du texte, des filets, des rectangles et une police
|
||
embarquée.
|
||
|
||
Conséquences à tenir :
|
||
|
||
- **La police est embarquée**, couvrant le latin étendu du français, ligatures
|
||
comprises.
|
||
- **Chaque police embarquée porte une table `ToUnicode`.** Sans elle, un
|
||
sous-ensemble de police encode ses glyphes dans un ordre interne : les accents
|
||
s'impriment correctement et l'extraction rend du charabia. Le test ne distingue
|
||
alors plus « é absent » de « é inextractible », il échoue sur un défaut qui
|
||
n'existe pas — ou pire, on le désarme.
|
||
- **Le badge se construit depuis le modèle, jamais depuis le DOM.**
|
||
- **Aucune mise à l'échelle n'est inscrite dans le document** : pas de
|
||
`/UserUnit`, pas de boîte de recadrage distincte de la boîte de média. Le test
|
||
le vérifie sur le fichier produit, et vérifie en outre la **position mesurée**
|
||
des filets de coupe : une transformation posée dans le flux de contenu ne se
|
||
voit sur aucune des deux boîtes.
|
||
- **Les mesures de texte ont une seule autorité : les métriques de la police
|
||
embarquée**, lues par la bibliothèque qui dessine. Le § 14.1 impose de mesurer
|
||
dans un navigateur ce qui dépend du rendu du navigateur ; le badge n'en dépend
|
||
pas, et le mesurer là introduirait la seconde arithmétique que le § 13.2
|
||
interdit.
|
||
|
||
### 11.2 La planche : deux impositions, A4 et Lettre
|
||
|
||
**Le logiciel sait produire sur les deux papiers, et aucun des deux n'est un cas
|
||
de repli.** Le badge est un A6 exact — 105 × 148 mm — dans les deux cas ; ce qui
|
||
change est le nombre de cellules par feuille et le tracé de coupe.
|
||
|
||
| papier | format | badges par feuille | axes de coupe | pour 257 badges |
|
||
|---|---|---|---|---|
|
||
| **A4** | 210 × 297 mm | **4**, deux colonnes de 105 et deux rangées de 148 | **3** : une verticale, deux horizontales | **65 planches** |
|
||
| **Lettre** | 215,9 × 279,4 mm | **2**, à plat, 148 de large et 105 de haut | **5** : un encadrement complet | **129 planches** |
|
||
|
||
**Le logiciel demande le format à la première production et ne le redemande
|
||
plus** (§ 8.5). Il n'en impose aucun par défaut : choisir pour l'opérateur, c'est
|
||
produire soixante-cinq feuilles inutilisables quand le bac contient l'autre
|
||
papier. Le format retenu est écrit sur la feuille de contrôle et dans le nom du
|
||
fichier (§ 11.3, § 11.6), parce que c'est la seule chose qui survit à l'envoi de
|
||
la planche à quelqu'un d'autre.
|
||
|
||
**Les deux impositions sont éprouvées à égalité** (§ 14.4) : la pagination sur
|
||
`n = 1, 4, 5, 259, 261` en A4 et `n = 1, 2, 3, 257` en Lettre, et la géométrie —
|
||
cellules, filets, zone de silence — sur chacune. Une branche qui ne serait
|
||
éprouvée que « au cas où » est une branche qui sort fausse le jour où elle sert.
|
||
|
||
#### L'A4 : quatre cellules, un millimètre perdu
|
||
|
||
A4 fait 210 × 297 mm, A6 fait 105 × 148 mm. Deux colonnes de 105 occupent
|
||
**exactement** 210 ; deux rangées de 148 occupent 296 et laissent **1 mm**.
|
||
|
||
Le logiciel **ne répartit pas ce millimètre dans la hauteur des badges.** Il
|
||
dessine quatre cellules de **105 × 148 mm exactement** et laisse le millimètre en
|
||
bande perdue au pied de la feuille. Le coût est **une troisième coupe** : une
|
||
verticale à 105, une horizontale à 148, une horizontale à 296 qui retire la
|
||
bande. Élargir les cellules à 148,5 économiserait cette coupe et produirait un
|
||
carton d'un demi-millimètre hors format, que rien ne garantit qu'une pochette
|
||
taillée à 148 accepte — et la planche cesserait de pouvoir s'annoncer A6. **Une
|
||
sortie qui ment sur son format est pire qu'une sortie qui coûte une coupe**, et
|
||
c'est la même règle qui gouverne le papier Lettre ci-dessous.
|
||
|
||
> La troisième coupe ne coûte rien à qui tranche la rame entière sur un massicot :
|
||
> trois réglages de butée, trois passes, quel que soit le nombre de feuilles. Elle
|
||
> coûte un geste par feuille aux ciseaux. C'est un argument de plus pour
|
||
> l'imposition en pile (§ 11.6) sur les gros tirages, pas une raison de rogner le
|
||
> badge.
|
||
|
||
**L'absence de marge latérale gouverne tout le reste.** Le bord d'un badge est le
|
||
bord de la feuille, où aucune imprimante à jet ou à laser courante ne dépose
|
||
d'encre. Le logiciel ne dessine donc **ni fond perdu, ni aplat de couleur, ni
|
||
cadre** : un fond coloré ressort avec une bande blanche de trois à cinq
|
||
millimètres sur deux côtés, et la coupe se met à chasser la bande au lieu de
|
||
suivre un repère.
|
||
|
||
#### Le Lettre : deux cellules, un encadrement complet
|
||
|
||
Le Lettre mesure 215,9 × 279,4 mm : deux rangées de 148 en demandent 296. Un
|
||
gabarit A4 imprimé sur Lettre perd la rangée du bas. Le logiciel **refuse de
|
||
réduire en silence** : il produit **deux badges A6 exacts par feuille, à plat** —
|
||
148 de large, 105 de haut — plutôt que quatre cartons rognés à 139 mm. Sur Lettre
|
||
les badges ne pavent plus la feuille : le logiciel trace un **encadrement de coupe
|
||
complet**, et la coupe demande cinq passes au lieu de trois. Le nombre de feuilles
|
||
**double**, et le logiciel l'annonce **avant** de produire.
|
||
|
||
> **Trois badges par feuille Lettre sont géométriquement possibles, et le logiciel
|
||
> ne les produit pas.** Deux A6 debout côte à côte occupent 210 × 148, et un A6
|
||
> couché entre dans la bande de 215,9 × 131,4 qui reste : quatre-vingt-sept
|
||
> feuilles au lieu de cent trente pour deux cent soixante badges. Le prix est une
|
||
> **coupe non traversante**, qui interdit de trancher la rame entière en un
|
||
> réglage — elle échange un tiers du papier contre un geste par feuille. Elle se
|
||
> décidera sur l'outil de coupe réellement disponible, et pas avant : une
|
||
> imposition de plus est une imposition de plus à éprouver.
|
||
|
||
**La pagination s'éprouve sur les deux impositions, et sur les comptes qui
|
||
séparent les deux arrondis** (§ 14.2) : `n = 1, 4, 5, 259, 261` pour l'A4 —
|
||
jamais 260, où la troncature et l'arrondi par excès rendent la même valeur — et
|
||
`n = 1, 2, 3, 257` pour le Lettre, où `3` sépare les deux calculs que `4`
|
||
confond. **Le compte est celui des participants non exclus** : un exclu n'a de
|
||
table à aucun tour, son badge serait vide, et c'est ce cas qui fait passer de 65
|
||
pages à 65 avec une case blanche, ou à 64, selon l'implémentation. Le témoin de
|
||
257 badges donne **65 planches A4** et **129 planches Lettre**.
|
||
|
||
**Le format de papier est un réglage local de la machine** (§ 8.5), retenu d'une
|
||
séance à l'autre, jamais écrit dans le fichier d'événement : il décrit le bac de
|
||
l'imprimante, pas le plan.
|
||
|
||
### 11.3 Le découpage, et la feuille de contrôle
|
||
|
||
**Les filets de coupe sont fins, d'une encre neutre, continus**, tracés sur les
|
||
**trois** axes de coupe. Ils tombent sur le bord du badge, dans la zone de
|
||
silence, et ce qu'il en reste après la coupe ne porte aucune information. Le
|
||
logiciel **ne pose pas de traits de coupe en marge** : il n'y a pas de marge, et
|
||
il ne prétend pas en poser. Les trois axes ne sont pas deux médianes : 148 n'est
|
||
pas la médiane de 297, et le troisième filet est celui qui retire la bande
|
||
perdue.
|
||
|
||
**La zone de silence est la garantie de la coupe.** Aucun texte, aucune marque ne
|
||
s'approche à moins de **8 mm** d'un bord de badge. Cette distance couvre **deux
|
||
bornes différentes selon le bord**, et le commentaire de la constante le dit,
|
||
sans quoi la prochaine main la « corrigera » par le calcul :
|
||
|
||
- sur un **bord de coupe** : le décalage de l'imprimante, qui déplace la planche
|
||
entière sur la feuille, plus l'erreur de main, qui déplace la lame ;
|
||
- sur un **bord de feuille** : la marge non imprimable de l'imprimante et le
|
||
biais d'entraînement du papier, qui n'ont rien à voir avec la coupe.
|
||
|
||
**8 mm est aujourd'hui un défaut de travail, non une constante mesurée au sens du
|
||
§ 14.1**, et la constante le déclare. La mesure appartient au **niveau manuel**
|
||
du § 14.4 : elle se fait sur les imprimantes visées, avant la livraison, parce
|
||
qu'un banc de navigateur ne mesure ni un décalage d'imprimante ni une erreur de
|
||
main. Une zone calculée au plus juste fait couper dans un patronyme.
|
||
|
||
**La cote intérieure des pochettes n'est pas une contrainte de la première
|
||
itération.** Le badge reste un A6 exact, et la troisième coupe reste : la découpe
|
||
se fait au massicot, où trois passes coûtent trois réglages de butée quel que soit
|
||
le nombre de feuilles. Si une pochette mesurée tolérait un demi-millimètre, la
|
||
bande perdue pourrait être répartie et la troisième coupe disparaître — mais la
|
||
planche cesserait alors de pouvoir s'annoncer A6, et **une sortie qui ment sur son
|
||
format est pire qu'une sortie qui coûte une coupe**.
|
||
|
||
**La feuille de contrôle est la page 1 du document, et ne se découpe pas.** Elle
|
||
porte une **échelle graphique de 100 mm, repérée tous les 10 mm**, les dimensions
|
||
annoncées du badge, le format de papier, l'imposition retenue, le nombre de
|
||
planches, le compte de badges produits et le compte attendu, le nom et la date de
|
||
l'événement, l'horodatage du placement, la version (§ 18.6), et le filigrane
|
||
BROUILLON s'il y a lieu. L'opérateur la mesure sous une règle avant d'engager la
|
||
rame : si la planche est sortie « ajustée à la page », la graduation ne fait plus
|
||
100 mm et le défaut se découvre sur la première feuille au lieu de la
|
||
soixante-cinquième.
|
||
|
||
**L'impression est recto seul.** Un verso aligné dépend d'un retournement dont le
|
||
décalage se cumule avec celui du recto, et un badge dont le dos appartient à
|
||
quelqu'un d'autre n'est pas rattrapable.
|
||
|
||
### 11.4 Ce que porte un badge
|
||
|
||
Le lecteur est la personne qui le porte, debout, le carton à hauteur de poitrine,
|
||
et qui cherche sa table. Le second lecteur est l'hôte de salle, à un mètre, qui
|
||
cherche à qui il parle.
|
||
|
||
| rang | contenu | pourquoi |
|
||
|---|---|---|
|
||
| 1 | **prénom et nom**, le plus grand corps de la planche | c'est l'identité, et c'est ce que lit l'hôte à un mètre |
|
||
| 2 | **une ligne par tour** : « Tour 1 — table 12 », et le siège quand les sièges sont attribués | c'est la question à laquelle le badge existe pour répondre |
|
||
| 3 | **appartenance**, un corps réduit, sous le nom | elle distingue deux homonymes à l'accueil |
|
||
| 4 | **nom de l'événement et date**, en pied, corps minimal | deux soupers dans la même salle le même mois |
|
||
| 5 | **l'horodatage du placement**, en pied, corps minimal, lisible en main et non à un mètre | sans lui, deux versions d'un plan circulent autour de deux cous et personne ne dit laquelle fait foi |
|
||
|
||
Le pied des **pages** imprimées porte cinq éléments, la version comprise
|
||
(§ 11.7). Le badge n'est pas une page : il porte le nom de l'événement, sa date
|
||
et l'horodatage, **pas le tour** — il les porte tous — et **pas la version**, qui
|
||
ne sert à aucun diagnostic au cou d'un invité et dont la place est comptée.
|
||
|
||
**Un itinéraire constant s'écrit en une ligne.** Quand une personne occupe la même
|
||
table à chacun des R tours — le cas de tout participant ancré (§ 4.2) — le badge
|
||
écrit « Toute la soirée — table 7 » plutôt que R lignes identiques. Le rang 2 ne
|
||
perd rien : la question « où vais-je au tour 3 » reçoit la même réponse, et la
|
||
place gagnée revient au nom.
|
||
|
||
**Le filigrane BROUILLON s'applique au badge comme au reste** (§ 11.7). C'est la
|
||
sortie où la confusion coûte le plus cher : elle ne reste pas sur une table, elle
|
||
part au cou de deux cents personnes.
|
||
|
||
**Ce qui n'y va pas**, et la raison de chaque exclusion :
|
||
|
||
- **Le courriel.** Un badge se perd, se photographie et se lit par-dessus une
|
||
épaule. Rien n'oblige à y porter une coordonnée.
|
||
- **Les notes.** Champ libre où l'opérateur écrit ce qu'il ne dirait pas à
|
||
l'intéressé.
|
||
- **Le titre pressenti.** Il est informatif et ne pourvoit aucune place (§ 4).
|
||
- **Un titre de place que le moteur a simplement fait occuper.** Le § 4.1 pose
|
||
qu'un titre n'est pourvu que par une **réservation**, et que dériver le rôle de
|
||
l'occupation désigne l'animateur par tirage au sort. Imprimer le titre du siège
|
||
occupé reproduit exactement cette faute, en la portant au cou. **Le badge porte
|
||
le titre uniquement quand une réservation en fait la titulaire**, et il l'écrit
|
||
sur la ligne du tour concerné, non en bandeau — une titulaire d'un seul tour
|
||
n'est pas animatrice de la soirée.
|
||
- **Le numéro d'identifiant**, sauf le cas d'homonymie ci-dessous.
|
||
- **Un code à barres ou un code QR.** Le logiciel est hors ligne, aucun lecteur
|
||
n'existe dans la salle, et le code laisse croire qu'un système suit l'entrée.
|
||
- **Une couleur par table.** Des dizaines de tables ne se distinguent pas par des
|
||
dizaines de teintes, et la planche sort souvent en noir et blanc.
|
||
- **Les indicateurs, le plafond, la qualité du placement.** Ils ne concernent pas
|
||
la personne qui porte le carton.
|
||
|
||
**Les homonymes que rien ne sépare.** Le § 12.6 distingue les homonymes par leur
|
||
appartenance et navigue par identifiant ; sur un badge, deux personnes de même
|
||
nom normalisé **et sans appartenance** ne se distinguent par rien. La pile étant
|
||
alphabétique, leurs deux cartons sont voisins : l'hôte en tend un au hasard et
|
||
deux personnes passent la soirée aux tables l'une de l'autre, sans que rien ne le
|
||
signale. Le logiciel **compte ces collisions avant de produire, les nomme, et
|
||
porte l'identifiant en pied des seuls badges concernés.**
|
||
|
||
### 11.5 Les cas qui cassent la mise en page
|
||
|
||
Le badge est un cadre utile de **89 × 132 mm** — 105 et 148 moins deux fois la
|
||
zone de silence. Ce qu'on y met est variable. Le logiciel fixe donc un **ordre de
|
||
sacrifice**, écrit une fois, et ne laisse jamais la mise en page se résoudre par
|
||
un débordement.
|
||
|
||
> nom › lignes de tour › appartenance › nom de l'événement
|
||
|
||
- **Un nom très long.** Le nom **ne s'abrège jamais**. Le logiciel réduit le corps
|
||
par paliers jusqu'à un plancher de lisibilité **mesuré sur les métriques de la
|
||
police embarquée**, puis replie sur une seconde ligne, puis sur une troisième.
|
||
Au-delà, il retire l'appartenance, et le badge reste juste. Le seuil se cale sur
|
||
le **glyphe le plus large** de l'alphabet visé, jamais sur une moyenne
|
||
(§ 14.1) : un nom en capitales larges déborde sinon par-dessus la première ligne
|
||
de tour, en silence.
|
||
- **Une appartenance très longue.** Elle **s'abrège** avec une marque, sur une
|
||
seule ligne. C'est une étiquette de contexte, pas une identité, et un
|
||
repliement sur trois lignes mange les tours.
|
||
- **Beaucoup de tours.** Le logiciel passe à deux colonnes au-dessus d'un nombre
|
||
de tours **mesuré sur le gabarit**, et **n'affiche jamais une liste tronquée** :
|
||
un badge qui omet le tour 5 envoie son porteur nulle part. Si la liste ne tient
|
||
pas à deux colonnes au corps plancher, le logiciel **refuse de produire la
|
||
planche** et nomme le nombre de tours en cause. Le gabarit tient largement les
|
||
deux à six tours d'une soirée ordinaire ; ce refus est une garde contre la
|
||
troncature silencieuse, pas un comportement attendu.
|
||
- **Un participant sans appartenance.** La ligne **n'existe pas**. Ni espace
|
||
réservé, ni tiret : le « — » signale une mesure vide dans la page de qualité
|
||
(§ 5.4), et sur un carton il se lit comme une donnée manquante dont la personne
|
||
viendra demander la cause à l'accueil. La place libérée revient au bloc du nom.
|
||
La mise en page est donc un flux, non une grille de cases fixes.
|
||
- **Une personne en réserve à un tour.** La ligne porte « pas de table » en toutes
|
||
lettres. Une ligne blanche se lit comme un défaut d'impression.
|
||
- **Une personne exclue** n'a pas de badge (§ 4). Le logiciel annonce le compte
|
||
produit et le compte attendu — « 257 badges pour 260 participants, 3 exclus » —
|
||
sur la feuille de contrôle, pour que l'écart soit lu avant la coupe et non à
|
||
l'accueil.
|
||
|
||
### 11.6 L'ordre des badges, et l'imposition
|
||
|
||
**L'ordre est alphabétique**, sur la **forme normalisée** du § 10.1 — espaces
|
||
réduits, casse et accents ignorés — appliquée au couple (nom, prénom), et
|
||
départagée par identifiant entier pour que deux productions de la même planche
|
||
soient identiques.
|
||
|
||
**La comparaison n'emploie pas `localeCompare`** : le § 15.5 l'interdit pour la
|
||
même raison qu'ici, l'ordre dépendrait de la langue du système et deux machines
|
||
produiraient deux piles différentes du même fichier. Le logiciel compare les
|
||
formes normalisées par points de code.
|
||
|
||
La justification de l'ordre est le geste réel : à l'accueil, une personne arrive
|
||
et **dit son nom**. L'hôte cherche dans une pile. Un classement par table
|
||
exigerait de connaître la table avant de connaître la personne, c'est-à-dire de
|
||
disposer déjà de la réponse que le badge apporte. Le classement par table sert un
|
||
autre geste — déposer les cartons sur les tables avant l'arrivée — que le logiciel
|
||
offre en second choix et ne retient pas par défaut.
|
||
|
||
**L'ordre sur la feuille dépend de la façon de couper, et le logiciel le
|
||
demande.** C'est la différence entre une pile prête à l'emploi et des centaines de
|
||
cartons à intercaler à la main.
|
||
|
||
| imposition | ce que le logiciel écrit sur la planche *i* (sur *S* planches, *P* cellules par planche) | pour qui |
|
||
|---|---|---|
|
||
| **feuille par feuille** *(défaut)* | les badges *P*(*i*−1)+1 à *Pi*, en ordre de lecture | ciseaux ou massicot à main : l'opérateur empile les cartons d'une feuille, puis la feuille suivante par-dessous, et la pile sort triée |
|
||
| **en pile** | en position *p*, le badge (*p*−1)×*S* + *i* | massicot qui tranche toute la rame : les *P* piles obtenues s'empilent dans l'ordre 1…*P* et donnent la séquence complète |
|
||
|
||
**Les cellules excédentaires restent vides, sans filet ni mention.** Leur place
|
||
dépend de l'imposition, et le logiciel ne prétend pas le contraire : en feuille
|
||
par feuille elles sont au pied de la **dernière** planche ; en pile, la formule
|
||
les envoie au bas des **dernières piles**. Dans les deux cas les blancs tombent à
|
||
la fin de la pile finale, ce qui est la propriété qui compte.
|
||
|
||
Les deux impositions produisent la même pile finale par deux gestes différents ;
|
||
prendre l'une pour l'autre oblige à retrier la totalité. Le logiciel inscrit
|
||
l'imposition retenue **dans le nom du fichier produit**, avec le nom de
|
||
l'événement, le format de papier et l'horodatage du placement, parce que c'est la
|
||
seule chose qui survit à l'envoi de la planche à quelqu'un d'autre. Le fichier se
|
||
dépose à côté des fichiers d'événement (§ 8.6), et le logiciel en nomme le chemin.
|
||
|
||
**Produire la planche n'est pas une modification.** Elle n'écrit aucun fichier
|
||
d'événement et n'ajoute aucune entrée d'historique (§ 8.2), donc elle reste
|
||
disponible en **mode lecture** et sur un plan **bloqué**, où la lecture et
|
||
l'impression restent ouvertes (§ 9).
|
||
|
||
### 11.7 Les autres sorties, et ce que porte toute page imprimée
|
||
|
||
Le logiciel n'en produit **aucune en PDF**. Elles se lisent à l'écran et chacune
|
||
porte une **feuille de style d'impression** que le navigateur ou l'hôte
|
||
`electron` applique. Une impression mal paginée s'y rattrape en réimprimant une
|
||
page ; aucune ne se découpe, aucune ne doit entrer dans une pochette.
|
||
|
||
| vue | qui s'en sert, quand | ce qui se passe sans elle |
|
||
|---|---|---|
|
||
| **liste d'accueil** | l'accueil, du début à la fin de l'arrivée | la pile de badges triée la remplace **pour l'arrivée** ; elle ne la remplace ni pour « où est assise telle personne ? » posé par un tiers, ni pour un badge perdu en cours de soirée. Le logiciel la conserve : c'est une table de texte, la sortie la moins exposée au risque de pagination, et la plus manipulée |
|
||
| **feuille par table** | l'animateur de table, à chaque changement de tour | chacun connaît sa table par son badge, donc la rotation se fait ; ce qui disparaît est l'appel nominal, et une place vide ne se remarque plus |
|
||
| **plan de salle** | l'opérateur et le personnel, au montage | il reste à l'écran, où il est interactif ; c'est la vue qui perd le plus à n'être pas sur papier pendant le montage d'une salle sans portable |
|
||
| **rapport de placement** | l'opérateur seul, avant la soirée, pour choisir une proposition | il a l'écran sous les yeux au moment où il choisit ; le logiciel exporte en plus les indicateurs en **CSV** |
|
||
| **page de qualité** | l'opérateur seul, au même moment | idem ; ses graphiques sont en SVG, que le navigateur imprime à la résolution de l'imprimante, et chacun garde le **jumeau tabulaire** du § 12.7, qui est ce qui s'imprime proprement |
|
||
|
||
**Chaque page imprimée porte un pied de cinq éléments** : le nom de l'événement,
|
||
sa date, **le tour**, **l'horodatage du placement** et **la version** (§ 18.6).
|
||
Une feuille sans horodatage circule après avoir été invalidée et personne ne peut
|
||
dire laquelle des deux versions posées sur la table est la bonne. La règle vise
|
||
la **page** ; un carton découpé et porté au cou n'est pas une page (§ 11.4).
|
||
|
||
**Un plan qui n'est ni retenu ni bloqué s'imprime avec la mention BROUILLON en
|
||
filigrane**, planche de badges comprise. Le logiciel n'interdit pas d'imprimer un
|
||
brouillon ; il interdit qu'on le confonde avec le plan définitif.
|
||
|
||
**La pagination du plan n'est pas une affaire de feuille de style.** Un navigateur
|
||
ne sait pas fragmenter **un** élément SVG en pages contiguës : une règle de saut
|
||
de page porte sur des boîtes, pas sur l'intérieur d'un dessin. Le logiciel
|
||
produit donc, pour l'impression, **autant d'éléments SVG que de pages**, chacun
|
||
portant son propre `viewBox` sur sa tuile et son cartouche de repérage, et la
|
||
feuille de style en place un par page. **La découpe du plan en pages est une
|
||
fonction pure** — elle rend N cadrages contigus et leurs cartouches — éprouvée
|
||
sous `node` : c'est du code qu'il faut éprouver, pas une règle de style. Le
|
||
logiciel calcule l'échelle qui ferait tenir le plan sur une page ; si la taille
|
||
de caractère tombe sous le seuil **mesuré** de lisibilité, il découpe, le choix
|
||
restant offert et le découpage proposé par défaut.
|
||
|
||
**La mention « disposition relative » et l'échelle graphique du § 7.7 restent sur
|
||
chaque page imprimée du plan.** Elles y gagnent même leur pleine utilité : c'est
|
||
désormais le réglage d'échelle du navigateur, et non le logiciel, qui peut changer
|
||
le facteur sans prévenir.
|
||
|
||
**Une vue d'aperçu d'impression** applique au document vivant la feuille de style
|
||
d'impression réelle. Ce n'est pas une seconde mise en page : c'est la même,
|
||
regardée avant d'être envoyée à l'imprimante — là où l'aperçu d'une mise en page
|
||
PDF en serait une seconde, avec le défaut que les deux dérivent et que les tests
|
||
des deux restent verts. C'est cette vue que le guide photographie (§ 19.6).
|
||
|
||
**L'export CSV des indicateurs suit les conventions du § 10.3** : UTF-8 avec
|
||
marque d'ordre d'octets, séparé par `;`, décimale française. Un fichier séparé par
|
||
des virgules dont les nombres portent une virgule décimale s'ouvre en colonnes
|
||
fausses dans un tableur francophone, et l'opérateur en conclut que le logiciel
|
||
compte mal. L'ordre des colonnes et des lignes est **fixé** : deux exports du même
|
||
placement sont identiques octet pour octet, sans quoi les comparer dans un
|
||
tableur est un exercice de tri. Il porte **en tête une ligne qui dit qu'il n'est
|
||
pas un format d'entrée** : le CSV des lignes refusées du § 10.1, lui, se réimporte
|
||
tel quel, et deux fichiers CSV dans le même logiciel dont l'un se réimporte et
|
||
l'autre non se confondent au premier essai.
|
||
|
||
**Fiches participants : la sortie imprimée est supprimée.** Son contenu — nom,
|
||
appartenance, titre, table et siège par tour — est exactement celui du badge. Deux
|
||
sorties pour la même information divergent ; celle qui tient autour du cou gagne.
|
||
**La fiche à l'écran demeure** : le § 12.5 en fait le chemin d'accès garanti à la
|
||
ligne d'une seule personne quand la matrice n'est pas dessinée, et le § 7.4 la
|
||
range parmi les documents qui ne tronquent jamais.
|
||
|
||
### 11.8 Ce que le logiciel refuse encore
|
||
|
||
- **Produire une planche depuis un plan qui a dérivé.** Si les participants ou le
|
||
mobilier ont changé depuis que le placement a été retenu, le logiciel signale la
|
||
dérive (§ 9) et nomme ce qui a changé **avant** de produire. L'horodatage du
|
||
badge le dirait aussi, mais après la coupe.
|
||
- **Produire une planche partielle en silence.** Rééditer le badge de trois
|
||
retardataires est une commande nommée, qui produit une planche de trois badges
|
||
et l'annonce comme telle sur sa feuille de contrôle. Tant que le placement n'a
|
||
pas bougé, le badge réédité est **identique** à celui de la première planche —
|
||
c'est ce qu'on attend d'un badge perdu ; s'il a bougé, l'horodatage diffère, et
|
||
c'est ce qu'on attend d'un badge refait.
|
||
|
||
---
|
||
|
||
## 12. La page de qualité
|
||
|
||
### 12.1 Les deux questions
|
||
|
||
L'opérateur n'ouvre pas cette page pour connaître une variance :
|
||
|
||
1. **Ce placement est-il bon ?** — reste-t-il à gagner en relançant ?
|
||
2. **Qui s'y trouve mal servi ?** — car la plainte ne vient pas de la moyenne,
|
||
elle vient d'une personne.
|
||
|
||
Le logiciel n'affiche **aucune forme qui ne réponde à l'une des deux**, et
|
||
chaque vue porte en toutes lettres la question qu'elle traite — **la vue de
|
||
comparaison du § 12.10.6 comprise** : *laquelle des deux sert mieux, et qui y
|
||
perd ?*
|
||
|
||
**Le minimum gouverne la page** : chaque vue place le cas le plus défavorable à
|
||
une position **fixe et prévisible** — le premier point, la première ligne —
|
||
jamais là où il faut le chercher. La moyenne l'accompagne en encre secondaire et
|
||
n'apparaît jamais seule. Un placement où chacun rencontre 27 personnes sauf une
|
||
qui en rencontre 14 a une excellente moyenne et un problème.
|
||
|
||
### 12.2 L'en-tête chiffré
|
||
|
||
Les indicateurs du § 5.4 en une rangée de tuiles, chacun avec son **plafond** et
|
||
la mention de la population mesurée. **Trois tuiles de tête**, dans cet ordre :
|
||
|
||
| tuile | la question qu'elle pose |
|
||
|---|---|
|
||
| **minimum brut** | combien de personnes rencontre la plus mal servie ? |
|
||
| **manque maximal** (§ 12.10.4) | de combien la plus privée est-elle privée, compte tenu de là où ce placement l'a assise ? |
|
||
| **écart d'itinéraire maximal** (§ 12.10.4) | cette proposition a-t-elle perdu avant même de s'asseoir ? |
|
||
|
||
Le chiffre de tête est une **mesure avec son unité et son plafond**, jamais un
|
||
score composite.
|
||
|
||
### 12.3 Première vue — le profil trié
|
||
|
||
**Question : qui est le plus mal servi, de combien, et combien sont-ils ?**
|
||
|
||
Une **courbe en escalier**. En abscisse les participants **triés du moins bien
|
||
au mieux servi** — c'est un rang, et l'axe le dit. Deux séries sur **un seul axe
|
||
vertical** : personnes distinctes rencontrées, et appartenances distinctes
|
||
rencontrées.
|
||
|
||
> Un seul axe, parce que le nombre d'appartenances rencontrées **ne peut pas
|
||
> dépasser** le nombre de personnes rencontrées : la seconde courbe est partout
|
||
> sous la première, et **l'écart se lit directement** comme la redondance des
|
||
> cercles fréquentés. Deux axes détruiraient cette propriété et fabriqueraient,
|
||
> par le seul choix d'alignement, une corrélation absente des données.
|
||
|
||
**Un seul tri, celui de la première série**, vaut pour les deux : une abscisse
|
||
désigne toujours le même participant. La seconde courbe est donc irrégulière, et
|
||
cette irrégularité est de l'information.
|
||
|
||
**Les égalités se départagent par une clé stable** — sinon deux affichages du
|
||
même placement donnent deux dessins, et deux impressions ne se superposent pas.
|
||
|
||
**Le plafond réalisé se trace dans le même ordre** : règle horizontale quand il
|
||
est constant, **ligne en escalier** dès qu'une table est incomplète. Une règle
|
||
unique mentirait.
|
||
|
||
**Le logiciel vérifie les trois inégalités de la ligne de décomposition du
|
||
§ 12.10.5** — `N − 1 ≥ plafond a priori ≥ plafond réalisé ≥ rencontres`. La garde
|
||
est une fonction du moteur,
|
||
`verifier_indicateurs(mesures, plafonds_a_priori, plafonds_realises)` (§ 14.12) :
|
||
avec une seule famille de plafonds, l'inégalité `a priori ≥ réalisé` — la seule
|
||
que la bascule vers l'occupation rend non triviale — ne serait éprouvée nulle
|
||
part. Si une inégalité tombe, le calcul est faux : la page **refuse de dessiner**
|
||
et le dit, plutôt que d'afficher un graphique impossible que personne ne
|
||
regardera d'assez près.
|
||
|
||
**Les places contraintes sont marquées.** Sans cela, l'opérateur attribue au
|
||
générateur un minimum que sa propre réservation impose.
|
||
|
||
**Pourquoi cette forme.** L'histogramme fait de la personne la plus mal servie
|
||
une barre de quelques pixels au bord du dessin, que l'œil saute. La boîte à
|
||
moustaches remplace le minimum par une convention de tracé que l'opérateur ne
|
||
parle pas. Le violon lisse précisément le bout de queue qui porte
|
||
l'information. La courbe triée **commence par le pire cas**.
|
||
|
||
**Ce qu'on lit quand le placement est mauvais :** une falaise à gauche — bon en
|
||
moyenne, contient une plainte ; une courbe plate mais basse — défaut systémique,
|
||
relancer a du sens ; **les deux courbes largement écartées** — les gens
|
||
rencontrent beaucoup de monde et toujours les mêmes cercles, l'échec exact que
|
||
la diversité cherche à éviter et qu'aucun chiffre global ne montre.
|
||
|
||
**Cette vue est inchangée pour UNE proposition** : le nombre de rencontres s'y
|
||
lit en absolu, l'escalier du plafond est tracé à côté, et l'opérateur lit l'écart
|
||
à l'œil. **Dès deux propositions, la comparaison change de quantité** : elle
|
||
trace le **manque**, et non les nombres bruts, pour la raison exposée au
|
||
§ 12.10.1. **Quand les courbes se croisent, l'arbitrage est réel** — l'un sert
|
||
mieux les plus mal lotis, l'autre la masse — et le graphique le montre au lieu de
|
||
le trancher. En comparaison, l'abscisse change de sens et **la page l'écrit** : le
|
||
rang 1 de l'une et de l'autre ne désignent pas la même personne.
|
||
|
||
### 12.4 Deuxième vue — les collisions d'appartenance
|
||
|
||
**Question : d'où vient le problème, et relancer peut-il y changer quelque
|
||
chose ?**
|
||
|
||
Des barres **horizontales**, une par appartenance, la pire en haut, chacune
|
||
empilée en **deux segments** : **prouvé inévitable** (le plancher du diagnostic)
|
||
et **reste**. Chaque ligne porte l'effectif du groupe.
|
||
|
||
**La barre est dessinée en collisions cumulées** — des occurrences
|
||
(paire, tour) —, parce que c'est l'unité dans laquelle le plancher se démontre.
|
||
Empiler deux unités dans une même barre produit un segment « reste » négatif : un
|
||
plan qui garde les deux mêmes collègues ensemble aux quatre tours mesure **1**
|
||
paire distincte contre un plancher de **4**, la garde du § 12.3 refuse alors de
|
||
dessiner, et elle a raison — l'arithmétique est fausse, non dans le code mais
|
||
dans la spécification.
|
||
|
||
**Chaque ligne porte en encre secondaire ses trois autres chiffres** : l'effectif
|
||
du groupe, les **paires distinctes**, et l'**excédent** (§ 5.4). Les deux derniers
|
||
sont ce qui distingue un conflit réparti d'un conflit concentré, et c'est
|
||
l'excédent qui ordonne les propositions (§ 5.7) : une vue qui ne montrerait que
|
||
le volume ferait conclure que le plan le mieux classé est le plus mauvais.
|
||
|
||
**Les lignes se trient sur l'excédent, pas sur la longueur de la barre.** La pire
|
||
appartenance est celle qui remet le plus souvent les mêmes personnes ensemble,
|
||
non celle qui porte le plus d'occurrences — c'est le même arbitrage qu'au § 5.7,
|
||
et deux tris opposés dans une même page se contredisent devant l'opérateur.
|
||
|
||
**Le second segment ne s'appelle pas « évitable ».** Le plancher se calcule en
|
||
relâchant les contraintes : c'est une **borne inférieure**, jamais un fait.
|
||
Nommer « évitable » le complément affirmerait qu'un meilleur placement le
|
||
retire, et condamnerait l'opérateur à relancer indéfiniment sur un conflit
|
||
qu'aucun placement ne retire.
|
||
|
||
**Les participants sans appartenance ne forment pas un groupe** : sinon un
|
||
pseudo-groupe de quarante personnes produit la plus longue barre, un plancher
|
||
entièrement fictif, et l'opérateur conclut que sa salle est trop petite.
|
||
|
||
C'est **la seule vue qui désigne une action** : un segment « prouvé inévitable »
|
||
long signifie que relancer est futile et qu'il faut une table de plus.
|
||
|
||
### 12.5 Troisième vue — les paires répétées
|
||
|
||
**La matrice « qui a rencontré qui » n'est pas lisible à 260 participants**, et
|
||
le logiciel ne la dessine pas à cette taille. Les raisons se mesurent : 67 600
|
||
cellules donnent environ 4 px par cellule sur un portable, loin sous les 24 px
|
||
de cible de pointage ; la matrice est creuse à huit neuvièmes ; elle ne porte
|
||
que R+1 valeurs dont trois sont décisionnelles ; et en ordre alphabétique elle
|
||
ne montre qu'un grain aléatoire où l'opérateur croira voir un motif.
|
||
|
||
**Ce qui la remplace** : un **tableau des paires répétées**, trié par nombre
|
||
décroissant — les deux noms, leurs appartenances, le nombre de fois, les tours.
|
||
Chaque ligne est cliquable et amène sur le plan, au tour fautif.
|
||
|
||
**Deux exceptions** : la matrice se dessine sous un seuil de participants
|
||
**mesuré sur le rendu réel**, et la **ligne d'une seule personne** reste toujours
|
||
accessible par sa **fiche à l'écran** — chemin garanti vers une personne quand la
|
||
matrice n'est pas dessinée, et c'est pourquoi la fiche écran demeure alors que sa
|
||
version imprimée est supprimée (§ 11.7).
|
||
|
||
> **Les paires répétées ne sont pas garanties sur la grande démonstration.** À
|
||
> 260 personnes, 33 tables et 4 tours, chacun occupe au plus 28 créneaux de
|
||
> voisinage sur 259 autres : rien n'oblige une paire à se répéter, et une épreuve
|
||
> qui affirmerait un tableau non vide pourrait échouer sans qu'aucun défaut
|
||
> n'existe. Le tableau se photographie donc (§ 19.3) sur une configuration
|
||
> **construite** où le dénombrement le force — 12 personnes, 4 tables de 3,
|
||
> 6 tours : 12 créneaux de voisinage pour 11 autres personnes, donc au moins une
|
||
> répétition pour chacun, quel que soit l'algorithme.
|
||
|
||
### 12.6 Les cas limites
|
||
|
||
Une page de statistiques se casse par ses bords, et chacun se produira.
|
||
|
||
| cas | ce que la page affiche |
|
||
|---|---|
|
||
| participants exclus | retirés de toutes les statistiques, effectifs mesuré et exclu annoncés |
|
||
| personne en réserve à un tour | gardée, point marqué, avec le nombre de tours non assis ; son plafond réalisé se calcule sur les seuls tours où elle est assise, **et son écart d'itinéraire porte ce que son manque ne porte pas** |
|
||
| aucun placement | le diagnostic préalable et une phrase, **jamais des axes vides** |
|
||
| une seule table | une phrase ; pas trois vues qui disent la même chose |
|
||
| un seul tour | le tableau des paires est remplacé par une phrase, non par un tableau vide |
|
||
| une seule appartenance, ou aucune | une phrase ; **pas** une seconde courbe plate à zéro, qui se lit comme une panne |
|
||
| aucune collision | la deuxième vue est remplacée par sa phrase |
|
||
| noms longs | tronqués à une largeur **mesurée**, entiers dans l'infobulle et le tableau |
|
||
| homonymes | distingués par leur appartenance ; la navigation se fait par identifiant |
|
||
| le manque ne prend qu'une ou deux valeurs distinctes | une phrase portant les effectifs, non une courbe à deux marches |
|
||
| deux propositions au même profil de manque | « indiscernables sur le manque » ; l'ordre explicite du § 5.7 les départage sur le critère suivant, et la page nomme lequel |
|
||
| deux profils qui alternent plusieurs fois | les trois effectifs de rangs, **sans** rang de bascule ; le jumeau tabulaire porte le détail |
|
||
| populations mesurées différentes entre deux propositions — ce que produit la dérive du § 9 sur des générations accumulées | ni courbe ni dominance : la page nomme les personnes exclues de part et d'autre et le dit **non comparable** |
|
||
| le plafond réalisé d'une personne vaut 0 | son manque vaut 0 et n'enseigne rien : la page marque le point et renvoie à l'écart d'itinéraire. Un manque nul n'est jamais lu comme « bien servi » sans son plafond à côté |
|
||
| le plafond a priori n'a pas pu être maximisé | l'écart d'itinéraire s'écrit **« inconnu »**, jamais 0 ; le certificat reste hors d'atteinte (§ 5.5) et le départage du § 5.7 qui en dépend est sauté, la page nommant le critère suivant |
|
||
|
||
### 12.7 Les règles de forme
|
||
|
||
- **Jamais deux axes verticaux.**
|
||
- **L'identité d'une série ne repose jamais sur la seule couleur** : au plus
|
||
deux séries, **étiquetées directement sur le tracé** en plus de la légende. La
|
||
page s'imprime, souvent en noir et blanc.
|
||
- **Les couleurs d'état sont réservées** : la teinte qui signale un conflit ici
|
||
est **la même** que sur le plan de salle, et ne sert à rien d'autre.
|
||
- **Une échelle séquentielle est une seule teinte, du clair au foncé.** Jamais
|
||
d'arc-en-ciel : un dégradé multicolore invente des frontières là où la
|
||
grandeur est continue.
|
||
- **Le texte garde une encre neutre** ; la couleur est portée par la marque.
|
||
- **La palette se valide par calcul, jamais à l'œil** : simuler d'abord les trois
|
||
dichromaties, **puis** mesurer l'écart — mesurer sur les couleurs d'origine ne
|
||
dit rien de ce que voit une personne daltonienne. **4,5:1** pour le texte,
|
||
**3:1** pour les marques porteuses d'information.
|
||
- **Chaque graphique a son jumeau tabulaire** : un survol n'est jamais le seul
|
||
chemin vers une valeur, et un tableau s'imprime. Celui de la vue de comparaison
|
||
est **l'escalier du manque** (§ 12.10.6), qui accepte en outre plus de deux
|
||
propositions là où le tracé s'arrête à deux.
|
||
- **Le pointage se fait au plus proche** : à 260 points, le pas est de quelques
|
||
pixels, et sans cette couche la vue est inutilisable au pavé tactile.
|
||
|
||
### 12.8 Ce que le logiciel ne dessine pas
|
||
|
||
- **Un score unique, une jauge, un feu vert** : il moyenne ce que l'opérateur
|
||
doit arbitrer, cache quel critère a été sacrifié, et éteint la seconde
|
||
question — un placement à 87 % ne dit pas qui est à 14.
|
||
- **Un camembert des appartenances** : il décrit les **entrées**, pas la qualité
|
||
de la sortie ; identique sous un bon et un mauvais placement.
|
||
- **La moyenne seule**, sous quelque forme que ce soit.
|
||
- **Un nuage « rencontres contre appartenances »** : deux entiers bornés, 260
|
||
points se superposent sur quelques centaines de positions.
|
||
- **Deux placements dans deux graphiques aux axes différents.**
|
||
- **Le taux de satisfaction**, `Σ rencontres / Σ plafond réalisé` : il affiche
|
||
100 % sur un placement dont rien ne peut plus être tiré, et **99,8 %** sur le
|
||
placement du § 12.1 où 259 personnes sont à 27 sur 27 et une à 14 sur 27. Deux
|
||
dixièmes de point séparent un plan sans reproche d'un plan qui contient une
|
||
plainte.
|
||
- **La somme, ou la moyenne, des manques** : une personne à 14 de manque et
|
||
quatorze personnes à 1 donnent le même total et appellent des décisions
|
||
opposées. C'est en outre le scalaire qu'une recherche descend le plus
|
||
volontiers, et il achète toujours la queue contre la masse.
|
||
- **La moyenne des rencontres**, et le taux de couverture des paires qui en est
|
||
la même chose divisée par C(N, 2) : quand les tables sont pleines à chaque
|
||
tour, le nombre de voisinages créés vaut `R · Σ C(c_t, 2)`, fixé par le
|
||
mobilier et identique pour toutes les propositions ; la moyenne est alors une
|
||
fonction affine décroissante du seul **excédent de rencontres**, que les
|
||
colonnes du § 5.4 encadrent déjà par les deux bouts. Sur un seul tour, les
|
||
répétitions sont impossibles et **toutes les propositions ont rigoureusement la
|
||
même moyenne**.
|
||
- **L'écart-type, la variance, l'indice d'équité** : minimisés en servant tout le
|
||
monde également mal. Une courbe plate et basse les emporte sur une courbe haute
|
||
portant une seule personne en bas — ils inversent l'ordre des deux questions du
|
||
§ 12.1.
|
||
- **Le rapport manque / plafond** : indéfini quand le plafond vaut 0, et il fait
|
||
passer un manque de 1 sur un plafond de 3 devant un manque de 3 sur 28
|
||
(§ 12.10.3).
|
||
|
||
### 12.9 D'où viennent les chiffres
|
||
|
||
Les statistiques sont calculées par le **moteur**, en fonctions pures, testées
|
||
sous `node`. **La page ne recalcule rien** : elle lit les mêmes valeurs que le
|
||
tableau d'indicateurs et que le classement. Dès que la même arithmétique existe
|
||
en deux endroits, le graphique et le tableau finissent par se contredire devant
|
||
l'opérateur. La règle vaut pour **l'export CSV des indicateurs** (§ 11.7) comme
|
||
pour le dessin : il lit le même tableau, quel que soit le support.
|
||
|
||
Les trois vues se dessinent en **SVG**, avec **un module de géométrie frère** de
|
||
celui du plan — même principe, même discipline d'épreuve, mais **pas le même
|
||
module** : l'unité du plan est le centimètre réel, celle d'un graphique est le
|
||
rang et le nombre. Les confondre ferait fuir l'unité du bâtiment dans les axes.
|
||
|
||
|
||
### 12.10 Le manque : la grandeur qui compare deux propositions
|
||
|
||
#### 12.10.1 Pourquoi les nombres bruts ne se comparent pas
|
||
|
||
Le § 12.3 trie les participants sur le nombre de personnes rencontrées. Tant que
|
||
toutes les tables sont pleines à tous les tours, ce nombre se compare : le
|
||
plafond réalisé est le même pour tout le monde, et la règle horizontale du
|
||
plafond suffit à lire l'écart. Dès qu'une table est incomplète — la variante du
|
||
§ 15.1 — le plafond réalisé devient un escalier, et cet escalier **change d'une
|
||
proposition à l'autre** : qui passe par la table incomplète dépend du placement.
|
||
|
||
Comparer les nombres bruts est alors faux dans les deux sens. Une personne à 27
|
||
sur un plafond réalisé de 27 est **servie au maximum de ce que son placement lui
|
||
permettait** ; une personne à 27 sur 28 est privée d'une rencontre. Les deux
|
||
affichent 27, et le rang 1 de la courbe désigne la mieux servie des deux.
|
||
|
||
#### 12.10.2 La grandeur de base : le manque
|
||
|
||
Pour une personne p de la population mesurée :
|
||
|
||
```
|
||
manque(p) = plafond réalisé(p) − rencontres(p)
|
||
```
|
||
|
||
Un entier positif ou nul, exprimé en **personnes**. Il ramène tout le monde à la
|
||
même échelle parce qu'il mesure chacun **contre ce que son propre placement
|
||
rendait possible**, et non contre une valeur commune qui n'existe pas. Zéro
|
||
signifie la même chose pour les quatre animateurs des tables de 7 de la grande
|
||
démonstration, dont le plafond vaut 24, et pour un mobile de quatre tables de 8,
|
||
dont il vaut 28 : *rien de ce placement-ci ne peut leur être repris*.
|
||
|
||
Le plafond réalisé se calcule **sur l'occupation**, le plafond a priori sur les
|
||
**capacités** : le § 5.5 en donne les deux formules et le glossaire.
|
||
|
||
C'est la lecture que le § 15.1 demande sans la nommer. Le minimum brut de la
|
||
grande démonstration vaut 24 et **aucune relance ne l'améliore jamais** ; le
|
||
manque, lui, répond à la relance. L'opérateur cesse de relancer sur un chiffre
|
||
qui ne bougera pas.
|
||
|
||
#### 12.10.3 Le manque relatif : refusé comme grandeur
|
||
|
||
Le manque absolu ne distingue pas un manque de 1 sur un plafond de 3 d'un manque
|
||
de 1 sur un plafond de 28. Le logiciel **ne fait pourtant pas du rapport
|
||
manque / plafond une grandeur** : ni axe, ni clé de tri, ni tuile d'en-tête, et
|
||
il ne le calcule nulle part.
|
||
|
||
| choix | mode de défaillance |
|
||
|---|---|
|
||
| le rapport gouverne la page | sur un événement à un tour et des tables de 4 — plafond 3 — un manque de 1 vaut 33 % et passe devant un manque de 3 sur 28, qui vaut 11 %. L'opérateur relance pour quelqu'un qui peut gagner **une** rencontre et ignore trois rencontres perdues. Le rapport est en outre indéfini quand le plafond vaut 0, et ne prend que quatre valeurs sur un plafond de 3 : des paliers que l'œil lit comme des écarts |
|
||
| le manque absolu seul | une personne sur un itinéraire bas paraît bien servie alors qu'elle est proportionnellement la plus privée |
|
||
|
||
Le second défaut se corrige sans inventer de grandeur : **le plafond réalisé
|
||
accompagne le manque partout** — tuile, infobulle, tableau — comme le § 12.2
|
||
l'exige déjà du chiffre de tête, et **à manque égal, le tri place d'abord le
|
||
plafond le plus bas**. Entre deux manques égaux, la personne proportionnellement
|
||
la plus privée est donc la première ; **entre deux manques différents, le tri ne
|
||
prétend rien sur la proportion**, et c'est le choix assumé ci-dessus.
|
||
L'infobulle d'un point porte la ligne entière, qui enseigne le vocabulaire :
|
||
|
||
> manque 3 · rencontres 24 · plafond réalisé 27 · plafond a priori 28 · 259 au total
|
||
|
||
#### 12.10.4 Les trois chiffres, et aucun quatrième
|
||
|
||
Chacun porte sa population mesurée et ses deux lignes secondaires — mobiles,
|
||
ancrés — comme tout agrégat du § 5.4. Quand la population mesurée est vide, les
|
||
trois s'écrivent « — ».
|
||
|
||
**1. Le manque maximal.** *De combien la personne la plus privée est-elle privée,
|
||
compte tenu de là où ce placement l'a assise ?* Le plus grand manque sur la
|
||
population mesurée. Vaut **0** sur un placement dont personne ne peut plus rien
|
||
tirer, et le logiciel écrit alors le nombre de personnes mesurées à côté. Vaut
|
||
**13** sur le placement du § 12.1 où chacun atteint un plafond réalisé de 27 sauf
|
||
une personne qui ne rencontre que 14. Majorant : le plus grand plafond réalisé,
|
||
qui borne l'axe. C'est la transposition du « le minimum gouverne la page » sur
|
||
une échelle comparable ; la page écrit **« le plus grand manque »**, jamais
|
||
« manque minimal », qui désignerait le mieux servi.
|
||
|
||
**2. L'effectif du manque.** *La falaise est-elle une personne ou quarante ?* Le
|
||
nombre de personnes dont le manque vaut au moins 1. Vaut **0** sur un placement
|
||
sans manque — un vrai zéro, mesuré sur une population non vide, et non un « — ».
|
||
Vaut **1** sur le placement précédent, et jusqu'à l'effectif mesuré sur un
|
||
placement systématiquement mauvais. Un manque maximal de 13 porté par une
|
||
personne et un manque maximal de 13 porté par quarante appellent deux décisions
|
||
opposées : déplacer quelqu'un, ou ajouter une table.
|
||
|
||
**3. L'écart d'itinéraire maximal.** *Cette proposition a-t-elle perdu avant même
|
||
de s'asseoir ?* Le plus grand `plafond a priori(p) − plafond réalisé(p)`. Il
|
||
recouvre les deux façons de perdre sur le parcours : visiter moins de tables
|
||
distinctes qu'il n'était possible, et occuper des tables incomplètes. Vaut **4**
|
||
dans la variante du § 15.1 pour un mobile que la proposition assoit aux quatre
|
||
tours à une table occupée par 7 : quatre tours à `7 − 1 − 1 = 5` sièges
|
||
renouvelés, soit 20, plus les 4 animateurs rencontrés une fois — 24 contre les 28
|
||
que la configuration promettait.
|
||
|
||
**C'est le chiffre qui sauve le manque d'un contresens.** Une personne mise en
|
||
réserve à trois tours sur quatre a un plafond réalisé minuscule et un manque
|
||
nul : elle n'apparaît ni au premier chiffre, ni au second, alors qu'elle est la
|
||
plus mal servie de la soirée. Son écart d'itinéraire, lui, est énorme.
|
||
|
||
**Et c'est une borne, pas une promesse.** Un écart d'itinéraire non nul n'est pas
|
||
toujours récupérable : dans la grande démonstration, les tables de 7 offrent 112
|
||
sièges-tours dont 16 aux animateurs, donc **96 sièges-tours mobiles** qu'un
|
||
placement ne peut pas ne pas servir ; chaque mobile n'en absorbant que 4 au plus,
|
||
**au moins 24 mobiles** terminent sous leur plafond a priori de 28, et l'écart
|
||
d'itinéraire maximal **ne descend jamais sous 1**. Il en va de même dans la
|
||
variante, où les 4 sièges vides laissent à chaque tour une table d'au moins 3
|
||
mobiles incomplète. **La page écrit ce plancher à côté du chiffre**, comme le
|
||
diagnostic du § 5.6, et n'appelle jamais « récupérable » le complément —
|
||
exactement la discipline du § 12.4 sur « prouvé inévitable » et « reste ». Sans
|
||
ce plancher, le troisième chiffre envoie l'opérateur relancer indéfiniment vers
|
||
un 0 que la configuration interdit.
|
||
|
||
#### 12.10.5 La décomposition, et ce qu'elle autorise à écrire
|
||
|
||
Quatre quantités sur une ligne, trois différences entre elles :
|
||
|
||
```
|
||
N − 1 ≥ plafond a priori ≥ plafond réalisé ≥ rencontres
|
||
écart d'itinéraire manque
|
||
```
|
||
|
||
Le logiciel **ne nomme que deux** de ces différences — celles qu'il affiche. La
|
||
première est le diagnostic du § 5.6, qui a déjà ses mots. Le § 5.5 pose « deux
|
||
notions, deux noms, jamais trois » ; la discipline tient, parce que **aucun de
|
||
ces deux noms ne désigne une borne** : ce sont des différences entre deux termes
|
||
voisins de la même ligne, et il n'existe toujours que deux bornes.
|
||
|
||
La décomposition tranche une ambiguïté : le certificat de « minimum atteint » se
|
||
lit **contre le plafond a priori**, donc sur `manque + écart d'itinéraire`,
|
||
jamais sur le seul manque (§ 5.5). Conséquence directe, que le guide d'usage
|
||
écrit : **la grande démonstration ne décerne jamais ce certificat.**
|
||
|
||
La vérification du § 12.3 — la courbe ne dépasse jamais son plafond, sinon la
|
||
page refuse de dessiner — devient un **test de signe sur les trois inégalités**
|
||
de la ligne. Un écart négatif est un calcul faux, et la page le dit au lieu de
|
||
tracer.
|
||
|
||
#### 12.10.6 La vue de comparaison
|
||
|
||
Elle porte sa question en toutes lettres, comme le § 12.1 l'exige de chaque vue :
|
||
**laquelle des deux sert mieux, et qui y perd ?**
|
||
|
||
Le § 12.3 reste inchangé pour **une** proposition. Quatre lignes ne se lisent
|
||
pas ; dès **deux** propositions, le logiciel dessine donc l'écart lui-même.
|
||
|
||
- **La quantité** : le manque, et lui seul. Une seule série par proposition, deux
|
||
propositions au plus sur le tracé, étiquetées directement (§ 12.7).
|
||
- **L'axe vertical** : le manque en personnes, **zéro en bas**, graduations
|
||
entières, borne haute commune aux deux courbes — jamais une échelle par courbe,
|
||
que le § 12.8 refuse. Le zéro porte son libellé : « rien à reprendre à ce
|
||
placement ».
|
||
- **L'abscisse** : le rang, chaque proposition triée **par son propre manque
|
||
décroissant**, départagé par plafond réalisé croissant puis par identifiant.
|
||
Les deux courbes descendent. Le rang 1 de l'une et de l'autre ne désigne pas la
|
||
même personne, et la page l'écrit.
|
||
- **Le jumeau tabulaire** (§ 12.7) est **l'escalier du manque** : une ligne par
|
||
valeur de manque, et par proposition une colonne d'effectif et une colonne
|
||
d'effectif cumulé — la seconde est la courbe elle-même, lue à l'envers. Il
|
||
s'imprime, et il accepte plus de deux propositions là où le tracé s'arrête à
|
||
deux. Il lit le même tableau que le tracé (§ 12.9).
|
||
|
||
**Quand aucune ne domine**, le § 12.3 dit déjà que l'arbitrage est réel. Le
|
||
manque change ce qui se lit : avec des nombres bruts, un croisement peut n'être
|
||
qu'un artefact des plafonds — la courbe du bas appartient peut-être à des gens
|
||
assis à des tables de 7. Avec le manque, **le croisement porte sur le
|
||
placement**. Le logiciel ne laisse pas lire la position du croisement à l'œil sur
|
||
260 points : **il compte les rangs**, parce que deux profils décroissants peuvent
|
||
se recroiser plusieurs fois et qu'un « rang de croisement » unique n'existe pas
|
||
en général. La page écrit les trois effectifs — « A sert mieux à 12 rangs, B à
|
||
200, égalité à 48 » — et, **quand le signe ne change qu'une fois**, nomme en plus
|
||
le rang de bascule.
|
||
|
||
Ce que la vue ne dit pas, et que l'en-tête garde à côté : le manque mesure chacun
|
||
contre son propre placement, donc **une proposition peut avoir un manque plus bas
|
||
en offrant moins de rencontres réelles**. La tuile du minimum brut et l'écart
|
||
d'itinéraire maximal restent affichés à côté des courbes ; une implémentation qui
|
||
les retire rend le piège invisible.
|
||
|
||
#### 12.10.7 La dominance
|
||
|
||
**Dominance de profil.** La proposition A domine B quand, à **chaque rang** i,
|
||
`manque_A(i) ≤ manque_B(i)`, avec au moins une inégalité stricte, les deux séries
|
||
étant triées chacune par son propre ordre. C'est exactement « les courbes ne se
|
||
croisent pas ». Un parcours linéaire après le tri suffit. Le logiciel l'énonce en
|
||
toutes lettres quand elle tient — « la proposition A sert mieux à tous les
|
||
rangs » — et **n'affiche rien quand elle ne tient pas** : un témoin presque
|
||
toujours éteint apprend à ne plus être regardé, et la page montre déjà le
|
||
croisement.
|
||
|
||
**La formulation est à surveiller mot à mot** : « sert mieux à tous les rangs »
|
||
n'est pas « sert mieux chaque personne ». Le rang 3 des deux propositions désigne
|
||
deux personnes différentes. Écrire la seconde phrase promet à l'opérateur qu'il
|
||
peut basculer sans léser personne, ce qui est faux.
|
||
|
||
**Dominance personne par personne** — `manque_A(p) ≤ manque_B(p)` pour tout p —
|
||
se calcule aussi, en une passe, et **le logiciel ne la calcule pas**. La raison
|
||
est une implication, pas une statistique : **un ordre point par point survit au
|
||
tri**, donc une dominance personne par personne entraîne toujours la dominance de
|
||
profil, qui est déjà annoncée. La seconde ligne ne s'allumerait que dans les cas
|
||
où la première est déjà allumée, et l'opérateur n'en tire aucune décision
|
||
différente.
|
||
|
||
La petite démonstration du § 15.3 donne le test qui peut échouer (§ 14.2) : son
|
||
plan parfait a un manque nul pour les douze, donc **aucune proposition ne le
|
||
domine, et il domine strictement toute proposition qui n'est pas elle-même à
|
||
manque nul**. Le test s'énonce dans ces termes et non en « domine toute autre
|
||
proposition » : la petite démonstration admet plusieurs plans parfaits, et deux
|
||
profils tous à zéro ne se dominent pas — la formulation forte ferait échouer le
|
||
test sur un résultat juste. Le test **refuse de passer** s'il ne trouve aucune
|
||
proposition concurrente à comparer.
|
||
|
||
#### 12.10.8 Ce que le logiciel ne sort pas
|
||
|
||
Le § 12.8 en porte la liste, allongée de quatre familles qui s'offrent
|
||
d'elles-mêmes et trompent dans ce contexte précis : le taux de satisfaction, la
|
||
somme ou la moyenne des manques, la moyenne des rencontres et le taux de
|
||
couverture des paires, l'écart-type et ses parents. Chacune y est écartée avec le
|
||
chiffre qui montre sa défaillance.
|
||
|
||
#### 12.10.9 D'où viennent ces chiffres
|
||
|
||
Du moteur, en fonctions pures, comme tout le § 12.9. Le manque et l'écart
|
||
d'itinéraire sont deux soustractions sur des tableaux déjà calculés ; ils
|
||
n'ouvrent aucun parcours des ensembles de rencontres et ne changent pas le coût
|
||
du § 5.10. Le tri du profil est en O(N log N) sur une clé stable. La page ne
|
||
recalcule rien.
|
||
|
||
Une réserve honnête sur l'un des deux : le manque ne coûte rien, le plafond
|
||
réalisé se lisant sur le placement. **L'écart d'itinéraire suppose le plafond a
|
||
priori disponible par personne**, et non seulement en agrégat au diagnostic ; son
|
||
coût est exactement le point 4 du § 17, que cette section rend porteur.
|
||
Tant qu'il n'est pas mesuré, le troisième chiffre s'écrit **« inconnu »** plutôt
|
||
que de retarder la page, et le départage du § 5.7 qui en dépend est sauté en
|
||
nommant le critère appliqué à sa place.
|
||
|
||
---
|
||
|
||
## 13. Architecture
|
||
|
||
### 13.1 Forme du projet
|
||
|
||
Une application **Capacitor**, dont le code web vit dans `www/` — la sortie de
|
||
Vite, que Capacitor désigne comme son répertoire web — et ne dépend d'aucun
|
||
service.
|
||
|
||
| plateforme | ce qui la porte | rôle |
|
||
|---|---|---|
|
||
| `electron` | une **coquille Electron propre au projet**, emballée par `electron-builder` | la livraison : un exécutable Windows |
|
||
| `web` | la plateforme web de Capacitor, servie en local | le développement et les tests sous Linux |
|
||
| `android` | la plateforme Android de Capacitor | ouverte, non requise pour la première livraison |
|
||
|
||
**Capacitor ne porte pas l'exécutable Windows, et c'est délibéré.** Il ne fournit
|
||
de plateforme de bureau que par une extension communautaire, et une plateforme
|
||
dont la mise à jour ne dépend pas du projet fige le moteur d'exécution embarqué :
|
||
l'exécutable livré porterait un Chromium que plus personne ne corrige. La coquille
|
||
du projet tient en deux fichiers — le processus principal et le script de
|
||
préchargement — et fixe elle-même sa version d'Electron, que la construction met
|
||
à jour comme n'importe quelle dépendance.
|
||
|
||
**La coquille est l'endroit où vivent les deux frontières de plateforme du § 13.4
|
||
sous `electron`** : le système de fichiers — écriture atomique, verrou, sonde
|
||
d'écriture, chemin publié par le lanceur portable (§ 8.6, § 8.8) — et
|
||
l'impression (§ 11.7). Le processus principal les exécute ; le script de
|
||
préchargement les expose à l'application par un **pont étroit et nommé**,
|
||
l'isolation de contexte restant active et l'intégration de Node coupée dans la
|
||
page. Une page qui atteindrait le système de fichiers directement ferait d'une
|
||
faille d'affichage — un nom importé interprété comme du balisage — un accès
|
||
complet au disque.
|
||
|
||
Le code que Vite compile vit sous **`src/`**, en modules nommés d'après les
|
||
couches du § 13.4 : `src/moteur`, `src/geometrie`, `src/stockage`, `src/csv`,
|
||
`src/pdf`, `src/demo`, `src/application`, `src/interface`. À la racine du
|
||
projet, **`version.json`** est l'unique source de la version (§ 18.3) et
|
||
**`src/version.genere.js`** le module qu'elle engendre — **versionné dans le
|
||
dépôt**, parce qu'une séance de développement lancée sans l'étape de construction
|
||
doit afficher une version et non une importation manquante, et **contrôlé par
|
||
rejeu du script**, parce qu'un fichier engendré et oublié affiche une version
|
||
périmée avec l'assurance d'une constante.
|
||
|
||
### 13.2 Une seule langue, et c'est une exigence
|
||
|
||
Tout — le moteur, la géométrie, l'interface — est écrit en **JavaScript**.
|
||
Aucune partie du calcul n'est dupliquée dans un second langage.
|
||
|
||
Cette propriété doit être **préservée délibérément**. Dès que la même
|
||
arithmétique existe en deux endroits, une suite de tests peut prouver qu'ils
|
||
s'accordent **sans jamais prouver qu'ils ont raison** : les deux dérivent
|
||
ensemble et les tests restent verts. Une seule implémentation rend ce mode de
|
||
défaillance impossible.
|
||
|
||
**Une exception, nommée, et une seule** : le calcul incrémental des indicateurs
|
||
du § 5.10. Elle tient à une condition écrite — **le recalcul complet est la
|
||
mesure**, celle dont sortent les chiffres affichés ; **l'incrémental n'existe que
|
||
dans la boucle de recherche**, où il sert de score et ne s'affiche jamais — et à
|
||
une épreuve qui lie les deux à la fin de chaque recherche, à cinq ordres de
|
||
grandeur de mouvements (§ 14.10).
|
||
|
||
### 13.3 La bibliothèque d'interface : Svelte 5, construit par Vite
|
||
|
||
Le logiciel n'emploie **aucune bibliothèque de dessin SVG** : le plan est du SVG
|
||
écrit à la main, et le déplacement, le zoom et la conversion de coordonnées sont
|
||
des fonctions pures du module de géométrie.
|
||
|
||
| critère | ce que Svelte apporte |
|
||
|---|---|
|
||
| tenir hors ligne | se compile en JavaScript ordinaire ; Vite est une dépendance de développement qui ne survit pas à la construction |
|
||
| rester rapide | compile des mises à jour fines, sans arbre virtuel ni mémoïsation à poser à la main |
|
||
| se tester sans navigateur | n'impose d'écrire aucun calcul dans un composant ; le composant ne contient que le câblage |
|
||
| être joli sans y passer des semaines | un fichier de jetons — couleurs, rayons, ombres, échelle typographique — décliné en deux thèmes |
|
||
|
||
**Les candidates écartées.** Vue 3 est le second choix défendable, écarté de peu
|
||
sur le poids et sur la discipline qu'exige sa réactivité. React est écarté sur la
|
||
mémoïsation à poser à la main. Preact sans étape de construction supprime un gain
|
||
plus petit qu'il n'en a l'air et se paie chaque jour en ergonomie ; il reste le
|
||
repli nommé. Lit est écarté parce que son intérêt tient au DOM fantôme, qui
|
||
cloisonne les styles et rend plus coûteuse la lecture du style calculé qu'exige
|
||
le § 14.2. Solid est écarté sur la taille de sa communauté, pour un logiciel
|
||
destiné à être repris. Les bibliothèques canvas résolvent un problème que le
|
||
logiciel n'a pas. **D3** est écarté pour une raison précise : `d3-zoom` conserve
|
||
la transformation dans une propriété accrochée au nœud du DOM, **hors du
|
||
modèle** — or l'historique et la sauvegarde automatique exigent que tout état
|
||
persistant vive dans le modèle.
|
||
|
||
> Le poids ne tranche pas : l'application compilée pèse quelques dizaines de
|
||
> kilooctets, un paquet Electron plus de cent millions. L'écart entre la plus
|
||
> légère et la plus lourde des candidates représente **moins d'un millième du
|
||
> livrable**.
|
||
|
||
**Le logiciel n'emploie pas Tailwind** ni aucun outil qui déduit les styles en
|
||
lisant le code source. Le mode de défaillance est précis : les classes d'état
|
||
d'une table — conflit, rencontre répétée, siège réservé — se composent **à
|
||
l'exécution** à partir de ce que le moteur rend. Un balayage de sources ne les
|
||
voit pas, les élague, et **la table en conflit reste grise pour toujours, sans
|
||
message d'erreur**.
|
||
|
||
### 13.4 Les couches
|
||
|
||
```
|
||
interface le plan, les listes, les formulaires
|
||
│
|
||
application les états, les commandes, l'historique
|
||
│
|
||
moteur le placement : pur, sans DOM, sans API de plateforme
|
||
│
|
||
stockage lecture et écriture de fichiers
|
||
```
|
||
|
||
**Le moteur ne connaît ni le DOM, ni Capacitor, ni Electron.** Il reçoit des nombres et des
|
||
listes, il rend des placements. C'est ce qui le rend testable sous `node`, en
|
||
quelques secondes — et c'est la condition pour qu'il soit éprouvé sérieusement.
|
||
|
||
**La géométrie du plan** est elle aussi un module de **fonctions pures**, séparé
|
||
du rendu.
|
||
|
||
Les **deux frontières de plateforme** sont le système de fichiers et
|
||
l'impression : une interface unique, deux implémentations — plus, pour le système
|
||
de fichiers, une **troisième implémentation injectée par les épreuves**, qui sait
|
||
simuler l'échec de renommage du § 8.8 et les cinq issues du § 8.6 : le chemin
|
||
publié par le lanceur portable, un dossier de travail sous un emplacement de
|
||
données applicatives, une sonde d'écriture qui échoue, une perte
|
||
d'inscriptibilité **en cours de séance**, et un support amovible. Sans elle, les
|
||
100 % de branches exigés des chemins de reprise (§ 14.13) ne s'écrivent pas, et
|
||
la règle à cinq issues du § 8.6 reste le code le moins éprouvé du logiciel alors
|
||
qu'elle décide de l'endroit où vit la soirée.
|
||
|
||
**La frontière des couches est gardée mécaniquement**, et pas seulement par
|
||
discipline : un test du **graphe d'imports** refuse qu'un module de `src/moteur`
|
||
ou de `src/geometrie` importe de `src/interface`, de `src/application`, de
|
||
`src/stockage` ou d'une interface de plateforme. Il refuse de même que
|
||
`src/stockage` importe de `src/csv`, de `src/application`, de `src/interface` ou
|
||
d'une plateforme, ou touche le navigateur hors de ses deux implémentations du
|
||
système de fichiers, qu'aucun autre de ses modules n'importe ; que `src/csv`,
|
||
qui lit le modèle du stockage, importe de `src/application`, de `src/interface`
|
||
ou d'une plateforme ; et que `src/application` importe de `src/interface` ou de
|
||
Svelte. Un type que la documentation d'un module importe compte comme un
|
||
import. Le projet `node` ne chargeant pas le greffon Svelte (§ 14.8), un test de
|
||
moteur qui importerait un composant échoue au chargement — mais cette garde-là
|
||
ne couvre que les composants, et un module qui touche `document` ne tomberait
|
||
qu'à l'exécution de la branche fautive.
|
||
|
||
### 13.5 Ce que le SVG donne et que le canvas ferait payer
|
||
|
||
- **La désignation de ce qui est sous le pointeur**, pour la sélection : le
|
||
navigateur répond exactement, sans une ligne de code. En canvas il faut la
|
||
réécrire et la maintenir en accord avec le dessin — une divergence donne des
|
||
clics qui tombent à côté sans que rien ne le signale.
|
||
- **L'épreuve par le style calculé** (§ 14.2). Sur un canvas il n'y a pas de
|
||
style calculé : on en serait réduit à comparer des pixels.
|
||
- **Le contraste et les deux thèmes** : en SVG les couleurs sont des propriétés
|
||
CSS, gouvernées par un seul fichier de jetons.
|
||
- **L'impression vectorielle** : le plan s'imprime en SVG, à la résolution de
|
||
l'imprimante (§ 11.7), là où un canvas exigerait un second chemin de rendu à
|
||
haute densité.
|
||
- **L'accessibilité** : `tabindex`, `role` et `aria-label` fonctionnent sur les
|
||
éléments SVG. Un canvas est un rectangle opaque pour un lecteur d'écran.
|
||
- **L'inspection** : le plan s'ouvre dans les outils du navigateur, élément par
|
||
élément.
|
||
|
||
Ce que le canvas gagnerait — des dizaines de milliers d'objets — ne se présente
|
||
pas : une salle plafonne à quelques dizaines de tables.
|
||
|
||
---
|
||
|
||
## 14. Exigences de développement
|
||
|
||
Chacune répond à une façon connue de se tromper.
|
||
|
||
### 14.1 Les constantes de géométrie se mesurent
|
||
|
||
Toute dimension qui dépend du rendu du texte se **mesure dans un navigateur**,
|
||
sur la police réellement rendue. Jamais déduite, jamais posée de tête.
|
||
|
||
Un seuil en nombre de caractères se cale sur le **glyphe le plus large** de
|
||
l'alphabet visé, pas sur une moyenne. Un seuil moyen laisse déborder les noms
|
||
écrits en lettres larges, **silencieusement**, par-dessus la ligne suivante.
|
||
|
||
Chaque constante mesurée porte en commentaire **ce qu'elle mesure, pourquoi, et
|
||
sur quelle configuration elle a été relevée**, afin que personne ne la
|
||
« corrige » plus tard par le calcul.
|
||
|
||
**Chaque seuil dit « mesuré » a un propriétaire nommé** — le banc de mesure du
|
||
§ 19.10, le niveau `node`, ou le **niveau manuel**. Un seuil sans propriétaire
|
||
reste à sa valeur de départ pour toujours. Deux mesures sortent du banc parce
|
||
qu'un banc de navigateur ne les prend pas : le **décalage des imprimantes
|
||
visées** et leur **marge non imprimable**, qui bornent séparément la zone de
|
||
silence du badge sur un bord de coupe et sur un bord de feuille (§ 11.3). Elles
|
||
appartiennent au **niveau manuel, avant livraison** ; tant qu'elles n'ont pas eu
|
||
lieu, la constante **déclare en commentaire qu'elle est un défaut de travail**.
|
||
|
||
### 14.2 Un test qui ne peut pas échouer ne prouve rien
|
||
|
||
- Tout test ajouté doit être **montré en train d'échouer** sur le code d'avant.
|
||
- Un test de rendu lit le **style calculé**, pas la présence d'une classe : une
|
||
classe peut se poser sur un élément qui ne peint rien.
|
||
- Un test qui parcourt un ensemble **refuse de passer quand cet ensemble est
|
||
vide** : « zéro fichier examiné » n'est pas « zéro problème ».
|
||
|
||
La clause de l'ensemble vide ne vaut pas que pour les balayages d'arborescence.
|
||
Elle vaut pour le **vérificateur d'invariants** (§ 14.12) — un vérificateur qui
|
||
rend toujours la liste vide rend verts tous les tests de propriété qui s'appuient
|
||
sur lui ; pour les **boucles de propriété**, qui comptent les configurations
|
||
acceptées et échouent sous un plancher, parce qu'un générateur refusant les
|
||
configurations difficiles rend la propriété vraie sans rien éprouver ; et pour le
|
||
**choix des comptes d'épreuve**. Un accord incrémental éprouvé à dix mouvements,
|
||
ou une pagination éprouvée sur le seul compte où la troncature et l'arrondi par
|
||
excès coïncident, sont des tests qui ne peuvent pas échouer.
|
||
|
||
### 14.3 Les règles se gardent, pas seulement les cas
|
||
|
||
Quand un défaut est corrigé, le test qui l'accompagne épingle la **règle**
|
||
plutôt que le cas. Une valeur remise à la main sera défaite par la main
|
||
suivante ; une règle qui fait échouer la construction tient.
|
||
|
||
### 14.4 Les niveaux d'épreuve
|
||
|
||
| niveau | ce qu'il couvre | quand |
|
||
|---|---|---|
|
||
| **`node`, série surveillée** | moteur, géométrie, CSV, indicateurs et plafonds — des fonctions pures ; cohérence des versions (§ 18.5) ; appariement captures/manifeste (§ 19.7) ; aller-retour et canonicité du stockage sur la petite démonstration ; **une** production de planche de badges avec contrôle de géométrie et de couche de texte ; grille de propriétés sur petites instances | **à chaque modification**, quelques secondes (§ 14.14) |
|
||
| **`node:long`** | accord de l'incrémental à 1, 10, 100, 1 000 et 10 000 mouvements ; rejeu du générateur et empreinte ; pagination des badges sur ses cinq comptes A4, ses quatre comptes Lettre et ses deux impositions ; reconstruction de tous les instants du journal ; aller-retour sur la grande démonstration ; coût d'enregistrement ; sonde d'écriture sur système de fichiers réel ; grande grille de propriétés | avant un commit, et à la construction |
|
||
| **navigateur** | ce que l'interface dessine, ce que les gestes produisent, les coordonnées ; le scénario de pilotage (§ 19.3) ; le banc de mesure (§ 19.10) ; les épreuves de style calculé | avant chaque livraison |
|
||
| **manuel** | une soirée complète, de la saisie à l'impression ; le décalage des imprimantes (§ 11.3) ; `pointercancel` et `touch-action` (§ 7.3) | avant chaque livraison au client |
|
||
| **sa propre commande** | l'audit par mutation (§ 14.13) | avant une livraison, sous aucun budget |
|
||
|
||
**La commande surveillée ne lance que le projet `node`.** Le lanceur en porte
|
||
deux (§ 14.8) ; lancés ensemble, ils démarrent un navigateur à chaque
|
||
modification, et le niveau navigateur reste à « avant chaque livraison ».
|
||
|
||
**Le partage entre les deux séries `node` est écrit avant la première mesure**,
|
||
et non après. Trois familles tombent naturellement dans la série surveillée sans
|
||
que personne ne les compte — la **production de PDF**, qui charge un
|
||
sous-ensemble de police et analyse sa sortie ; les **épreuves de stockage** à
|
||
système de fichiers réel ; la **grille de propriétés**, qui appelle un générateur
|
||
capable de construire 260 participants et un tour témoin. Chacune a l'ordre de
|
||
grandeur que le § 14.14 désigne comme fatal, et la réponse naturelle au
|
||
dépassement — relever le budget — supprime la propriété qu'il achète.
|
||
|
||
**jsdom ne sait pas éprouver la géométrie SVG** : il n'implémente ni `getBBox`,
|
||
ni `getScreenCTM`, ni la mise en page du texte. Tout ce qui touche à une
|
||
dimension réelle exige un vrai moteur de rendu. Croire l'inverse fait sauter
|
||
l'étage intermédiaire et laisse partir les défauts de coordonnées.
|
||
|
||
### 14.5 Accessibilité et thèmes
|
||
|
||
Le texte tient **4,5:1** de contraste, les marques porteuses d'information
|
||
**3:1**. Chaque couleur est accompagnée du ratio **mesuré sur la couleur relue du
|
||
rendu**, non sur le jeton source. Les deux thèmes sont éprouvés **par rendu**,
|
||
jamais seulement par calcul, et le tableau des ratios est **engendré pour les
|
||
deux** à partir des styles relevés au pilotage (§ 19.5), jamais recopié.
|
||
|
||
**Le logiciel n'applique aucune opacité partielle, aucun filtre et aucun mode de
|
||
fusion au texte ni aux marques porteuses d'information.** Le style calculé rend
|
||
la couleur **résolue** d'un élément, non le pixel composé : sous une opacité
|
||
partielle, la couleur relue diffère de la couleur peinte et le tableau des ratios
|
||
mentirait avec l'autorité d'une mesure. Sans opacité ni filtre, les deux
|
||
coïncident par construction.
|
||
|
||
### 14.6 La traduction, posée dès le départ
|
||
|
||
Une seule langue est livrée, mais **aucune chaîne visible n'est écrite en dur
|
||
dans un composant**. Le coût est nul aujourd'hui ; le retrouver plus tard suppose
|
||
de relire toute l'interface.
|
||
|
||
**La règle se garde par deux moyens, et aucun n'est le scénario de captures**
|
||
(§ 19.11) : comparer le texte affiché au texte attendu ne distingue pas une
|
||
chaîne venue de la table de traduction d'une chaîne écrite dans le composant —
|
||
les deux s'affichent à l'identique. Les deux gardes sont un **balayage des
|
||
sources** sous `node`, qui refuse une chaîne visible en dur et **échoue sur un
|
||
balayage vide** (§ 14.2), et une **exécution en langue témoin** où chaque chaîne
|
||
traduite est décorée d'une marque : tout ce qui apparaît à l'écran sans sa marque
|
||
est écrit en dur.
|
||
|
||
### 14.7 Le déterminisme
|
||
|
||
Aucune source non reproductible — `Math.random`, `Date.now`, `performance.now`,
|
||
`crypto.getRandomValues` — dans le **moteur**, dans le **générateur de
|
||
démonstrations**, ni dans le **stockage** et l'**analyseur CSV**, qui reçoivent
|
||
l'horloge et l'aléa en paramètre. Un test de l'arborescence le refuse, **et
|
||
échoue si son balayage ne trouve aucun fichier**. Son périmètre est nommé :
|
||
`src/moteur`, `src/demo`, `src/stockage` et `src/csv`, à l'exclusion du code
|
||
d'épreuve, où un tirage sert légitimement (§ 14.12). L'interdit ne porte pas sur
|
||
le reste de l'application : l'application lit l'horloge pour horodater les
|
||
entrées de l'historique, que le stockage écrit telles qu'il les reçoit, et le
|
||
banc de mesure du § 19.10 relève des temps par image.
|
||
|
||
**Ce n'est pas une règle d'hygiène.** L'interdit de `performance.now` dans le
|
||
moteur est ce qui rend vraie la phrase « à graine et entrée égales, placement
|
||
identique » : une recherche bornée par une durée rendrait le placement dépendant
|
||
de la vitesse de la machine, et le § 19.4 neutraliserait alors toutes ses sources
|
||
de variation sauf la seule qui compte. La règle d'arrêt de la recherche est pour
|
||
cette raison un **compte**, jamais une durée (§ 5.10).
|
||
|
||
**La version est une constante engendrée à la construction**, jamais une lecture
|
||
d'horloge à l'exécution (§ 18.5) : le moteur et le générateur ne la lisent pas.
|
||
|
||
|
||
### 14.8 L'outillage des épreuves
|
||
|
||
**Un seul lanceur, deux projets : Vitest.** Le niveau `node` et le niveau
|
||
navigateur du § 14.4 sont deux configurations du même lanceur, pas deux outils.
|
||
|
||
| projet | environnement | ce qu'il inclut | greffon Svelte |
|
||
|---|---|---|---|
|
||
| `node` | `node`, aucun DOM | `src/moteur`, `src/geometrie`, `src/stockage`, `src/csv`, `src/demo`, `src/pdf` | **absent** |
|
||
| `navigateur` | un vrai moteur de rendu, piloté par le mode navigateur de Vitest | `src/interface`, les épreuves de coordonnées et de style calculé | présent |
|
||
|
||
**Pourquoi Vitest.** Il lit la **configuration Vite qui construit le livrable** : un module importé par un test se résout exactement comme un module
|
||
importé par l'application. Deux chaînes de résolution — l'une pour construire,
|
||
l'autre pour éprouver — sont le mode de défaillance du § 13.2 transposé à
|
||
l'outillage : le test passe sur un fichier que l'application ne charge jamais, un
|
||
alias diverge, et rien ne le signale. Le lanceur observe en outre le **graphe de
|
||
dépendances**, donc une modification du moteur ne rejoue que les tests du moteur,
|
||
et son mode navigateur offre **la même API d'assertion** au niveau 2, de sorte
|
||
que l'oracle de géométrie du § 7.2 s'écrit une fois.
|
||
|
||
**Les trois durées sont des cibles, mesurées la première semaine.** Le démarrage
|
||
à froid, la relance sous surveillance et la série complète se mesurent sur un
|
||
squelette, au même titre que la publication du dossier d'origine par le lanceur
|
||
portable (§ 8.6) et la construction depuis Linux (§ 16). Ce qui est tranché ici
|
||
n'est pas le chiffre mais **lequel gouverne** : le démarrage à froid se paie une
|
||
fois par séance, la relance sous surveillance se paie à chaque geste, et c'est
|
||
elle que le budget du § 14.14 borne.
|
||
|
||
**L'issue de secours est une règle d'écriture, et elle passe par un module.** Les
|
||
tests du projet `node` n'emploient que les assertions de `node:assert/strict` —
|
||
jamais `expect`, jamais un comparateur propre à Vitest. Ils importent `test` et
|
||
`describe` d'un **unique module local**, `test/lanceur.js`, qui réexporte ceux du
|
||
lanceur en place. Les fonctions de `node:test` ne sont pas globales et celles de
|
||
Vitest ne portent pas le même chemin d'import : sans cette indirection, « tourner tels quels sous `node --test` » est
|
||
faux, et le repli exigerait de retoucher chaque fichier le jour où il presse.
|
||
Deux conditions le rendent réel et sont tenues par le projet `node` : ses modules
|
||
s'importent par **chemins relatifs à extension explicite**, sans alias ni syntaxe
|
||
propre à Vite. Le prix est réel — les écarts affichés par
|
||
`assert.deepEqual` sont plus pauvres — et il s'accepte parce qu'une dépendance
|
||
qu'on ne peut pas quitter finit par dicter l'architecture.
|
||
|
||
**Ce qui est écarté, et pourquoi.**
|
||
|
||
| écarté | raison |
|
||
|---|---|
|
||
| **Jest** | exige sa propre chaîne de transformation, parallèle à celle de Vite — la seconde résolution que l'on veut éviter |
|
||
| **Mocha + Chai + Sinon + nyc** | quatre choix à maintenir et à accorder pour ce qu'un lanceur rend d'emblée |
|
||
| **Karma, Jasmine, QUnit** | conçus pour un monde sans étape de construction ; ils rajoutent un second serveur devant Vite |
|
||
| **`node --test` seul** | ne résout ni les alias de Vite ni un composant ; pas de graphe de dépendances sous surveillance. Reste le **repli nommé**, et c'est pour lui que la règle d'assertion et `test/lanceur.js` existent |
|
||
| **jsdom**, sous quelque lanceur que ce soit | le § 14.4 l'écarte déjà pour la géométrie ; elle l'est aussi pour le style — sans mise en page, le style calculé ne rend pas ce qui est peint, et l'épreuve du § 14.2 devient une formalité toujours verte |
|
||
| **un pilote de navigateur distinct pour le niveau 2** | deux pilotes signifient deux façons de décrire un geste de pointeur, et le § 7.3 est précisément l'endroit où cette divergence coûte cher |
|
||
|
||
Pour les composants, **aucun outil supplémentaire** : le projet `navigateur`
|
||
monte le composant dans un vrai moteur de rendu. Un composant ne contient que du
|
||
câblage (§ 13.3) ; ce qu'il y a à vérifier est ce qui est **peint**.
|
||
|
||
### 14.9 Où vivent les tests
|
||
|
||
Le test vit **à côté du module** — `plafond.js` et `plafond.test.js` dans le même
|
||
répertoire. Un arbre miroir éloigné ne s'ouvre pas quand on modifie le module, et
|
||
le test qui n'est pas sous les yeux n'est pas étendu. Les tests du niveau
|
||
navigateur portent le suffixe `.navigateur.test.js`, qui est aussi ce qui les
|
||
range dans leur projet.
|
||
|
||
Les données d'épreuve vivent dans `test/fixtures/`, les fichiers d'exemple livrés
|
||
restant ceux du § 10.3 — une donnée d'épreuve qui double un fichier livré dérive
|
||
de lui. **Les noms inventés d'une donnée d'épreuve sont disjoints du réservoir du
|
||
générateur** : le balayage du § 15.6 refuse qu'un fragment forgé apparaisse hors
|
||
des listes du générateur, des fichiers de démonstration et des CSV d'exemple, et
|
||
une donnée d'épreuve qui emprunte une racine d'organisation plausible le fait
|
||
échouer. Un test vérifie cette disjonction, faute de quoi la garde du § 15.6
|
||
tombe au premier exemple écrit de bonne foi.
|
||
|
||
**La co-localisation se garde.** Un test de la construction affirme que le paquet
|
||
livré ne contient **aucun fichier de test ni aucun import du lanceur**. Le § 14.15
|
||
pose que la suite n'est pas livrée ; avec des tests rangés dans `src/`, cette
|
||
propriété tient par la seule analyse statique de Vite, c'est-à-dire par
|
||
chance, jusqu'au jour où un module de production importe une
|
||
donnée d'épreuve « pour un cas par défaut ».
|
||
|
||
### 14.10 La carte de ce qui s'éprouve
|
||
|
||
| module (§ 13.4) | niveau | ce qui s'y éprouve |
|
||
|---|---|---|
|
||
| moteur | `node` | indicateurs, plafonds et borne, diagnostic, invariants d'un plan, accord de l'incrémental et du recalcul |
|
||
| géométrie | `node` + navigateur | conversions, ajustement, bornes de zoom, désignation d'un siège ; l'oracle du moteur de rendu au niveau 2 ; la découpe du plan en pages (§ 11.7) |
|
||
| stockage | `node` | sérialisation canonique, équivalence geste/entrée/état, reprise après panne, dérivation des noms de fichiers, forme positionnelle (§ 8.9) |
|
||
| analyseur CSV | `node` | encodage, séparateur, association des colonnes, réconciliation des appartenances, lignes refusées, aller-retour |
|
||
| générateur de démonstrations | `node` | vecteur du générateur pseudo-aléatoire, empreinte des fichiers livrés, refus, noms forgés |
|
||
| PDF | `node` | couche de texte, pagination, police embarquée et table `ToUnicode`, géométrie de la planche, reproductibilité |
|
||
| interface | navigateur | ce que les gestes produisent, le style calculé, les trois invariants du § 7.2 |
|
||
|
||
#### Le moteur
|
||
|
||
**Un tour où une table est vide ne produit aucune rencontre.** Un plan dont une
|
||
table n'accueille personne au tour 3, et une autre où elle accueille une seule
|
||
personne : dans les deux cas, zéro paire ajoutée. Il refuse l'énumération qui
|
||
compte la paire (p, p) et crédite un convive isolé d'une rencontre avec lui-même
|
||
— défaut qui ne se voit sur aucun total, parce qu'il ajoute un au bon endroit.
|
||
|
||
**Le plafond d'un ancré sur quatre tables de 5, un ancré chacune, cinq tours, vaut
|
||
16 ; celui d'un mobile de la même instance vaut 19.** Les deux valeurs sont dans
|
||
le même test, parce que c'est leur écart qui est l'objet. Il refuse la suppression
|
||
du terme `min(n_p, …)`, qui se lit comme une borne redondante et dont l'absence
|
||
porte l'ancré à 19, affichant à perpétuité un animateur « à 84 % de son plafond »
|
||
(§ 5.5).
|
||
|
||
**Les retours imposés sont identiques pour toutes les propositions d'une même
|
||
configuration.** Sur la grande démonstration, chacune annonce 99 retours imposés.
|
||
Ce n'est pas le seul chiffre qu'elles partagent, mais c'est le seul qui, additionné
|
||
aux retours choisis, **noierait une quantité variable de quelques unités dans une
|
||
constante de 99**, à une résolution où l'opérateur ne lit plus l'écart. Le test
|
||
affirme les deux comptes **séparés et chacun à sa valeur** (§ 5.4).
|
||
|
||
**Une population vide rend « — », jamais zéro.** Il refuse `somme / 0` affiché en
|
||
`0`, et refuse un minimum d'ensemble vide rendu en `Infinity` que la mise en forme
|
||
convertit en tiret par accident plutôt que par décision.
|
||
|
||
**L'appartenance d'un ancré voyage dans l'instance réduite.** Une table porte un
|
||
ancré d'une appartenance inventée, un mobile de la même appartenance existe, la
|
||
séparation est active : le plan rendu signale la collision ou l'évite, il ne
|
||
l'ignore pas. Il refuse la réduction qui n'annote pas les tables, laquelle assoit
|
||
un invité à côté d'un collègue animateur aux quatre tours **sans qu'aucun
|
||
indicateur ne s'en aperçoive** (§ 5.2).
|
||
|
||
**L'incrémental et le recalcul complet s'accordent, et le test dit à partir de
|
||
quand ils cessent de s'accorder.** Sur chaque configuration de démonstration, une
|
||
suite déterministe d'échanges, avec comparaison terme à terme de A(p) et de |F(p)|
|
||
à **1, 10, 100, 1 000 et 10 000 mouvements** — non au seul terme. Il refuse la mise
|
||
à jour qui oublie de décrémenter un compteur sur la table quittée. **Le nombre de
|
||
mouvements est l'exigence du test** : un test à dix mouvements est le test qui ne
|
||
peut pas échouer du § 14.2. Cette épreuve est dans la série `node:long`.
|
||
|
||
#### La géométrie
|
||
|
||
**Le plan vide donne `k_ajusté = 1 px/cm`, pas `Infinity`.** Il refuse la division
|
||
par zéro du § 7.2, que le `transform` accepte sans rien dessiner et sans rien
|
||
signaler.
|
||
|
||
**La borne basse du zoom est relative, et le test le prouve en chiffres.** Sur une
|
||
fenêtre de 1 366 × 700 et un plan de 36 m, `k_ajusté ≈ 0,19 px/cm` : le test
|
||
affirme **`k_min < 0,2`**, et affirme `k_min = 0,5 × k_ajusté` sur deux plans
|
||
d'encombrements très différents. Affirmer `k_min ≤ k_ajusté` serait inutile : avec
|
||
un facteur de 0,5 la relation est vraie par construction, y compris si un plancher
|
||
absolu a été ajouté — c'est le test qui ne peut pas échouer du § 14.2. Ce qui doit
|
||
échouer, c'est le plancher absolu de 0,2.
|
||
|
||
**Le siège désigné est celui que le dessin a posé, dans les deux régimes de zoom.**
|
||
Le test demande au module la position de chaque chaise, puis lui redemande quel
|
||
siège contient ce point ; puis vérifie qu'un point du vide entre deux chaises ne
|
||
désigne rien. Il le refait **sous le seuil du § 7.3**, où les sièges cessent d'être
|
||
des cibles : sans ce second passage, le basculement n'est éprouvé nulle part.
|
||
|
||
**La découpe du plan en pages est une fonction pure** (§ 11.7) : elle rend N
|
||
cadrages contigus et leurs cartouches, et s'éprouve sous `node`. Un navigateur ne
|
||
sait pas fragmenter un SVG ; c'est du code qu'il faut éprouver, pas une règle de
|
||
style.
|
||
|
||
**Au niveau navigateur**, la fonction pure est confrontée à
|
||
`getScreenCTM().inverse()` sur un rendu réel, à plusieurs `k` et après défilement
|
||
de la page. **Les trois invariants du § 7.2 ont chacun leur test** : que le `<svg>`
|
||
racine ne porte pas d'attribut `viewBox` ; que son style calculé donne une bordure
|
||
et un remplissage intérieur nuls — `getBoundingClientRect` les inclut, donc
|
||
l'origine du contenu s'en décale ; qu'aucun ancêtre, ni lui-même, ne porte de
|
||
transformation CSS. Il refuse la feuille de style qui ajoute un jour
|
||
`transform: translateZ(0)` « pour la fluidité » : le plan répondrait faux partout,
|
||
et la cause serait cherchée dans le module de géométrie.
|
||
|
||
#### Le stockage
|
||
|
||
**Le système de fichiers est un paramètre, pas un import** (§ 13.4). Sans
|
||
l'implémentation injectée, ni le test du renommage ni les 100 % de branches du
|
||
§ 14.13 ne s'écrivent, et les chemins de reprise restent le code le moins éprouvé
|
||
du logiciel alors qu'ils sont ceux qui perdent une soirée.
|
||
|
||
**Deux sérialisations du même état sont identiques octet pour octet**, y compris
|
||
quand l'état a été construit dans deux ordres d'insertion différents, et y compris
|
||
quand les sièges ne sont pas attribués — c'est là que la règle du tri croissant du
|
||
§ 8.9 s'éprouve. Il refuse l'ordre de clés hérité de l'itération d'objet et
|
||
l'horodatage glissé dans la charge utile.
|
||
|
||
**Chaque instant du journal se reconstruit.** Après une suite de commandes
|
||
dépassant deux frontières d'instantané, le test reconstruit **tous** les instants
|
||
et compare chacun à l'état écrit à ce moment. Il refuse l'application de correctifs
|
||
juste quarante-neuf fois et fausse au passage de l'instantané — défaut qui
|
||
n'apparaît qu'au bout d'une longue séance, c'est-à-dire la veille de l'événement.
|
||
Série `node:long`.
|
||
|
||
**Un journal en avance d'une entrée se rattrape.** Le test écrit la ligne de
|
||
journal et n'écrit pas le fichier d'état : l'ouverture compare les révisions et
|
||
retrouve le geste. Il refuse la reprise fondée sur la date de modification des
|
||
fichiers, que recopier un dossier suffit à fausser.
|
||
|
||
**Une ligne illisible arrête la lecture et annonce le compte.** Journal de cent
|
||
entrées corrompu à la trente-septième : trente-six entrées retenues,
|
||
soixante-quatre annoncées écartées.
|
||
|
||
**La dérivation des noms refuse `con` comme `CON`**, et borne le plus long
|
||
chemin que l'événement écrit, corbeille et écriture atomique comprises (§ 8.6) :
|
||
le test recompose ce chemin en entier, et échoue sur une borne posée sur
|
||
`.gtt.json`, neuf caractères, sur le seul `.gtt-journal.jsonl`, dix-huit, ou qui
|
||
oublie la corbeille ou l'écriture atomique. Elle reçoit la racine en paramètre et
|
||
son test l'exerce sur les **deux** dossiers de travail possibles (§ 8.6).
|
||
|
||
**Un renommage atomique qui échoue ne détruit pas la cible.** L'implémentation
|
||
d'épreuve échoue au renommage : la cible conserve son contenu antérieur, et le
|
||
logiciel nomme le fichier. Il refuse l'écriture qui tronque la cible avant
|
||
d'écrire, c'est-à-dire la variante qui perd une soirée quand un agent de
|
||
synchronisation tient le fichier ouvert.
|
||
|
||
**La forme positionnelle du § 8.9 est abîmée délibérément** — une longueur fausse,
|
||
un identifiant dupliqué, un identifiant inconnu, un identifiant manquant sans
|
||
entrée de réserve — et le logiciel doit nommer chacune et écarter la **seule**
|
||
proposition fautive. Un second test **périme** délibérément : il exclut une
|
||
personne, supprime une table, réduit une capacité, et exige que les propositions
|
||
touchées soient **signalées comme dérive et conservées**, jamais écartées.
|
||
|
||
**Le coût d'un enregistrement complet se mesure** sur la grande démonstration et
|
||
échoue au-delà du seuil du § 8.2. Il écrit sur un système de fichiers réel : série
|
||
`node:long`, comme la **sonde d'écriture** du § 8.6, qui ne s'éprouve utilement
|
||
contre un système de fichiers réel qu'au moins une fois.
|
||
|
||
#### L'analyseur CSV
|
||
|
||
**Un fichier séparé par des virgules n'est pas lu comme séparé par
|
||
point-virgule.** Le test affirme que le candidat `;` est écarté parce qu'il donne
|
||
**un** champ. Il refuse la règle fondée sur la seule constance, qui importerait
|
||
chaque ligne entière dans la colonne `nom` (§ 10.1).
|
||
|
||
**Un contenu latin encodé en UTF-16 est refusé globalement, avec son remède.** Il
|
||
refuse l'ordre à trois temps, dont le mode de défaillance est muet.
|
||
|
||
**Le collage en bloc et l'import du même contenu rendent les mêmes participants.**
|
||
Le test fournit **le même texte décodé** par les deux chemins : un collage ne porte
|
||
pas d'octets, et les quatre temps de détection d'encodage ne concernent que
|
||
l'import. Il refuse le second analyseur « simplifié » du § 10.2.
|
||
|
||
**Le rapport d'import nomme chaque groupe d'orthographes fondues.** L'assertion
|
||
porte **sur le rapport**, pas seulement sur le résultat fusionné. Il refuse la
|
||
fusion silencieuse, qui gonfle G, gonfle A_max avec lui, et qu'aucun indicateur ne
|
||
signale.
|
||
|
||
**Une valeur d'`exclu` hors de la liste fermée refuse la ligne, et la ligne revient
|
||
dans le CSV des refus, lequel se réimporte tel quel.** Le test boucle : import,
|
||
export des refus, correction d'un champ, réimport. Il refuse le refus global sur
|
||
une faute unique, et refuse le fichier de refus qu'il faudrait nettoyer à la main.
|
||
|
||
#### Le générateur de démonstrations
|
||
|
||
**Le vecteur du générateur pseudo-aléatoire est rejoué, et il vient de la
|
||
publication de référence de l'algorithme.** Un vecteur produit en exécutant
|
||
l'implémentation ne prouve rien d'autre que sa stabilité : il fige la transition, y
|
||
compris fausse. Pris hors du code, il arbitre. Si l'algorithme retenu ne publie pas
|
||
de vecteur, le test change de nature et le dit : il devient un **témoin de
|
||
non-régression**, pas un oracle (§ 14.11).
|
||
|
||
**Le profil d'appartenances attendu est lu dans le fichier livré**, jamais écrit à
|
||
la main (§ 15.2).
|
||
|
||
**Le générateur refuse quatre configurations**, chacune son test : zéro
|
||
participant, zéro table, une graine dont aucun tour témoin ne se construit, un
|
||
compte de groupes dépassant le réservoir de noms.
|
||
|
||
**La petite démonstration atteint 8 pour les douze participants, zéro paire
|
||
répétée, zéro collision**, et le logiciel écrit « minimum atteint ». Le test mesure
|
||
d'abord le plan écrit au § 15.3 — il éprouve les indicateurs contre un oracle —
|
||
puis lance la recherche et affirme qu'elle **atteint le même plafond**, sans jamais
|
||
comparer le plan obtenu au plan écrit (§ 15.5, point 6).
|
||
|
||
**La variante 5, 4, 3 annonce au moins quatre collisions cumulées, et au moins une
|
||
paire distincte.** Les deux chiffres sont différents et le test les sépare : cinq
|
||
personnes ne tiennent pas sur quatre tables sans que deux se retrouvent, donc **au
|
||
moins une collision par tour, quatre sur les quatre tours** — mais l'indicateur
|
||
« paires de même appartenance » compte des **paires distinctes**, et un plan qui
|
||
garde les deux mêmes personnes ensemble aux quatre tours n'en affiche qu'**une**.
|
||
Affirmer « aucune proposition ne descend sous quatre » sur cet indicateur ferait
|
||
échouer le test sur un plan légitime. Un second test tient l'autre bout : le
|
||
classement par défaut du § 5.7 **préfère le plan qui répartit**, donc celui dont
|
||
l'**excédent** est nul et qui porte quatre paires distinctes, et non celui qui
|
||
concentre ses quatre collisions sur une seule paire. Le test compare deux plans
|
||
construits à la main — l'un réparti, l'autre concentré — et affirme l'ordre ; il
|
||
est l'unique garde contre une réécriture du classement qui le remettrait à
|
||
l'envers (§ 5.4).
|
||
|
||
#### Le PDF
|
||
|
||
**Un nom portant « Ôjgq » et « Æ » se retrouve caractère par caractère dans la
|
||
couche de texte.** Il refuse le sous-ensemble de police qui laisse tomber un glyphe
|
||
en silence, et la police sans table `ToUnicode`, dont les accents s'impriment juste
|
||
et s'extraient en charabia (§ 11.1).
|
||
|
||
**La géométrie du quatre-par-page est contrainte, et le test l'affirme telle
|
||
qu'elle est** : quatre cellules de 105 × 148 mm exactement, **trois** filets de
|
||
coupe — une verticale à 105 mm, une horizontale à 148 mm, une horizontale à 296 mm
|
||
qui retire la bande perdue —, et aucun contenu de badge à moins de la zone de
|
||
silence d'un axe de coupe ou d'un bord de feuille. Ce ne sont pas deux médianes :
|
||
148 n'est pas la médiane de 297.
|
||
|
||
**La pagination s'éprouve sur les comptes qui séparent les deux arrondis, et sur
|
||
les deux impositions** : `n = 1, 4, 5, 259, 261` en A4 — jamais 260, où `260 / 4`
|
||
tombe juste et où un calcul qui tronque rend la même valeur — et
|
||
`n = 1, 2, 3, 257` en Lettre. Le compte est celui des **participants non exclus**.
|
||
Série `node:long`.
|
||
|
||
**Deux exports du même placement produisent la même couche de texte.** Il refuse
|
||
l'horodatage d'export glissé dans le pied de page à la place de celui du placement.
|
||
L'assertion porte sur la couche de texte et non sur les octets du fichier, que la
|
||
date de création et l'identifiant de document d'une bibliothèque rendent différents
|
||
sans que rien ne soit faux.
|
||
|
||
**Le filigrane BROUILLON se lit dans la couche de texte** d'un plan ni retenu ni
|
||
bloqué. Il refuse le filigrane dessiné en image, que personne ne vérifie plus.
|
||
|
||
**Le niveau `node` est une garde structurelle, à une condition nommée.** Le DOM
|
||
n'existe pas, donc un générateur de PDF qui lirait l'écran échoue dès que le test
|
||
l'exécute — la garde n'est pas l'environnement seul, elle est l'environnement
|
||
**plus** un test qui parcourt le chemin de production ; sans ce test, rien ne lève.
|
||
Et elle ne tient que parce que les mesures de texte du badge emploient les
|
||
**métriques de la police embarquée** (§ 11.1) : un appel vivant à
|
||
`getComputedTextLength` ramènerait le DOM dans le module PDF.
|
||
|
||
### 14.11 Les oracles, et ce qui n'en est pas
|
||
|
||
Un **oracle** est une source de la réponse attendue **produite hors du code
|
||
éprouvé**. Un **témoin de non-régression** est une valeur produite par le code
|
||
lui-même, figée. Les deux sont utiles, ils ne prouvent pas la même chose, et les
|
||
confondre fait croire qu'une suite arbitre là où elle ne fait que conserver.
|
||
|
||
| source | nature | ce qu'elle arbitre |
|
||
|---|---|---|
|
||
| le plan parfait de la petite démonstration (§ 15.3), vérifié par énumération | **oracle** | les indicateurs |
|
||
| `getScreenCTM().inverse()` du moteur de rendu (§ 7.2) | **oracle** | la conversion de coordonnées |
|
||
| une énumération exhaustive sur une instance minuscule | **oracle** | la formule du plafond, le diagnostic |
|
||
| le vecteur du générateur pseudo-aléatoire | **oracle** s'il vient de la publication de l'algorithme, **témoin** s'il a été produit par l'implémentation | la transition du tirage |
|
||
| les fichiers de démonstration livrés et leur empreinte (§ 15.5) | **témoin** — ils sortent du générateur qu'ils éprouvent | que le générateur n'a pas changé |
|
||
|
||
Le témoin garde sa valeur entière : le § 15.5 veut que la régénération soit un
|
||
geste conscient, et un témoin fait exactement cela. Ce qu'il ne fait pas, c'est
|
||
dire que le générateur a raison.
|
||
|
||
**Ce qu'un oracle apporte qu'une assertion écrite à la main n'apporte pas.**
|
||
|
||
1. **Il ne partage pas l'erreur de lecture de l'auteur.** Une valeur attendue tapée
|
||
dans un test est calculée par la tête qui a écrit le code, sur la même lecture
|
||
de la spécification. Si cette lecture est fausse, le test confirme la faute. Une
|
||
énumération exhaustive sur quatre tables de 5 contredit la formule sans avoir
|
||
rien lu.
|
||
2. **Il couvre ce que personne ne penserait à écrire.** Le moteur de rendu donne la
|
||
matrice exacte à **tout** `k`, non aux trois valeurs que l'auteur aurait
|
||
choisies — et ce sont les valeurs non choisies qui cassent.
|
||
3. **Il survit à un changement légitime.** Quand la loi de distribution change, le
|
||
fichier livré dit **ce qui a changé** ; un effectif écrit dans le test est remis
|
||
à la main, et la remise à la main est l'endroit où l'on cesse de vérifier.
|
||
|
||
**Un oracle tiré du code éprouvé n'est pas un oracle.** L'aller-retour CSV du
|
||
§ 10.2 en est l'exemple : importer puis exporter prouve que les deux moitiés
|
||
s'accordent, jamais qu'elles ont raison — le mode de défaillance du § 13.2. Il
|
||
reste utile, et il est **toujours accompagné** d'au moins une donnée d'épreuve dont
|
||
le contenu attendu est écrit à la main, ligne par ligne : le fichier de cas limites
|
||
du § 10.3.
|
||
|
||
**Le recalcul complet est l'oracle de l'incrémental**, à la condition du § 13.2 :
|
||
le recalcul est la mesure, l'incrémental ne vit que dans la boucle de recherche, et
|
||
le test les lie à la fin de chaque recherche.
|
||
|
||
### 14.12 Les invariants et les tests de propriété
|
||
|
||
**Le vérificateur d'invariants est du code de l'application, pas du code de test.**
|
||
`verifier_invariants(plan, configuration)` rend la liste des violations :
|
||
|
||
- chaque participant non exclu est assis à **exactement une** table par tour ;
|
||
- aucun participant n'occupe deux places sur un même tour — distinct du précédent,
|
||
qui ne dit rien de deux sièges à la **même** table ;
|
||
- aucune table ne dépasse sa capacité, et aucun siège n'est hors de sa table ;
|
||
- chaque réservation est honorée, de portée « tour désigné » comme « tous les
|
||
tours » ;
|
||
- aucun participant exclu n'est placé.
|
||
|
||
Il vit dans le moteur parce que l'application l'appelle : après une génération,
|
||
après la commande qui complète un placement partiel, après un retour arrière, et
|
||
au chargement d'un fichier. **Un vérificateur appelé seulement par les tests cesse
|
||
d'être appelé** ; appelé par les commandes, il refuse au moment du geste, là où le
|
||
§ 5.9 veut les refus.
|
||
|
||
**La garde du § 12.3 est une seconde fonction, de signature différente.**
|
||
`verifier_indicateurs(mesures, plafonds_a_priori, plafonds_realises)` éprouve les
|
||
**trois** inégalités de la ligne de décomposition du § 12.10.5. Elle ne prend pas
|
||
un plan mais des mesures, et ce qu'elle refuse n'est pas un geste mais un
|
||
**dessin**. Les deux fonctions vivent dans le moteur, sous la même discipline ; les
|
||
confondre obligerait le vérificateur de plan à recevoir des indicateurs qu'il n'a
|
||
aucune raison de connaître.
|
||
|
||
**Le vérificateur est lui-même éprouvé par mutation.** Le test prend un plan
|
||
valide, déplace une personne pour qu'elle occupe deux tables au tour 2, et affirme
|
||
que le vérificateur signale **cette violation-là** et aucune autre. Une mutation
|
||
par invariant listé. Sans cela, un vérificateur qui rend toujours la liste vide
|
||
rend verts tous les tests de propriété qui s'appuient sur lui.
|
||
|
||
**Les tests de propriété valent, et s'écrivent d'abord à la main.** Le corpus
|
||
d'entrées existe déjà : le générateur de démonstrations est déterministe et sait
|
||
produire une configuration à partir d'une graine. Une boucle sur une grille de
|
||
graines et de tailles, appelant les deux vérificateurs, couvre l'essentiel sans
|
||
dépendance nouvelle.
|
||
|
||
**Une bibliothèque de propriétés se justifie à deux endroits, et seulement deux** :
|
||
la formule du plafond et la détection du séparateur. Toutes deux prennent des
|
||
entrées petites et combinatoires, toutes deux bénéficient du **rétrécissement** —
|
||
quand une configuration à 120 participants échoue, l'outil rend un contre-exemple à
|
||
trois tables, là où la réduction manuelle coûte une heure. Leurs oracles ne sont
|
||
pas de même nature et le texte ne les confond pas : le plafond s'arbitre par
|
||
**énumération exhaustive** ; le séparateur s'arbitre par **construction** — un
|
||
tableau de champs engendré, rendu avec un séparateur connu, et la détection doit le
|
||
retrouver. Ailleurs, non : une propriété sur la recherche rejouerait la partie
|
||
lente et épuiserait le budget du § 14.14.
|
||
|
||
**La bibliothèque tourne sur une graine fixée.** Une bibliothèque de propriétés
|
||
retire par défaut une graine neuve à chaque exécution : le même code échoue une
|
||
fois sur dix et passe le reste du temps. Un test capricieux se désactive, et le
|
||
désactiver coûte plus cher que le défaut qu'il annonçait. La graine est écrite dans
|
||
le dépôt ; un contre-exemple trouvé devient un test nommé à part entière.
|
||
|
||
**Le piège qu'un test de propriété pose.** Un générateur qui refuse les
|
||
configurations difficiles rend la propriété vraie sans rien éprouver. La boucle
|
||
**compte les configurations acceptées et échoue sous un plancher** (§ 14.2).
|
||
|
||
### 14.13 La couverture
|
||
|
||
**Aucun seuil global.** Un pourcentage d'ensemble se satisfait en couvrant les
|
||
modules faciles pendant que les branches qui comptent restent nues : le plafond
|
||
d'un participant partiellement fixé, la frontière d'instantané, le renommage qui
|
||
échoue. Le chiffre monte, et c'est le chiffre qui cache.
|
||
|
||
**La couverture se mesure et se publie par module**, jamais agrégée. Sa valeur est
|
||
de **désigner ce qui n'est jamais exécuté** ; c'est une liste à lire, pas une note.
|
||
|
||
**Deux seuils durs, en branches, sur deux modules.** Le module des indicateurs et
|
||
du plafond, et les chemins de reprise du stockage, tiennent **100 % de branches**.
|
||
Ils sont petits, purs — le second à la condition d'injection du § 13.4 — et chaque
|
||
branche non couverte y est un cas que l'opérateur rencontre au pire moment. Une
|
||
branche qu'on ne sait pas couvrir est soit du code mort — on le retire — soit un
|
||
cas que la spécification n'a pas prévu — on l'écrit.
|
||
|
||
**La mesure est sa propre commande**, `make couverture` : la série `node` sous
|
||
instrumentation, hors des budgets du § 14.14. Elle publie un tableau par couche
|
||
et par module, et un rapport à parcourir qui montre, ligne à ligne, ce qui n'est
|
||
jamais exécuté. Les deux seuils portent sur quatre fichiers —
|
||
`src/moteur/indicateurs.js` et `src/moteur/plafond.js`, `src/stockage/depot.js`
|
||
et `src/stockage/journal.js` — et la commande échoue en deçà. Un test de
|
||
l'arborescence refuse un seuil abaissé, un seuil global, et un seuil posé sur un
|
||
fichier absent ou hors de la mesure : l'outil tiendrait ce dernier pour atteint,
|
||
faute de branche à compter. Il refuse de même une commande qui ne mesure pas —
|
||
sans instrumentation, la série passe et aucun seuil ne se lit —, une commande
|
||
dont les arguments redéfinissent la mesure ou ce qu'elle exécute, un tableau qui
|
||
tait les modules pleins, et l'absence du rapport ligne à ligne.
|
||
|
||
**Ce qui l'empêche de devenir une case à cocher** ne vient pas de l'outil de
|
||
couverture :
|
||
|
||
1. **La clause du § 14.2** — tout test ajouté est montré en échec sur le code
|
||
d'avant. Un test qui n'assert rien ne peut pas être montré en échec. Aucun outil
|
||
ne le vérifie ; l'historique des correctifs, lui, se relit.
|
||
2. **Un audit par mutation**, périodique et non bloquant, sur le moteur seul. Un
|
||
mutant survivant sur une ligne couverte à 100 % est la preuve directe que le
|
||
test exécute sans rien affirmer. Il est **sa propre commande**, lancée avant une
|
||
livraison, avec un résultat écrit — ni dans la série surveillée, ni dans
|
||
`node:long`, parce que son coût est d'un autre ordre que les budgets du § 14.14.
|
||
Un audit que l'on tenterait de faire tenir dans un budget ne serait pas fait du
|
||
tout.
|
||
|
||
### 14.14 Le budget de temps
|
||
|
||
| cycle | budget | au-delà |
|
||
|---|---|---|
|
||
| relance sous surveillance, après une modification | **2 secondes** | le développeur commence à grouper ses modifications, et le test cesse de désigner laquelle a cassé |
|
||
| série `node` surveillée complète, à froid | **10 secondes** | le développeur change de fenêtre ; le retour de contexte coûte plus que l'épreuve, et le niveau glisse vers « avant un commit » |
|
||
|
||
**La série surveillée s'adapte au nombre de cœurs, et le budget tient sur toute
|
||
machine.** À partir de quatre cœurs, des processus complets portent la série
|
||
entière. En deçà, le lanceur n'en porterait la série que dans un seul
|
||
processus, et la relance qui suit un module de base — celui que presque toutes
|
||
les épreuves importent — dépasserait deux secondes. La série s'y joue donc en
|
||
threads sur tous les cœurs, et ses épreuves lourdes passent dans la série
|
||
`node:long`. Aucune épreuve ne disparaît : elle change de série, et ce qu'elle
|
||
éprouve ne dépend pas de la machine. Une garde refuse, dans ces séries, ce
|
||
qu'un thread refuserait et qu'un processus accepte, et le lanceur annonce à
|
||
chaque exécution le mode qu'il a retenu.
|
||
|
||
**Pourquoi ces deux valeurs.** En deçà de deux secondes, l'attention reste sur le
|
||
code et le résultat se lit comme la suite du geste. Au-delà de dix, elle part
|
||
ailleurs ; le niveau `node` cesse d'être lancé à chaque modification, et les
|
||
défauts qu'il attrapait se retrouvent par un chemin plus long, souvent au niveau
|
||
navigateur, qui ne dit plus quelle ligne les a produits. Ce sont des **budgets**,
|
||
c'est-à-dire des seuils de refus — non une prédiction de ce que la suite coûtera,
|
||
que seule la mesure du § 14.8 donnera.
|
||
|
||
**Le budget est tenable par construction.** Quelques centaines de tests sur des
|
||
fonctions pures s'exécutent en une fraction de seconde. Le temps part ailleurs :
|
||
dans le démarrage du lanceur, et dans trois ou quatre épreuves lourdes. **Dépasser
|
||
le budget n'est donc jamais le signe que la suite a grandi** ; c'est le signe qu'un
|
||
test a acquis un système de fichiers réel, un minuteur, ou une vraie recherche.
|
||
|
||
**Les épreuves lourdes sont nommées et rangées à part**, dans la série `node:long`
|
||
du § 14.4. **L'audit par mutation n'y est pas** (§ 14.13).
|
||
|
||
**Deux règles tiennent le budget.** Aucun test de la série surveillée n'attend un
|
||
délai — un minuteur est la façon ordinaire dont une série de deux secondes en
|
||
devient une de quarante. Aucun n'écrit hors d'un répertoire temporaire, dont le
|
||
stockage reçoit le chemin en paramètre : un module qui ne sait pas où il écrit ne
|
||
se teste pas en parallèle.
|
||
|
||
**La mesure est affichée, le refus est généreux.** Le lanceur imprime à chaque
|
||
exécution la durée totale et les dix tests les plus lents ; la construction échoue
|
||
quand la série surveillée dépasse **trente secondes**. Un refus calé sur le budget
|
||
lui-même serait capricieux sur une machine chargée, et un test capricieux se
|
||
désactive — ce qui coûte plus cher que le dépassement qu'il annonçait. La série
|
||
`node:long` porte son propre plafond, posé après sa première mesure. La dérive se
|
||
lit dans la liste bien avant d'être fatale.
|
||
|
||
**Deux contrôles qui balayent une arborescence sont bornés** : la cohérence des
|
||
versions (§ 18.5) et l'appariement captures/manifeste (§ 19.7). Chacun déclare son
|
||
périmètre — l'arbre des sources pour l'un, les sources de documentation et le
|
||
manifeste pour l'autre —, exclut les dépendances et les sorties de construction,
|
||
refuse de passer sur un balayage vide, et est **montré en train d'échouer** :
|
||
modifier `version.json` seul fait tomber le premier ; remplacer une image par une
|
||
version antérieure fait tomber le second.
|
||
|
||
### 14.15 Ce que l'exécutable emporte, et ce qu'il n'emporte pas
|
||
|
||
La suite d'épreuves **n'est pas livrée** : lanceur, données d'épreuve et moteur de
|
||
rendu de test sont des dépendances de développement, et les données d'épreuve
|
||
contiennent des cas qui n'ont rien à faire chez l'opérateur. Cette propriété est
|
||
**gardée**, non supposée, par le test de construction du § 14.9.
|
||
|
||
Mais le travail d'épreuve laisse **trois pièces dans l'application livrée**, et ce
|
||
sont celles que l'exigence « des tests unitaires dans l'application » vise
|
||
réellement :
|
||
|
||
- le **vérificateur d'invariants** du § 14.12, appelé par les commandes et au
|
||
chargement d'un fichier — une incohérence se refuse au moment du geste plutôt que
|
||
de se découvrir sur une feuille imprimée ;
|
||
- le **vérificateur d'indicateurs**, qui tient la garde du § 12.3 : la page refuse
|
||
de dessiner une courbe au-dessus de son plafond et le dit ;
|
||
- le **contrôle de cohérence** du § 8.8, qui est le même réflexe appliqué au
|
||
fichier : le logiciel refuse d'ouvrir ce qu'il ne peut pas justifier, et le
|
||
nomme.
|
||
|
||
Ces trois pièces sont du code de production, tenu par les mêmes épreuves que le
|
||
reste. Ce qui reste dehors est le harnais, jamais la vérification.
|
||
|
||
---
|
||
|
||
## 15. Les configurations de démonstration
|
||
|
||
Elles servent à trois choses : montrer le logiciel sans saisir 260 personnes,
|
||
enseigner la lecture des indicateurs, et servir de témoin de test. Une
|
||
démonstration qui change d'un lancement à l'autre ne remplit aucun des trois.
|
||
|
||
**Charger une démonstration crée une configuration neuve**, nommée d'après elle
|
||
suivie d'un numéro d'ordre, ouverte en lecture. Le logiciel n'écrase jamais la
|
||
configuration courante et ne modifie jamais la démonstration d'origine :
|
||
l'opérateur qui explore pendant une présentation ne doit pas pouvoir détruire
|
||
son vrai plan d'un clic.
|
||
|
||
### 15.1 La grande démonstration — 260 membres
|
||
|
||
| grandeur | valeur |
|
||
|---|---|
|
||
| membres | 260 |
|
||
| sièges par défaut | 8 |
|
||
| tables | **33 : 29 à 8 sièges, 4 à 7** |
|
||
| sièges | 29×8 + 4×7 = **260**, aucun libre |
|
||
| animateurs | 33, un par table, **pris parmi les 260** |
|
||
| mobiles | 227 |
|
||
| tours | 4 |
|
||
| appartenances | tirées, de 1 à 12 membres |
|
||
|
||
**Le calcul, et pourquoi il ne tombe pas juste tout seul.** 260 ÷ 8 = 32,5 :
|
||
aucun nombre entier de tables de 8 ne reçoit 260 personnes. 33 tables de 8
|
||
offrent 264 sièges, soit 4 vides. Le logiciel règle cela par la capacité
|
||
individuelle, et **la démonstration exerce ainsi, sur son premier écran, la
|
||
fonction « défaut global, exception par table »**.
|
||
|
||
**La configuration est exactement tendue** : la capacité libre vaut 227, soit
|
||
exactement le nombre de mobiles. Chaque tour est une partition exacte, le moteur
|
||
n'a jamais la liberté de déplacer quelqu'un vers une table moins remplie, et
|
||
tout mouvement doit être un échange. C'est une propriété de l'espace de
|
||
recherche, pas un détail.
|
||
|
||
**Ce que la configuration rend possible, avant la génération :**
|
||
|
||
| | plafond a priori |
|
||
|---|---|
|
||
| un mobile passant par quatre tables de 8 | **28** |
|
||
| un mobile passant une fois par une table de 7 | **27** |
|
||
| un animateur d'une table de 8 | **28** |
|
||
| **les quatre animateurs des tables de 7** | **24** |
|
||
|
||
> Ces quatre-là sont, **par construction, les personnes les plus mal servies de
|
||
> la démonstration**. Comme le minimum se calcule sur tous les participants
|
||
> placés, **c'est ce 24 qui gouverne le minimum global, et aucune relance ne
|
||
> l'améliorera jamais.** C'est précisément ce que la ligne secondaire « ancrés »
|
||
> du § 5.4 permet de lire sans conclure à un défaut du moteur — et c'est la
|
||
> démonstration la plus utile du catalogue, puisqu'elle montre en une lecture
|
||
> pourquoi les deux populations sont séparées.
|
||
|
||
**99 retours imposés** (33 × 3), attribués aux réservations et non additionnés
|
||
aux autres.
|
||
|
||
**Les animateurs sont tirés sans égard à leur appartenance**, et aucun
|
||
regroupement ne leur en impose une commune — sinon chaque mobile accumulerait au
|
||
moins 3 redondances qu'aucune recherche ne réduit, et la démonstration vedette
|
||
illustrerait une impossibilité. Un test le garde.
|
||
|
||
**La variante sans exception** — 33 tables toutes à 8, 4 places libres — est
|
||
livrée à côté. Quatre sièges de marge rendent au moteur la liberté que la
|
||
configuration principale lui refuse ; comparer les deux mesure ce que coûte
|
||
l'absence de marge. Dans cette variante, **les tables incomplètes changent d'un
|
||
tour à l'autre**, donc le plafond **réalisé** varie d'une proposition à l'autre :
|
||
c'est elle, et non la principale, qui est la **configuration témoin de la vue de
|
||
comparaison** du § 12.10.6, puisqu'elle est la seule où les escaliers de plafond
|
||
diffèrent entre deux propositions.
|
||
|
||
**La grande démonstration ne décerne jamais « minimum atteint ».** Les tables de 7
|
||
offrent 112 sièges-tours dont 16 aux animateurs, donc 96 sièges-tours mobiles
|
||
qu'un placement ne peut pas ne pas servir ; chaque mobile n'en absorbant que 4 au
|
||
plus, **au moins 24 mobiles** terminent sous leur plafond a priori, et l'écart
|
||
d'itinéraire maximal ne descend jamais sous 1 (§ 12.10.4). Le certificat se lisant
|
||
contre le plafond a priori (§ 5.5), il est hors d'atteinte ici — et c'est une
|
||
propriété de la configuration, non un défaut du moteur.
|
||
|
||
**Un témoin de pagination à 257 badges** accompagne le catalogue sans en être une
|
||
cinquième entrée : c'est une **donnée d'épreuve** (§ 14.9), qui donne 65 planches
|
||
A4 et 129 planches Lettre (§ 11.2). Les configurations dont une épreuve ou une
|
||
capture d'écran a besoin et que le catalogue ne porte pas se **construisent** par
|
||
des scénarios courts (§ 19.3) ; le catalogue reste à **quatre** entrées, et
|
||
l'étape qui lit leur nombre le lit dans le catalogue plutôt que de l'écrire.
|
||
|
||
### 15.2 La distribution des appartenances
|
||
|
||
Loi discrète sur 1..12, à poids fixés, décrochant après 3 :
|
||
|
||
| taille | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
|
||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||
| poids | 24 | 20 | 16 | 8 | 6 | 5 | 4 | 3 | 2 | 2 | 1 | 1 |
|
||
|
||
Somme 92 ; les tailles 1 à 3 pèsent 65,2 % des groupes ; taille moyenne
|
||
théorique 3,446, d'où **environ 75 groupes** pour 260 membres.
|
||
|
||
**Le compte réellement obtenu n'est pas une donnée de cette spécification** : il
|
||
dépend de la graine, fixée dans le dépôt. Le fichier livré porte le profil qu'il
|
||
a effectivement produit, et **c'est ce profil, lu dans le fichier, qui est la
|
||
valeur attendue du test**. Inscrire ici des effectifs tirés donnerait une
|
||
certitude fausse.
|
||
|
||
**La règle du dernier groupe** : tirer tant que le total reste sous 260, puis
|
||
**tronquer le dernier au reste**.
|
||
|
||
**La faisabilité se construit, elle ne se déduit pas d'une inégalité.** « Le plus
|
||
gros groupe tient dans le nombre de tables » est nécessaire mais **ne suffit
|
||
pas** : trois tables de 10, 1 et 1 ne peuvent séparer quatre groupes de 3, parce
|
||
que la table de 10 exigerait 10 appartenances distinctes. Le logiciel vérifie
|
||
**deux** inégalités — plus gros groupe ≤ nombre de tables, plus grande
|
||
capacité − places vides ≤ nombre de groupes — puis **construit un tour
|
||
témoin**. Les places vides sont les sièges qui restent une fois chaque membre
|
||
assis, zéro quand la salle n'en laisse pas : une table reçoit au moins sa
|
||
capacité moins ces places, chacun de ses occupants d'une appartenance
|
||
différente. Dans une salle exactement pleine, la seconde inégalité se lit
|
||
« plus grande capacité ≤ nombre de groupes » ; ailleurs, cette lecture
|
||
refuserait à tort deux tables de 3 pour deux membres d'appartenances
|
||
différentes, qu'un tour sépare. Le générateur **refuse une graine dont aucun
|
||
tour témoin ne se construit**.
|
||
|
||
### 15.3 La petite démonstration — 12 membres
|
||
|
||
| grandeur | valeur |
|
||
|---|---|
|
||
| membres | 12 |
|
||
| tables | 4, de **3 sièges** |
|
||
| tours | 4 |
|
||
| réservations | **aucune** |
|
||
| appartenances | **4, 4, 4**, fixées à la main |
|
||
|
||
Elle tient sur un écran, et c'est sa raison d'être : la grande noie un défaut
|
||
dans 33 tables, la petite le montre d'un coup d'œil.
|
||
|
||
**Elle admet un plan parfait, et ce plan est écrit.** Avec 4 tours et 2 voisins
|
||
par tour, nul ne peut rencontrer plus de **8** personnes sur 11.
|
||
|
||
| tour | table 1 | table 2 | table 3 | table 4 |
|
||
|---|---|---|---|---|
|
||
| 1 | 1, 2, 3 | 4, 5, 6 | 7, 8, 9 | 10, 11, 12 |
|
||
| 2 | 6, 9, 12 | 3, 8, 11 | 2, 5, 10 | 1, 4, 7 |
|
||
| 3 | 4, 8, 10 | 2, 7, 12 | 1, 6, 11 | 3, 5, 9 |
|
||
| 4 | 5, 7, 11 | 1, 9, 10 | 3, 4, 12 | 2, 6, 8 |
|
||
|
||
Appartenances : **A** = {1, 5, 8, 12}, **B** = {2, 4, 9, 11}, **C** = {3, 6, 7,
|
||
10}. Chaque table réunit à chaque tour un membre de chacune. **Vérifié par
|
||
énumération** : partition valide à chaque tour, zéro paire répétée, chacun
|
||
visite les quatre tables, 8 rencontres pour les douze, zéro collision. Le
|
||
logiciel doit afficher **« minimum atteint »**, les compteurs de collision et de
|
||
répétition **mesurés** à zéro, et la ligne secondaire « ancrés » à **« — »** :
|
||
cette démonstration n'ayant aucune réservation, sa population d'ancrés est vide,
|
||
et le § 5.4 exige le tiret, jamais zéro.
|
||
|
||
**La variante « conflit inévitable »** ne change qu'une colonne : appartenances
|
||
de tailles **5, 4, 3**. Cinq personnes ne se répartissent pas sur quatre tables
|
||
sans que deux se retrouvent : **au moins une collision par tour, quatre collisions
|
||
cumulées sur les quatre tours**, quel que soit l'algorithme — et **au moins une
|
||
paire distincte**, jamais quatre : un plan qui garde les deux mêmes personnes
|
||
ensemble aux quatre tours n'en réunit qu'une. Les deux planchers sont dans deux
|
||
unités différentes et le logiciel ne les confond jamais (§ 5.4).
|
||
|
||
Cette variante est aussi l'instance la plus courte où le classement du § 5.7 se
|
||
lit : le groupe de 5 porte dix paires possibles pour quatre collisions forcées,
|
||
donc l'**excédent n'est pas forcé** — un plan peut répartir ses quatre collisions
|
||
sur quatre paires distinctes, et c'est celui que le logiciel met en tête, devant
|
||
un plan qui les concentrerait sur une seule.
|
||
|
||
Une personne déplacée d'un groupe à l'autre fait basculer le logiciel de « minimum
|
||
atteint » à « quatre collisions cumulées inévitables » : c'est le contraste le
|
||
plus court que la spécification puisse offrir.
|
||
|
||
### 15.4 Les cas limites, exercés plutôt que supposés
|
||
|
||
- **Une seule appartenance** — la contrainte de séparation est vide de sens : le
|
||
logiciel doit l'annoncer et proposer de la désactiver, non afficher 12
|
||
conflits sans commentaire.
|
||
- **Une seule table, un seul tour** — les indicateurs de répétition doivent
|
||
valoir zéro, non lever une erreur de division.
|
||
- **Des noms longs** — la grande démonstration contient une poignée de noms
|
||
proches de la limite, dont un de glyphes larges et un à diacritiques. Sans
|
||
eux, une démonstration faite de noms courts laisse les seuils faux jusqu'à la
|
||
première vraie liste.
|
||
|
||
Le générateur **refuse** une configuration à zéro participant ou à zéro table :
|
||
un fichier vide passe tous les tests sans rien démontrer.
|
||
|
||
### 15.5 La reproductibilité
|
||
|
||
1. **Un seul générateur pseudo-aléatoire**, à état entier et transition
|
||
explicite, accompagné d'un **vecteur de test** qu'un test rejoue.
|
||
2. **Une graine entière écrite dans la définition**, pas enfouie dans le code.
|
||
3. **L'ordre des tirages fait partie du contrat** : insérer un tirage au milieu
|
||
décale tout ce qui suit.
|
||
4. **Rien de dépendant de l'environnement** : aucun `localeCompare`, aucun
|
||
parcours reposant sur l'ordre d'itération d'un objet ; deux éléments de même
|
||
rang se départagent par leur identifiant entier. **Cette règle vaut pour le
|
||
moteur autant que pour le générateur** : sans elle, le même code et la même
|
||
graine donnent deux placements sur deux moteurs d'exécution, et toute la
|
||
reproductibilité des captures d'écran tombe (§ 19.4).
|
||
5. **Le fichier livré est la source de vérité.** Un test rejoue le script
|
||
générateur et compare l'empreinte au fichier livré. **L'empreinte porte sur la
|
||
charge utile, l'en-tête exclu** (§ 8.8) : sans cette restriction,
|
||
`produit_version` la fait échouer à chaque livraison, et le geste conscient
|
||
qu'elle réclame devient mécanique, donc aveugle. **La régénération devient un
|
||
geste conscient**, et non une dérive qui change les démonstrations sous les
|
||
pieds de l'opérateur. L'échec nomme ce qui a changé.
|
||
6. **Le placement, lui, n'est pas figé** : la démonstration fixe les entrées, le
|
||
moteur reste libre. Figer le résultat empêcherait de montrer la comparaison de
|
||
plusieurs propositions, qui est le cœur du logiciel. **Cela ne veut pas dire
|
||
que le moteur dispose d'une source de hasard libre** : il prend une graine et
|
||
un compte d'arrêt (§ 5.7), et à graine et entrée égales il rend le même plan.
|
||
Ce qui n'est pas figé est le **stockage** d'un résultat dans la démonstration,
|
||
pas le déterminisme du calcul.
|
||
|
||
### 15.6 Les noms : inventés, lisibles, sans collision
|
||
|
||
Une liste de chaînes aléatoires est illisible en démonstration, et des noms
|
||
réels n'ont pas leur place dans un dépôt. Le logiciel compose des noms **forgés
|
||
mais prononçables**, par produit cartésien **tiré sans remise** — ce qui garantit
|
||
l'unicité sans boucle de rattrapage, donc sans tirage supplémentaire qui
|
||
décalerait la suite.
|
||
|
||
**Les personnes** : un prénom courant et un patronyme forgé par assemblage d'un
|
||
préfixe et d'une terminaison. Le réservoir compte **au moins 320 combinaisons**,
|
||
plus que les 260 personnes : un réservoir plus étroit forcerait des homonymes de
|
||
famille, que l'œil lit comme des parentés sur une feuille de table.
|
||
|
||
**Les appartenances** : une tête de nom d'organisation — `Ateliers`,
|
||
`Coopérative`, `Verrerie` — et une racine forgée. La tête dit immédiatement
|
||
qu'il s'agit d'une organisation. Le réservoir se dimensionne sur le **maximum
|
||
structurel** : une loi qui peut tirer 260 groupes d'une personne exige 260
|
||
combinaisons. Le générateur **refuse** de produire une démonstration dont le
|
||
compte de groupes dépasse son réservoir.
|
||
|
||
Un test vérifie l'unicité et qu'aucun fragment forgé n'apparaît dans le dépôt
|
||
**hors des listes du générateur, des fichiers de démonstration et des CSV
|
||
d'exemple**. Le balayage s'étend aux **données d'épreuve** (§ 14.9), dont les noms
|
||
inventés sont disjoints de ce réservoir : une donnée d'épreuve qui emprunte une
|
||
racine d'organisation plausible fait échouer la garde, et c'est ce qui l'empêche
|
||
de tomber au premier exemple écrit de bonne foi. Il refuse de passer sur un balayage vide. **Ce test n'établit rien
|
||
sur le monde réel** : aucune vérification locale ne peut prouver qu'un nom forgé
|
||
ne désigne nulle part une organisation existante.
|
||
|
||
---
|
||
|
||
## 16. Livraison
|
||
|
||
L'exécutable Windows se construit depuis Linux par `electron-builder`, qui
|
||
emballe la coquille Electron du projet et le même `www/` que servent les
|
||
plateformes de Capacitor (§ 13.1). La construction s'exécute dans un conteneur,
|
||
sous une image épinglée par empreinte, de sorte que la machine de développement
|
||
n'a rien à installer en dehors du projet. La cible portable n'exige pas Wine :
|
||
`electron-builder` édite lui-même les ressources de l'exécutable. L'image n'en
|
||
porte donc pas, et une cible qui l'exigerait — un installateur — y échoue au lieu
|
||
de produire le livrable que ce paragraphe exclut. Le conteneur ne reçoit aucune
|
||
variable de l'hôte : un numéro de construction d'intégration continue y
|
||
remplacerait le quatrième champ de la version Windows (§ 18.1).
|
||
|
||
**Cette capacité est à vérifier dès la première semaine**, sur un squelette vide,
|
||
au même titre que la publication du dossier d'origine par le lanceur portable
|
||
(§ 8.6). Découvrir en fin de projet qu'une étape exige une machine Windows coûte
|
||
beaucoup plus cher que de le découvrir tout de suite.
|
||
|
||
Ce qui est livré au client : **un exécutable**, et rien d'autre à installer.
|
||
|
||
**Le livrable est un exécutable portable à fichier unique, non un
|
||
installateur**, et ce n'est pas une préférence : la règle du dossier de travail du
|
||
§ 8.6 en dépend. Un installateur qui rangerait l'exécutable sous un emplacement de
|
||
données applicatives y ferait réussir la sonde d'écriture, et le logiciel créerait
|
||
son dossier de données exactement là où le § 8.6 l'interdit.
|
||
|
||
**La construction refuse de produire un exécutable dont la version n'a pas de
|
||
section au changelog** (§ 18.4), et refuse d'écraser un exécutable portant le même
|
||
nom dans le dossier de sortie (§ 18.2).
|
||
|
||
### 16.1 Les documents livrés
|
||
|
||
| document | contenu |
|
||
|---|---|
|
||
| `GUIDE-WINDOWS.md` | obtenir l'exécutable, le lancer, **la règle du dossier de travail et ses deux issues** (§ 8.6), la commande qui ouvre ce dossier, comment sauvegarder, que faire si l'antivirus bloque, **où lire la version** et comment signaler un problème |
|
||
| `GUIDE-USAGE.md` | le parcours d'une soirée, de la liste de participants à l'impression, écran par écran |
|
||
| `CHANGELOG.md` | ce qui change d'une version à l'autre, et ce qu'il faut faire en remplaçant (§ 18.4) |
|
||
|
||
**Le `GUIDE-USAGE.md` suit les douze étapes du § 2.1**, et le scénario de pilotage
|
||
les implémente : chaque capture d'écran déclare l'étape à laquelle elle
|
||
appartient, et un contrôle du niveau `node` échoue si une étape n'a aucune
|
||
capture, ou si l'ordre des captures contredit l'ordre des étapes (§ 19.3). Deux
|
||
sources pour une même séquence sont le mode de défaillance que toute cette
|
||
spécification combat — à ceci près qu'il est ici mécaniquement détectable, donc
|
||
qu'il se **garde** au lieu de se surveiller. La liste des écrans est celle du
|
||
§ 19.3.
|
||
|
||
**`GUIDE-WINDOWS.md` sort de la boucle d'engendrement** (§ 19.8) : le pilotage
|
||
s'exécute sur `web` sous Linux, et les écrans que ce guide décrit appartiennent
|
||
à `electron` sous Windows. Il **ne cite jamais un nom d'exécutable complet** : il
|
||
cite le **gabarit** et désigne « le fichier dont le nom commence par
|
||
`gestion_table_tournante_libre_v` » — un nom daté change à chaque livraison, et
|
||
une phrase qui en cite un envoie l'opérateur chercher un fichier qui n'existe pas.
|
||
Il n'écrit pas davantage de phrase fixe sur l'emplacement des fichiers : il énonce
|
||
la règle du § 8.6, ses deux issues, et la commande qui ouvre le dossier.
|
||
|
||
Le guide d'usage se rédige **à partir des configurations de démonstration**, pour
|
||
que le lecteur reproduise exactement ce qu'il lit. Il est relu par quelqu'un qui
|
||
n'a pas écrit le logiciel ; un guide relu par son auteur ne révèle aucune étape
|
||
manquante.
|
||
|
||
### 16.2 Le poste de développement
|
||
|
||
Le projet s'installe et se lance d'une commande, la même sur chaque système
|
||
Linux, NixOS compris, et sous macOS ; Windows a la sienne. Une étape laissée à
|
||
la main est celle qu'un nouveau venu oublie, sans savoir ensuite laquelle.
|
||
|
||
| commande | ce qu'elle fait |
|
||
|---|---|
|
||
| `./install.sh` | pose les paquets du système qui manquent — par apt, dnf, zypper ou pacman, sous `sudo` —, puis le Node du projet et les dépendances que fixe `package-lock.json` ; relancée, ne refait que ce qui manque |
|
||
| `./install_dev.sh` | ajoute le Chromium des épreuves du navigateur, des polices et, sur amd64, `podman`, sous lequel se construit l'exécutable |
|
||
| `./run.sh` | lance la coquille Electron sur une session graphique ; sans écran, ou là où Electron n'est pas publié, sert l'application à un navigateur par le serveur de Vite |
|
||
| `make` | lance l'application comme `./run.sh` ; `make help` nomme les autres cibles — les trois séries d'épreuves, la couverture (§ 14.13), la construction, le contrôle de version (§ 18.5), l'essai de démarrage |
|
||
|
||
**Le Node du projet a une seule source** : la majeure écrite dans
|
||
`.node-version`, que lisent les scripts bash, les scripts PowerShell et
|
||
`shell.nix`. L'installation télécharge cette version de nodejs.org, la vérifie
|
||
contre sa somme et la range hors du dépôt, dans le dossier de l'utilisateur :
|
||
elle n'exige aucun droit d'administration et ne dépend pas du Node que porte —
|
||
ou ne porte pas — le système. Chaque cible du `Makefile` passe par ce Node.
|
||
|
||
**Sous Windows**, `install.cmd`, `install_dev.cmd` et `run.cmd` passent la main à
|
||
des scripts PowerShell 5.1, la version que porte tout Windows. **Sous NixOS**,
|
||
où les binaires téléchargés ne trouvent ni leur chargeur ni leurs bibliothèques
|
||
aux chemins qu'ils attendent, `scripts/installation/shell.nix` déclare Node,
|
||
Electron et Chromium d'un nixpkgs épinglé par révision et par somme ; les scripts
|
||
du projet s'y relancent d'eux-mêmes.
|
||
|
||
**`make verifier_systemes` éprouve l'installation elle-même.** Sur chaque système
|
||
du catalogue — les familles Debian, Fedora, openSUSE et Arch, et Nix —, un
|
||
conteneur `podman` exécute, sous un compte ordinaire qui passe par `sudo`,
|
||
`./install_dev.sh`, puis les trois séries d'épreuves, l'essai de démarrage
|
||
d'Electron et le serveur de Vite ; les scripts PowerShell s'y éprouvent sous
|
||
`pwsh`. Ce qu'un conteneur n'éprouve pas est nommé, et reste à vérifier sur une
|
||
machine : le bac à sable d'Electron, une autre architecture que celle de l'hôte,
|
||
NixOS lui-même, une vraie session graphique, Windows et macOS réels.
|
||
|
||
---
|
||
|
||
## 17. Ce que ce document ne tranche pas
|
||
|
||
Les numéros ne bougent pas : ils sont cités ailleurs. Quatre points sont
|
||
désormais tranchés et le disent ; deux reçoivent leur **instrument** sans que leur
|
||
valeur soit fixée ; un seul reste entièrement ouvert.
|
||
|
||
1. **TRANCHÉ — le format de stockage des placements.** La saisie reste en champs
|
||
nommés, les placements engendrés passent à la **forme positionnelle** : § 8.9.
|
||
2. **Tranché pour moitié — la bibliothèque PDF.** La question de l'ingestion du
|
||
SVG **disparaît** : la bibliothèque ne dessine plus que du texte, des filets et
|
||
des rectangles (§ 11.1). Reste ouvert **laquelle**, sur un cahier des charges
|
||
désormais étroit : exécution sous `node`, police embarquée en latin étendu,
|
||
table `ToUnicode` écrite, boîte de page exacte, couche de texte extractible, et
|
||
la possibilité de neutraliser la date de création et l'identifiant de document.
|
||
S'y ajoute le choix de l'outil d'**extraction** qui éprouve le résultat, dont
|
||
le test dépend entièrement.
|
||
3. **TRANCHÉ — la désignation des animateurs est manuelle.** Il n'existe aucune
|
||
commande qui les répartit : § 5.8 et § 2.1, étape 6.
|
||
4. **Le coût de la maximisation du plafond a priori** sur les itinéraires
|
||
admissibles. **Son instrument est nommé** — le banc du § 19.10 le relève sous
|
||
`node` sur la grande démonstration et sa variante — et le point **porte
|
||
désormais trois usages** au lieu d'un : le diagnostic, le troisième chiffre de
|
||
la page de qualité, et le départage du § 5.7. Tant qu'il n'est pas mesuré, les
|
||
deux derniers s'effacent proprement : « inconnu », et critère sauté nommé.
|
||
5. **TRANCHÉ — la comparaison trace le manque.** § 12.10, et sa configuration
|
||
témoin est la variante sans exception (§ 15.1).
|
||
6. **Le seuil de zoom qui retire les listes de noms.** **Son instrument est
|
||
nommé** : le banc du § 19.10 relève le **temps par image** à plusieurs zooms sur
|
||
les 33 tables avec listes. Aucune capture d'écran ne le mesure — une image est
|
||
muette sur la durée qui l'a produite. Ce qui reste à dire est ce qu'est un temps
|
||
par image **acceptable sur la machine de l'opérateur**, qui n'est pas celle du
|
||
développement.
|
||
7. **OUVERT, et hors périmètre de la première itération — les dégagements de
|
||
référence d'une salle.** Le mot « conforme » reste absent de l'interface et la
|
||
mention « disposition relative » reste sur chaque impression du plan (§ 7.7).
|
||
L'unité étant déjà le centimètre réel, l'ajouter plus tard ne coûte **aucune
|
||
migration**.
|
||
|
||
---
|
||
|
||
## 18. La version, le nom du livrable, le changelog
|
||
|
||
Le logiciel se livre comme un fichier unique que l'opérateur copie lui-même. Il
|
||
n'existe ni dépôt de paquets, ni mise à jour automatique, ni canal par lequel le
|
||
logiciel pourrait annoncer son âge. Ce qui permet de dire *laquelle de ces copies
|
||
est laquelle* tient donc entièrement dans le **nom du fichier**, dans ce que
|
||
**l'application affiche** et dans le **changelog** — et cette section existe pour
|
||
que ces endroits ne puissent pas se contredire.
|
||
|
||
### 18.1 Le schéma : `AAAA.MM.JJ.NN`
|
||
|
||
Une version est **la date de la livraison suivie d'un rang dans la journée**, sur
|
||
quatre champs, tous présents, tous à longueur fixe :
|
||
|
||
```
|
||
2026.10.05.01 première livraison du 5 octobre 2026
|
||
2026.10.05.02 seconde livraison du même jour
|
||
2026.01.05.01 5 janvier 2026 — le mois garde son zéro de tête
|
||
```
|
||
|
||
Les quatre champs sont **toujours** écrits : l'année sur 4 chiffres, le mois et le
|
||
jour sur 2, le rang sur 2, zéros de tête compris. Un format qui change de forme
|
||
entre deux livraisons — le rang n'apparaissant qu'à partir de la seconde — casse
|
||
le tri d'un dossier, la comparaison de deux noms et l'expression régulière du
|
||
contrôle du § 18.5.
|
||
|
||
**Pourquoi la date.** L'opérateur et celui qui reçoit son signalement n'ont qu'une
|
||
chose en commun : le moment où le fichier a été envoyé. Un numéro de publication
|
||
oblige à ouvrir le changelog pour savoir s'il est vieux de trois jours ou de trois
|
||
mois. `AAAA.MM` ne suffit pas : pendant la préparation d'un événement, plusieurs
|
||
livraisons dans le même mois sont la règle.
|
||
|
||
**Pourquoi pas un versionnage sémantique.** Personne ne consomme ce logiciel comme
|
||
une dépendance : il n'y a ni interface de programmation publiée, ni résolution de
|
||
contraintes, ni arbitre pour trancher « est-ce un changement cassant ». Un champ
|
||
majeur mal choisi est une erreur muette, qu'aucun contrôle ne peut relever. Ce que
|
||
le versionnage sémantique servirait à dire ici — « ce que tu enregistres
|
||
aujourd'hui, la version d'hier ne le modifiera plus » — est dit par la **version
|
||
de format des fichiers** du § 8.8, qui est un nombre distinct et le reste
|
||
(§ 18.4).
|
||
|
||
**L'ordre.** Deux versions se comparent champ par champ comme quatre entiers. Les
|
||
champs étant à longueur fixe, l'ordre alphabétique des noms de fichiers coïncide
|
||
avec l'ordre chronologique : un opérateur qui trie son dossier par nom lit sa
|
||
version courante sur la dernière ligne, sans rien savoir du schéma.
|
||
|
||
**Le rang.** Il repart à `01` chaque jour et ne décroît jamais. Un numéro est
|
||
**consommé au moment où l'exécutable quitte la machine qui l'a construit**, et
|
||
cesse alors d'être réutilisable : deux binaires différents répondant à un même
|
||
nom, chacun dans une main différente, rendent irréparable le seul moment qui
|
||
compte — l'opérateur qui décrit un défaut en nommant sa version. Un exécutable
|
||
construit puis jeté sans être envoyé n'a consommé aucun numéro.
|
||
|
||
**Le rang plafonne à 99.** La construction refuse le centième et le dit. Élargir
|
||
le champ détruirait la propriété de tri que toute la section achète, et cent
|
||
livraisons dans une journée désignent un autre problème que le format du numéro.
|
||
|
||
**La forme technique.** Le fichier de description du projet, `package.json`,
|
||
exige trois champs entiers **sans zéro de tête** — le format de version qu'il
|
||
valide refuse `0105` — et `electron-builder` en tire la ressource de version de
|
||
l'exécutable Windows. Le logiciel dérive donc le mois et le jour en **un entier**,
|
||
`MM × 100 + JJ`, écrit sans remplissage, et le rang en entier :
|
||
|
||
```
|
||
version affichée 2026.10.05.01 2026.01.05.01
|
||
version technique 2026.1005.1 2026.105.1 (AAAA . MM×100+JJ . NN)
|
||
version Windows 2026.1005.1.0 2026.105.1.0
|
||
```
|
||
|
||
La dérivation est **monotone** — `MM × 100 + JJ` croît strictement avec la date,
|
||
donc `2026.105.1 < 2026.1005.1 < 2026.1231.1 < 2027.105.1` — et **bijective** : le
|
||
jour étant inférieur à 100, un entier se redécompose d'une seule façon. Un
|
||
contrôle refait le trajet dans les deux sens et refuse une divergence. Les trois
|
||
nombres tiennent dans les **16 bits** que le format de ressources Windows impose à
|
||
chacun de ses quatre champs : l'année, `MM × 100 + JJ` qui vaut au plus 1231, et
|
||
le rang.
|
||
|
||
**Quand la plateforme `android` sera ajoutée**, son `versionCode`, un entier
|
||
unique, dérivera de la même source sous la forme `AAMMJJNN` sur huit chiffres —
|
||
`26100501` —, monotone et sous la borne de 2 100 000 000 qu'impose la
|
||
plateforme, et rejoindra le contrôle du § 18.5.
|
||
|
||
**La longueur fixe ne vaut que pour la forme affichée et pour le nom de fichier**,
|
||
qui sont les seules qu'un humain trie. La forme technique n'est jamais lue par
|
||
l'opérateur et n'est jamais triée.
|
||
|
||
### 18.2 Le nom du livrable
|
||
|
||
```
|
||
gestion_table_tournante_libre_v<AAAA>_<MM>_<JJ>_<NN>.exe
|
||
gestion_table_tournante_libre_v2026_10_05_01.exe
|
||
```
|
||
|
||
Les séparateurs de la version deviennent des **soulignés**. Le nom ne contient
|
||
alors **qu'un seul point**, celui de l'extension : tout outil qui coupe un nom à
|
||
son dernier point, et le réglage de Windows qui masque les extensions connues,
|
||
laissent la version entière visible. Un nom portant `v2026.10.05.01` la ferait
|
||
amputer par les outils qui coupent au **premier** point.
|
||
|
||
**Le nom n'est jamais saisi.** Il est **calculé** à la construction depuis
|
||
l'unique source du § 18.3, par substitution des points par des soulignés. Un nom
|
||
tapé à la main finit par mentir sur son contenu, et le signalement arrive sur
|
||
cette copie-là.
|
||
|
||
La construction **refuse d'écraser un exécutable portant le même nom** dans le
|
||
dossier de sortie. Ce refus est un garde-fou local — le § 18.5 énonce ce qu'il ne
|
||
couvre pas — et une option explicite le lève pour reconstruire un numéro qui n'a
|
||
pas été livré.
|
||
|
||
**Le guide ne cite jamais un nom complet, il cite le gabarit** (§ 16.1).
|
||
|
||
### 18.3 Une seule source, quatre destinations
|
||
|
||
La version est écrite **à un seul endroit**, un fichier `version.json` à la racine
|
||
du projet :
|
||
|
||
```json
|
||
{ "version": "2026.10.05.01" }
|
||
```
|
||
|
||
Tout le reste en dérive, par un script de construction :
|
||
|
||
| destination | forme | usage |
|
||
|---|---|---|
|
||
| `package.json` | technique | ce qu'exigent npm et `electron-builder`, qui en tire la ressource de version Windows |
|
||
| nom de l'exécutable, posé dans la configuration d'`electron-builder` | soulignés | ce que l'opérateur voit dans son dossier |
|
||
| `src/version.genere.js` | affichée **et** technique | ce que l'application affiche |
|
||
| titre de la section de tête du `CHANGELOG.md` | affichée | ce que l'opérateur lit avant de remplacer |
|
||
|
||
**Le module engendré est versionné dans le dépôt**, et un contrôle rejoue le
|
||
script puis compare — la même discipline qu'au § 15.5 pour les fichiers de
|
||
démonstration (§ 13.1).
|
||
|
||
**Le module porte aussi la provenance de la construction** — livraison, séance de
|
||
développement, ou construction de documentation — posée par la commande qui
|
||
l'engendre, jamais lue dans l'horloge ni dans l'environnement. Sans elle, une
|
||
capture d'écran prise pendant le développement annonce un numéro déjà livré, et le
|
||
diagnostic porte sur un binaire que personne ne détient.
|
||
|
||
### 18.4 Le `CHANGELOG.md`
|
||
|
||
**Son lecteur est l'opérateur qui décide s'il remplace son exécutable.** Il est
|
||
rédigé en français, la version la plus récente en tête, et il n'est jamais
|
||
tronqué.
|
||
|
||
Une section par version :
|
||
|
||
```markdown
|
||
## 2026.10.05.01 — 5 octobre 2026
|
||
|
||
**Ce qui change pour vous**
|
||
- Le plan de salle se recadre sur la table sélectionnée.
|
||
- L'import d'un tableur accepte la tabulation comme séparateur.
|
||
|
||
**Ce qui est corrigé**
|
||
- Un nom portant des accents, importé depuis un tableur enregistré par un
|
||
logiciel qui n'annonce pas son encodage, s'affichait avec des caractères
|
||
parasites sur le plan imprimé.
|
||
- Une table ramenée à sa capacité par la poignée pouvait désasseoir une personne
|
||
à un tour qui n'était pas affiché.
|
||
|
||
**Version de format des fichiers** : inchangée.
|
||
|
||
**En remplaçant** : rien à faire. Copier le nouvel exécutable par-dessus
|
||
l'ancien ; les événements déjà enregistrés s'ouvrent sans manipulation.
|
||
```
|
||
|
||
La date en toutes lettres du titre n'est pas une seconde source : le contrôle la
|
||
**réengendre** depuis la version et compare.
|
||
|
||
Les deux premières rubriques s'omettent quand elles sont vides. **Les deux
|
||
dernières lignes sont obligatoires** : ce sont celles qui répondent à la question
|
||
que l'opérateur se pose. Une rubrique absente se lit comme un oubli ; un « rien à
|
||
faire » écrit est une réponse.
|
||
|
||
La ligne de format est obligatoire parce qu'elle seule annonce un échange devenu à
|
||
sens unique. Quand la version de format monte, un événement ouvert en écriture par
|
||
la nouvelle version **se convertit** (§ 8.8) ; l'ancienne version ne le rouvrira
|
||
plus qu'en lecture seule. La ligne dit donc, dans ce cas, de **conserver une copie
|
||
du fichier avant la première modification** — la conversion n'a pas lieu à
|
||
l'ouverture, et c'est exactement le moment où l'opérateur peut encore se ménager
|
||
un retour en arrière.
|
||
|
||
**L'épreuve de ce qui entre.** Une ligne du changelog a pour sujet **ce que
|
||
l'opérateur voit, fait ou subit**. Une ligne dont le sujet est un module, un
|
||
fichier source, une bibliothèque ou un test n'y entre pas : ce récit vit dans
|
||
l'historique du dépôt. Une correction se décrit **par son symptôme visible** — « les
|
||
accents s'affichaient de travers sur le plan imprimé » — et non par sa cause dans
|
||
le code : l'opérateur reconnaît ce qu'il a vécu, pas ce qui l'a produit.
|
||
|
||
**Il n'existe aucune section « à venir ».** Une version existe lorsqu'un exécutable
|
||
a été livré. Une section d'intentions transforme le fichier en feuille de route, et
|
||
l'opérateur cesse de lire la première section comme « ce que je peux avoir ».
|
||
|
||
La section de tête s'écrit donc **au moment de la livraison**. La commande de
|
||
livraison **refuse de construire** si la section de tête manque, si son titre ne
|
||
reproduit pas la version de `version.json`, si son corps est vide, ou si l'une des
|
||
deux lignes obligatoires manque. Une livraison dont le motif n'est écrit nulle part
|
||
est une livraison dont l'opérateur ne peut pas décider.
|
||
|
||
### 18.5 Le contrôle qui interdit la divergence
|
||
|
||
Chaque endroit où un chiffre se recopie est une occasion de diverger. Un contrôle
|
||
de la série `node` surveillée (§ 14.4) **échoue** quand :
|
||
|
||
1. la version technique de `package.json` n'est pas celle que la
|
||
dérivation du § 18.1 produit depuis `version.json`, ou ne se redécompose pas en
|
||
la version affichée ;
|
||
2. le module engendré ne reproduit pas, caractère pour caractère, ce que le script
|
||
réengendre ;
|
||
3. le titre de la première section du `CHANGELOG.md` n'est pas la version
|
||
courante, sa date en toutes lettres n'est pas celle que la version engendre, ou
|
||
son corps n'a pas ses deux lignes obligatoires ;
|
||
4. les sections du changelog ne sont pas en ordre strictement décroissant, ou deux
|
||
sections portent la même version ;
|
||
5. la date inscrite dans la version dépasse de plus d'un jour la date civile locale
|
||
de la machine qui construit. La tolérance absorbe l'écart de fuseau ; ce que le
|
||
contrôle attrape est une année ou un mois tapé de travers, pas une minute ;
|
||
6. une chaîne **de la forme affichée** apparaît ailleurs que dans `version.json`,
|
||
le module engendré et le changelog, ou une chaîne **de la forme technique**
|
||
ailleurs que dans `package.json` et le module engendré ;
|
||
7. un document construit porte le littéral `#_#`, ou cite un nom d'exécutable qui
|
||
**ne se conforme pas au gabarit** du § 18.2. Un gabarit non substitué se lit
|
||
comme un nom de fichier et envoie l'opérateur chercher un fichier qui n'existe
|
||
pas. Le contrôle ne peut pas exiger qu'un nom cité **figure parmi les sorties de
|
||
la construction** : un gabarit ne satisfait par construction jamais cette
|
||
condition, et la seule formulation qui tienne échouerait ;
|
||
8. son balayage ne trouve **aucun** fichier à examiner (§ 14.2).
|
||
|
||
**Le périmètre du balayage du point 6 se pose à l'écriture du contrôle, pas
|
||
après.** Il porte sur **l'arbre des sources, les fichiers de configuration et les
|
||
scripts de construction**, à l'exclusion des **données engendrées** — fichiers de
|
||
démonstration qui portent `produit_version`, manifeste des captures d'écran,
|
||
données d'épreuve —, des guides, des épreuves de la dérivation elle-même qui citent
|
||
des exemples par nécessité, des dépendances et des sorties de construction. Sans
|
||
cette exclusion le contrôle échoue au premier jour, et un contrôle qui échoue en
|
||
permanence se désactive.
|
||
|
||
Un contrôle de fin de construction lit le nom du fichier réellement produit et
|
||
exige qu'il dérive de la même source. Ces contrôles se montrent en train d'échouer
|
||
(§ 14.2) : modifier `version.json` seul fait tomber les points 1, 2 et 3, et
|
||
l'échec nomme lequel des endroits est resté en arrière.
|
||
|
||
**Ce que ces contrôles ne couvrent pas, et qui reste un geste humain.**
|
||
Reconstruire après une modification du code **sans** changer `version.json` produit
|
||
un second binaire sous un numéro déjà livré, et aucun des huit points ne le voit :
|
||
le changelog n'a pas changé non plus, donc tout s'accorde. Le refus d'écrasement du
|
||
§ 18.2 n'attrape le cas que si l'exécutable précédent est encore dans le dossier de
|
||
sortie. La discipline qui tient réellement est celle du § 18.4 : livrer exige
|
||
d'écrire une section, et écrire une section pour une version déjà publiée fait
|
||
échouer le point 4.
|
||
|
||
**Le déterminisme n'est pas entamé** (§ 14.7). La version est une **constante
|
||
engendrée à la construction**, jamais une lecture d'horloge à l'exécution. Le
|
||
moteur et le générateur de démonstrations ne la lisent pas. Le script de
|
||
construction, lui, lit la date civile au point 5 ; il n'appartient ni au moteur ni
|
||
au générateur, sur lesquels seuls porte l'interdit.
|
||
|
||
### 18.6 Où la version se voit
|
||
|
||
Un opérateur qui signale un défaut sans pouvoir dire sa version coûte un
|
||
aller-retour complet : la première réponse n'est pas un diagnostic, c'est une
|
||
question. Le logiciel fait donc en sorte qu'on n'ait pas à la chercher.
|
||
|
||
| endroit | forme |
|
||
|---|---|
|
||
| **bandeau d'application**, sur tout écran (§ 8.4) | `2026.10.05.01`, avec la provenance et — quand un plan est ouvert — le libellé de mode |
|
||
| **premier écran** (§ 8.1), liste des événements comme répertoire vide | `version 2026.10.05.01` |
|
||
| **titre de la fenêtre** | `Gestion table tournante Libre — 2026.10.05.01` |
|
||
| **panneau « À propos »** | un bloc d'une ligne que l'opérateur **copie** |
|
||
| **pied de chaque page imprimée** (§ 11.7) | la version, à côté de l'horodatage du placement |
|
||
| **en-tête du fichier d'état** (§ 8.8) | champ `produit_version` |
|
||
|
||
**Le bandeau d'application est le porteur principal, et le titre de fenêtre son
|
||
complément.** Un signalement arrive le plus souvent sous forme d'image, et une
|
||
capture faite à la touche d'impression d'écran, ou cadrée sur le défaut, ne
|
||
contient pas toujours la barre de titre du système — elle ne la contient jamais sur
|
||
la plateforme `web`, où le titre est celui d'un onglet. Un bandeau dessiné par
|
||
l'application est dans l'image quel que soit le cadrage. Le § 8.4 impose en plus un
|
||
cadre permanent **autour du plan** pour le mode : les deux coexistent, le cadre
|
||
signalant le mode là où la main agit, le bandeau portant la version jusque sur la
|
||
liste des événements.
|
||
|
||
**Le bandeau annonce aussi la version de format du fichier ouvert** dès qu'elle
|
||
diffère de celle que le binaire écrit : plus ancienne, « ce fichier se convertira à
|
||
votre première modification » ; plus récente, « ce fichier s'ouvre en lecture
|
||
seule ». Le changelog annonce l'événement à la livraison ; le bandeau l'annonce au
|
||
moment où l'opérateur peut encore se ménager un retour en arrière, c'est-à-dire
|
||
avant le premier geste.
|
||
|
||
**Le bloc copiable** du panneau « À propos » tient en une ligne — version affichée,
|
||
version technique, plateforme, provenance de la construction, version de format
|
||
des fichiers — et se colle dans un courriel. Un chiffre recopié à la main se
|
||
recopie faux. Il porte les **deux** formes parce qu'un opérateur qui lit la fiche
|
||
de propriétés du fichier sous Windows y trouve `2026.1005.1.0` et non
|
||
`2026.10.05.01` ; sans la correspondance écrite quelque part, il cite un numéro qui
|
||
ne figure dans aucun changelog.
|
||
|
||
**Le pied des impressions** porte la version parce qu'une feuille circule et survit
|
||
à la séance. La règle vaut pour la **page** ; un carton découpé et porté au cou
|
||
n'est pas une page et n'en porte rien (§ 11.4).
|
||
|
||
**Le champ dans le fichier d'état** enregistre la version qui a écrit le fichier
|
||
**en dernier**. Il ne compromet pas la sérialisation canonique du § 8.8 — c'est une
|
||
constante de construction, pas une valeur tirée de l'horloge — et il change une
|
||
fois lors du premier geste suivant un remplacement d'exécutable, ce qui coûte un
|
||
correctif et non du bruit à chaque geste. Il **n'est pas** la version de format,
|
||
qui reste un nombre séparé, comparé selon ses propres règles.
|
||
|
||
**Pendant une construction de documentation, le numéro est remplacé par une marque
|
||
de provenance** — dans le bandeau, au pied des pages imprimées et sur la feuille de
|
||
contrôle des badges. La raison est au § 19.4 : sans cette marque, le texte relevé
|
||
de chaque capture d'écran change à chaque incrément de version, et toutes les
|
||
images se réécrivent. Le manifeste, lui, enregistre la **version réelle**, et le
|
||
contrôle de version de la livraison la compare (§ 19.7).
|
||
|
||
---
|
||
|
||
## 19. Les captures d'écran, la documentation engendrée, le banc de mesure
|
||
|
||
> **Vocabulaire.** Le § 7.3 emploie « capture » au sens de la **capture du
|
||
> pointeur**. Cette section écrit toujours **« capture d'écran »** en entier, et
|
||
> aucun autre texte n'emploie « capture » seul. Deux mécanismes sans rapport
|
||
> portant le même mot dans un même document se confondent à la lecture rapide — et
|
||
> la lecture rapide est celle que fait l'implémenteur qui cherche une règle.
|
||
|
||
### 19.1 Une seule boucle, pas deux chantiers
|
||
|
||
Le scénario de pilotage et la documentation sont **un seul mécanisme**. Le
|
||
scénario conduit l'application le long du parcours de l'opérateur (§ 2.1) et dépose
|
||
une capture d'écran à chaque étape ; la documentation affiche ces captures et rien
|
||
d'autre. `GUIDE-USAGE.md` (§ 16.1) est la **lecture** d'un parcours que la machine
|
||
vient de refaire.
|
||
|
||
La conséquence est la raison d'être du couplage : **un guide ne peut plus vieillir
|
||
en silence.** Si une commande change de nom, si un écran disparaît, si un bouton
|
||
cesse d'être atteignable, l'étape correspondante échoue, la construction de la
|
||
documentation s'arrête en nommant l'étape, et **elle ne republie pas les images du
|
||
passage précédent** : une documentation partielle qui se complète avec d'anciennes
|
||
images est exactement le défaut que la boucle existe pour supprimer.
|
||
|
||
Le pilotage s'exécute sur la plateforme `web` (§ 13.1), servie en local. Le
|
||
pilote, le navigateur et son pilote de protocole sont des **dépendances de
|
||
développement** : l'exigence « aucune connexion requise » du § 2 porte sur le
|
||
logiciel livré, jamais sur l'atelier qui le construit.
|
||
|
||
Les scripts s'exécutent sous `node`, dans la même langue que tout le reste
|
||
(§ 13.2). **Ils ne calculent aucun indicateur.** Quand une étape vérifie un chiffre
|
||
affiché, elle appelle la fonction du moteur qui l'a produit et compare : la même
|
||
implémentation sollicitée deux fois, un **oracle** au sens du § 14.11, jamais une
|
||
seconde arithmétique.
|
||
|
||
### 19.2 Le pilote, et les propriétés dont le reste dépend
|
||
|
||
Le pilote est **Selenium**, conduit par sa liaison `node`. Le choix se remplace,
|
||
mais il engage quatre propriétés dont toute la section dépend, et une substitution
|
||
se juge sur elles, pas sur le nom :
|
||
|
||
1. **Des événements de pointeur que le navigateur traite comme réels.** Sans eux,
|
||
un glissement du § 7.1 ne se conduit pas et la moitié du parcours n'est pas
|
||
atteignable.
|
||
2. **La capture d'écran d'un élément**, et non de la fenêtre. Elle **recadre sur la
|
||
surface de l'application** : le cadre de fenêtre et la barre système — ce qui
|
||
diffère entre `web` sous Linux et `electron` sous Windows — sortent de
|
||
l'image, et le guide d'usage reste vrai sur la plateforme livrée.
|
||
3. **La maîtrise de la taille du cadre d'affichage.** Voir § 19.4 : la commande du
|
||
standard dimensionne la **fenêtre**, pas le cadre d'affichage, et le logiciel ne
|
||
s'en contente pas.
|
||
4. **Un navigateur lancé sans accès réseau hors de la boucle locale.** Le § 2
|
||
interdit que quoi que ce soit sorte de la machine ; une requête sortante est
|
||
alors un **échec de connexion observable**, et non une requête qu'il faudrait
|
||
avoir su écouter.
|
||
|
||
Deux propriétés que le logiciel **ne demande pas** au pilote, parce qu'aucun pilote
|
||
ne les offre de façon portable :
|
||
|
||
- **La lecture de la console.** Le logiciel installe donc, **avant le chargement de
|
||
ses propres scripts**, un collecteur de page — `error`, `unhandledrejection`, et
|
||
l'enveloppe de `console.error` — qui accumule dans un tableau que chaque étape
|
||
relit. Une étape qui trouve ce tableau non vide échoue en citant la première
|
||
entrée. Le mécanisme est portable et il attrape ce qu'un journal de console
|
||
n'attrape pas : une promesse rejetée sans gestionnaire.
|
||
- **L'émulation d'une préférence système.** Le logiciel règle son **propre**
|
||
réglage de thème, qui est celui que l'opérateur manipule (§ 13.3), et injecte une
|
||
feuille de style qui annule transitions et animations. La préférence système
|
||
n'est consultée qu'au premier lancement, que le profil neuf du § 19.4 produit à
|
||
chaque exécution : **le scénario la pose explicitement et ne la déduit jamais.**
|
||
|
||
### 19.3 Le parcours, et ce qu'il photographie
|
||
|
||
Un parcours ordonné, du premier écran à l'impression. **Son ordre est celui des
|
||
douze étapes du § 2.1, et c'est la seule source de cet ordre** : chaque capture
|
||
d'écran déclare l'étape à laquelle elle appartient, et un contrôle du niveau `node`
|
||
échoue si une étape n'a **aucune** capture, ou si l'ordre des captures contredit
|
||
l'ordre des étapes.
|
||
|
||
Chaque étape **affirme une condition visible avant de prendre sa capture
|
||
d'écran** — l'élément nommé est présent et porte le libellé attendu — et
|
||
l'exécution s'arrête sur la première condition fausse et sur la première entrée du
|
||
collecteur d'erreurs. **Une étape n'attend jamais un délai pour laisser le logiciel
|
||
rattraper son retard : elle attend un état.** Un délai n'est légitime que lorsque
|
||
la durée *est* la chose éprouvée — la pression longue du § 7.3, le retour
|
||
automatique en lecture du § 8.4 — et l'étape avance alors l'horloge contrôlée du
|
||
§ 19.4 plutôt que d'attendre le temps réel.
|
||
|
||
**Le parcours principal construit son événement, il ne charge pas une
|
||
démonstration.** C'est le parcours de l'opérateur. Un CSV ne reproduit pas une
|
||
configuration (§ 10.3) ; une démonstration chargée court-circuite précisément les
|
||
sept premiers écrans que le guide doit montrer. Les démonstrations livrées
|
||
n'interviennent qu'aux étapes où une configuration **déjà tendue** est le propos.
|
||
|
||
| fichier | configuration | ce que la capture d'écran montre | ce que son étape affirme |
|
||
|---|---|---|---|
|
||
| `01-premier-ecran.png` | répertoire neuf | le répertoire vide : la commande de création et les configurations de démonstration offertes, **jamais une liste vide** (§ 8.1) | chaque configuration que le catalogue déclare est nommée et cliquable, et **leur nombre est lu dans le catalogue, jamais écrit dans le scénario** |
|
||
| `01b-passer-en-ecriture.png` | idem | la commande « Modifier » à place fixe, et le cadre de mode autour du plan (§ 8.4) | avant ce geste, une tentative de modification est refusée ; après, elle passe |
|
||
| `02-import-apercu.png` | `participants_cas_limites.csv` | l'aperçu obligatoire (§ 10.1) : encodage et séparateur détectés et **nommés**, association par en-tête, nombre de lignes refusées | l'encodage et le séparateur affichés sont ceux que le module d'import a retenus ; l'aperçu n'est pas contournable |
|
||
| `03-import-rapport.png` | idem | le rapport : lignes valides, orthographes d'appartenance fondues avec leur compte, lignes refusées avec leur motif | au moins un groupe d'orthographes fondu et au moins une ligne refusée sont visibles |
|
||
| `04-grille-participants.png` | l'événement en construction | la grille éditable, le tri, le filtre, une personne exclue (§ 10.2) | le compte affiché égale le compte importé |
|
||
| `05-tables-etat.png` | idem, deux tables posées par le scénario | deux tables à 8 places dont l'une **suit le défaut** et l'autre porte une **surcharge de même valeur** (§ 6.1) | les deux états s'affichent différemment **à valeur égale** |
|
||
| `06-defaut-consequence.png` | idem | l'énoncé chiffré avant application — tables suivant le défaut, surchargées épargnées, total des sièges contre nombre de participants | le total annoncé égale celui que recalcule le moteur |
|
||
| `07-plan-tables.png` | idem | le plan, ses tables à l'échelle, l'échelle graphique, la mention « disposition relative » (§ 7.7) | la mention est présente tant qu'aucune dimension de salle n'est saisie |
|
||
| `08-animateurs-designes.png` | idem | un siège **cadenas fermé + anneau** (réservé, tous les tours) et un siège **hachuré à cadenas ouvert** (titre non pourvu), avec la liste numérotée qui énonce les deux états en toutes lettres (§ 7.6), et les deux listes de travail du § 5.8 | les marques sont des éléments SVG, **aucun point de code emoji dans le sous-arbre** ; aucune chaise ne porte plus de deux marques |
|
||
| `09-refus-sur-place.png` | idem | un refus du § 5.9 affiché **sur la place concernée**, nommant sa cause et le geste qui le lève | le refus photographié est l'un de ceux qui subsistent à la pose — `k_t > c_t`, une personne exclue, deux places sur un même tour, un dépôt sur une place réservée à un autre ; aucune boîte de dialogue n'est ouverte |
|
||
| `10-diagnostic.png` | **grande démonstration** | le diagnostic préalable (§ 5.6), le plafond a priori, les **99 retours imposés** énoncés à part, et le plancher de l'écart d'itinéraire | les valeurs affichées égalent celles du moteur pour la même entrée ; le plafond des quatre animateurs des tables de 7 vaut **24** |
|
||
| `11-propositions.png` | l'événement en construction | plusieurs propositions comparées, l'ordre de classement **écrit dans l'interface** (§ 5.7), les colonnes de diversité | au moins trois propositions ; l'ordre affiché est celui que produit la clé de tri du moteur |
|
||
| `12-qualite-profil.png` | **grande démonstration** | la courbe en escalier, deux séries sur **un seul axe**, le plafond réalisé tracé dans le même ordre, le pire cas au premier rang (§ 12.3) | la page a **dessiné** plutôt que d'opposer son refus — c'est ainsi que la garde du § 12.3 montre qu'elle n'a pas été déclenchée |
|
||
| `13-qualite-collisions.png` | **petite démonstration, variante « conflit inévitable »** | les barres horizontales à deux segments, « prouvé inévitable » et « reste », en **collisions cumulées**, avec l'effectif, les paires distinctes et l'**excédent** en encre secondaire | le segment prouvé vaut **4 collisions cumulées** et égale le plancher du diagnostic, au chiffre près ; les lignes sont triées sur l'excédent (§ 12.4) |
|
||
| `14-qualite-paires.png` | **12 personnes, 4 tables de 3, 6 tours**, construite | le tableau des paires répétées trié, pas la matrice (§ 12.5) | le tableau n'est pas vide, et chaque ligne mène au tour fautif |
|
||
| `15-pas-de-matrice.png` | **grande démonstration** | à 260 participants, le tableau et non la matrice, avec la phrase qui le dit | aucune matrice n'est dessinée |
|
||
| `16-minimum-atteint.png` | **petite démonstration** | **« minimum atteint »**, les compteurs de collision et de répétition **mesurés** à zéro, la ligne secondaire « ancrés » à **« — »** (§ 15.3) | la mention est présente, les compteurs mesurés sont nuls, et la population vide affiche un tiret, **jamais zéro** |
|
||
| `17-comparaison-manque.png` | **variante sans exception** (§ 15.1) | les deux profils de manque qui se croisent, l'axe commun, le zéro libellé, les trois effectifs de rangs (§ 12.10.6) | la page écrit sa question en toutes lettres et les trois effectifs ; aucune échelle propre à une courbe |
|
||
| `18-comparaison-escalier.png` | idem | l'escalier du manque, jumeau tabulaire de la vue précédente | les effectifs cumulés du tableau reproduisent la courbe |
|
||
| `19-ajustement.png` | l'événement retenu | le déplacement d'une personne après la retenue, l'interface disant **lequel des deux gestes** est en cours (§ 5.8) | le libellé de l'état des données est visible |
|
||
| `20-mode-lecture.png` | idem | le cadre et le libellé permanents autour du plan, et le bandeau d'application (§ 8.4) | un geste de chaque famille — glisser une personne, glisser une table, tirer une poignée, poser une réservation — est tenté, et l'empreinte d'état est inchangée après les quatre |
|
||
| `21-historique.png` | idem | la liste des instants, le fil courant distingué des fils abandonnés, un jalon nommé (§ 8.3) | au moins un fil abandonné est présent et marqué comme tel |
|
||
| `22-plan-brouillon.png` / `23-plan-retenu.png` | idem | l'**aperçu d'impression** du plan (§ 11.7) avec son cartouche, son échelle graphique et son horodatage, **avec** puis **sans** le filigrane BROUILLON | **deux images, parce qu'une absence ne se photographie pas** : le filigrane présent sur l'un, absent sur l'autre, et la paire est le seul exposé lisible de la règle |
|
||
| `24-badges-controle.png` / `25-badges-planche.png` | idem | la **feuille de contrôle** avec son échelle de 100 mm et ses comptes, puis une **planche** de quatre A6 et ses trois filets de coupe | voir § 19.6 — ces deux images ne viennent pas du navigateur. La paire est nécessaire : la feuille de contrôle est ce que l'opérateur mesure sous une règle avant d'engager la rame, et une planche seule ne le montre pas |
|
||
|
||
**Trois pièges de dénombrement que cette table évite, et qui sont la raison de sa
|
||
forme.**
|
||
|
||
- **Le premier écran n'offre pas « deux démonstrations ».** Le § 15 en livre
|
||
**quatre** configurations. Une étape qui en affirme deux échoue, ou pire, fige un
|
||
compte faux. Le compte se lit dans le catalogue.
|
||
- **Les paires répétées ne sont pas garanties sur la grande démonstration** — le
|
||
§ 12.5 donne le dénombrement. Le tableau se photographie sur une configuration où
|
||
la répétition est forcée.
|
||
- **Les collisions d'appartenance ne sont pas garanties non plus sur la grande** :
|
||
sa plus grosse appartenance compte 12 membres pour 33 tables. La variante
|
||
« conflit inévitable » les force.
|
||
|
||
**Les configurations qu'aucun catalogue ne porte sont construites, pas
|
||
cataloguées.** Trois besoins sortent des quatre entrées du § 15 : les paires
|
||
répétées sur 12 personnes, 4 tables de 3 et 6 tours ; le témoin de pagination à 257
|
||
badges ; les cas dégénérés du § 12.6. Le scénario les **construit**, par des
|
||
scénarios courts, et le catalogue reste à quatre entrées — sans cette règle,
|
||
l'étape `01-premier-ecran` change de valeur chaque fois qu'une épreuve a besoin
|
||
d'une configuration de plus.
|
||
|
||
**Les cas dégénérés du § 12.6 se photographient à part**, par des scénarios courts
|
||
qui montent la configuration visée et vont droit à l'écran : aucun placement, une
|
||
seule table, un seul tour, une seule appartenance, aucune collision, une personne
|
||
en réserve, un plafond réalisé nul. La raison n'est pas la fragilité d'une chaîne
|
||
longue : **chacun exige une configuration différente**, et un parcours linéaire
|
||
n'en porte qu'une à la fois.
|
||
|
||
S'y ajoute, au même titre, le fichier `participants_cas_limites_cp1252.csv`
|
||
(§ 10.3) : la décision d'encodage en quatre temps du § 10.1 est le mécanisme dont
|
||
la défaillance est la plus muette — « Benoît » devient « Benoît » et aucun calcul
|
||
n'échoue — et c'est la raison pour laquelle le guide la montre.
|
||
|
||
**La grande démonstration n'intervient qu'aux étapes où l'échelle est le propos.**
|
||
Le coût d'un parcours mené sur 260 personnes est dominé par la génération, et il
|
||
est **mesuré, non supposé** (§ 14.1) : le seuil au-delà duquel une étape quitte le
|
||
parcours principal est cette mesure.
|
||
|
||
### 19.4 Ce qui rend une capture d'écran reproductible
|
||
|
||
Une capture d'écran qui change à chaque exécution rend sa revue impossible : le
|
||
diff est entièrement bruit, et la seule image qui comptait s'y noie. Le logiciel
|
||
neutralise chaque source de variation nommément.
|
||
|
||
| source de variation | neutralisation |
|
||
|---|---|
|
||
| **la règle d'arrêt de la recherche** | **un compte, jamais une durée** (§ 5.10). C'est la source décisive, celle qui rendrait toutes les autres inutiles : une recherche bornée par le temps rend le placement dépendant de la vitesse de la machine, et la capture du plan de salle ne serait reproductible sur aucune autre machine que celle de référence |
|
||
| **le placement engendré** | une **graine entière écrite dans le scénario**, passée à la commande de génération ; à graine et entrée égales, le placement est identique |
|
||
| **l'ordre d'itération et les comparaisons de chaînes dans le moteur** | la règle du § 15.5, point 4, **étendue du générateur au moteur** : aucun `localeCompare`, aucun parcours reposant sur l'ordre des clés d'un objet, départage par identifiant entier |
|
||
| **l'ordre des propositions à égalité** | l'identifiant vient de la place dans la suite de graines dérivées, **jamais de l'ordre d'achèvement** (§ 5.7) : sinon le départage se fait par la vitesse de la machine, et deux exécutions classent deux propositions à égalité dans deux ordres |
|
||
| **la taille du cadre d'affichage** | **la commande du standard dimensionne la fenêtre, pas le cadre d'affichage** : la hauteur de la barre du navigateur diffère d'une version à l'autre, et un cadre laissé libre gouverne `k_ajusté` (§ 7.2) et donc le seuil de 24 px au-delà duquel les sièges cessent d'être des cibles (§ 7.3). Le scénario pose le rectangle de fenêtre, **relit `innerWidth` et `innerHeight`, corrige, et échoue** s'il n'obtient pas les valeurs visées |
|
||
| **le facteur d'échelle de l'affichage** | fixé **au lancement du navigateur**, pas par une commande du standard, qui n'en offre aucune |
|
||
| **le mode d'affichage du navigateur** | un seul mode, nommé, inscrit au manifeste : les métriques de texte en diffèrent |
|
||
| **le cadrage mémorisé** | le § 8.5 range le cadrage dans un fichier de réglages locaux : le scénario démarre sur un **profil neuf**, ce qui lui donne du même geste un dossier de travail neuf |
|
||
| **le numéro d'ordre du nom de démonstration** | charger une démonstration crée « ‹nom› 2 » à la deuxième exécution (§ 15) ; le répertoire neuf ramène chaque exécution au premier ordinal |
|
||
| **l'horloge** — horodatage d'historique, pied de page, « dernière modification », retour automatique en lecture | **une seule source d'horloge dans toute l'application hors moteur**, et **elle possède aussi les minuteries**. Un instant figé ne suffit pas : un compte à rebours bâti sur une minuterie du moteur d'exécution continue d'avancer sous une horloge gelée, et **le retour automatique en lecture tombe au milieu d'une étape**. Le scénario installe une horloge dont il contrôle l'avance, ce qui lui permet aussi d'éprouver les durées |
|
||
| **le numéro de version affiché** | une construction de documentation pose une **marque de provenance à la place du numéro** (§ 18.6), dans le bandeau comme au pied des pages imprimées et sur la feuille de contrôle des badges. Sans elle, le texte relevé de chaque capture change à chaque livraison, l'empreinte d'étape change avec lui, et **toutes les images se réécrivent à chaque incrément** — précisément le bruit que la réécriture conditionnelle du § 19.5 existe pour supprimer |
|
||
| **le chemin affiché** dans la liste des événements (§ 8.1) | racine de travail neutre, et le contrôle **refuse une capture dont le texte relevé — celui de tous les nœuds de texte de la région photographiée, pas des seuls éléments que l'étape nomme — porte un chemin hors de cette racine**. Un chemin réel porte un nom de compte, qu'une documentation publie |
|
||
| **la police réellement rendue** | la police embarquée (§ 11.1) est celle du pilotage. Le scénario **mesure une chaîne témoin** par `getComputedTextLength()` et la compare à la valeur enregistrée **avec une tolérance relative, constante mesurée** (§ 14.1) — exiger l'égalité exacte reproduirait, sur une mesure de texte, l'erreur que cette section refuse sur les pixels. La tolérance se fixe au-dessous de l'écart qu'introduit un repli de police, qui est d'un tout autre ordre |
|
||
| **le thème** | réglé par le réglage de l'application, non par une préférence système émulée (§ 19.2) |
|
||
| **les transitions et animations** | annulées par une feuille de style injectée. Une capture prise à mi-transition est une image différente à chaque exécution |
|
||
| **l'horodatage interne du PDF** | il vient de la même source d'horloge, donc de l'instant contrôlé |
|
||
| **le tramage du rendu** — lissage des glyphes, accélération matérielle | **ne se neutralise pas** d'une machine à l'autre. C'est la source que le logiciel ne prétend pas supprimer, et elle décide de tout ce qui suit |
|
||
|
||
**Parce que le tramage ne se neutralise pas, la fraîcheur ne se contrôle jamais sur
|
||
les pixels.** Une empreinte d'image échouerait sur toute machine autre que celle
|
||
qui a produit la référence ; un contrôle qui échoue partout se désactive, et il ne
|
||
reste rien. Le contrôle porte donc sur **l'état**, pas sur l'image.
|
||
|
||
### 19.5 Le manifeste, et l'écriture conditionnelle
|
||
|
||
Chaque capture d'écran porte une entrée dans un **manifeste engendré** :
|
||
identifiant d'étape, **étape du § 2.1 à laquelle elle appartient**, version réelle
|
||
de l'application, graine, dimensions relues du cadre d'affichage, facteur
|
||
d'échelle, mode d'affichage, thème, instant contrôlé, le **texte relevé** de la
|
||
région photographiée, les **styles calculés** des marques porteuses d'information,
|
||
et une **empreinte canonique de l'état de l'événement** au sens du § 8.8.
|
||
|
||
**L'empreinte d'une étape est celle de son entrée de manifeste entière, l'image
|
||
exclue** — et non celle du seul état de l'événement. Les étapes 10 à 18 consultent
|
||
le même état sous des vues différentes : une empreinte réduite à l'état leur
|
||
donnerait à toutes la même valeur, et une régression de la page de qualité ne
|
||
réécrirait jamais son image.
|
||
|
||
**Le scénario ne réécrit un fichier image que si l'empreinte de son étape a
|
||
changé.** Sans cette règle, une exécution sur une autre machine réécrit toutes les
|
||
images par le seul effet du lissage, le dépôt enfle à chaque passage et le diff
|
||
cesse d'être lisible. Avec elle, **une image qui change dans un diff signifie que
|
||
l'état qu'elle montre a changé** — c'est la propriété qui rend la revue possible.
|
||
|
||
Les styles calculés relevés servent deux fois : ils détectent une feuille de style
|
||
qui ne s'est pas appliquée, et ils **produisent le tableau des ratios de contraste
|
||
que le § 14.5 exige mesurés sur la couleur relue du rendu**, **pour les deux
|
||
thèmes**. Ce tableau est engendré, pas recopié.
|
||
|
||
Une réserve que le style calculé impose, et qui devient une règle de conception :
|
||
**le style calculé rend la couleur résolue d'un élément, non le pixel composé.**
|
||
C'est pourquoi le § 14.5 interdit toute opacité partielle, tout filtre et tout mode
|
||
de fusion sur le texte et sur les marques porteuses d'information.
|
||
|
||
### 19.6 Les images qui ne viennent pas du navigateur
|
||
|
||
**La planche de badges est le seul PDF** (§ 11.1), et le logiciel la construit
|
||
depuis le modèle par une bibliothèque embarquée, jamais par le moteur de rendu. Une
|
||
capture d'écran d'un aperçu, pour la montrer, exigerait une **seconde
|
||
implémentation de la mise en page** — la double arithmétique que le § 13.2
|
||
interdit, transposée à la pagination, avec le même défaut : les deux dérivent et
|
||
les tests des deux restent verts.
|
||
|
||
Le logiciel engendre donc ces images **sous `node`, en tramant les pages du PDF
|
||
effectivement produit**. Le rasteriseur est une dépendance de construction de la
|
||
documentation. Ce chemin est plus fort qu'un aperçu : l'image montre l'artéfact
|
||
livré, et non une approximation à l'écran. Sa fraîcheur se contrôle sur
|
||
l'**empreinte canonique du PDF produit**, que l'horloge contrôlée du § 19.4 rend
|
||
stable, et non sur les pixels tramés.
|
||
|
||
**Le plan, lui, n'est plus un PDF** : ses deux images viennent du navigateur, sur
|
||
la **vue d'aperçu d'impression** du § 11.7, qui applique au document vivant la
|
||
feuille de style d'impression réelle. Ce n'est pas une seconde mise en page : c'est
|
||
la même, regardée avant d'être envoyée à l'imprimante.
|
||
|
||
Le plan **à l'écran** reste une capture ordinaire du navigateur : il est la surface
|
||
de travail, et c'est à ce titre que le guide le montre.
|
||
|
||
### 19.7 Comment une capture d'écran périmée se détecte
|
||
|
||
Une documentation dont les images montrent une version antérieure est pire qu'une
|
||
documentation sans image : elle fait croire au lecteur qu'il regarde le logiciel
|
||
qu'il a sous les yeux. Trois contrôles, à **trois moments**, et le moment compte
|
||
autant que le contrôle.
|
||
|
||
**Au niveau `node`, à chaque modification** (§ 14.4), sans navigateur :
|
||
**l'appariement**. Toute image référencée par la documentation a son entrée au
|
||
manifeste, et toute image présente est référencée. Une orpheline est un échec dans
|
||
les deux sens. S'y ajoute le contrôle d'ordre du § 19.3 : une étape du § 2.1 sans
|
||
capture, ou des captures dont l'ordre contredit celui des étapes, font échouer la
|
||
construction. Ces contrôles passent sur une machine sans navigateur, à toute heure,
|
||
et c'est pourquoi ils sont à ce niveau — **bornés** à leur périmètre, dépendances
|
||
et sorties de construction exclues (§ 14.14).
|
||
|
||
**Au niveau `node`, à la construction d'une livraison** : **la version**. La version
|
||
réelle inscrite au manifeste égale celle que la construction produit. Ce contrôle
|
||
**ne descend pas au cycle de chaque modification** : le manifeste n'est réécrit que
|
||
par une exécution en navigateur, donc un simple incrément de version le rendrait
|
||
rouge à chaque modification jusqu'à ce que quelqu'un relance le pilotage. Un
|
||
contrôle qui échoue en permanence se désactive. Au cycle court, l'écart de version
|
||
est **affiché, nommé et non bloquant** ; à la livraison, il bloque.
|
||
|
||
**Au niveau navigateur, avant chaque livraison** : le scénario rejoue le parcours et
|
||
compare, étape par étape, l'empreinte d'étape et le texte relevé à ceux du
|
||
manifeste. Un écart nomme l'étape et la nature de l'écart.
|
||
|
||
Ces contrôles **refusent de passer sur un balayage vide** (§ 14.2) : « zéro capture
|
||
examinée » n'est pas « zéro capture périmée ». Et chacun est montré en train
|
||
d'échouer avant d'être retenu — une image volontairement remplacée par une version
|
||
antérieure doit faire échouer la construction.
|
||
|
||
**Ce que le contrôle d'état ne voit pas, et il faut le dire : une régression
|
||
purement visuelle à état identique.** Les styles calculés relevés en attrapent une
|
||
partie — une couleur d'état devenue grise, un contraste tombé sous le seuil — mais
|
||
une image mal cadrée, une table qui déborde ou un texte qui se chevauche passent.
|
||
La relecture humaine du § 16.1 reste la seule garde.
|
||
|
||
### 19.8 Ce qui est engendré, ce qui est écrit à la main
|
||
|
||
La frontière passe là où une divergence est **mécaniquement détectable**.
|
||
|
||
**Écrit à la main, jamais engendré** : toute phrase qui décrit une fonctionnalité,
|
||
qui dit pourquoi elle existe, et qui dit ce qu'un écran veut dire. Un texte
|
||
explicatif engendré à partir du code décrit ce que le code fait, ce que le lecteur
|
||
voit déjà sur l'image, et jamais ce qu'il cherche. Les légendes des captures
|
||
d'écran sont également manuelles : nommer ce qu'il faut regarder dans une image est
|
||
un jugement.
|
||
|
||
**Engendré** :
|
||
|
||
| contenu | source unique |
|
||
|---|---|
|
||
| les captures d'écran et leur manifeste | le scénario, et pour les images de la planche, la construction sous `node` |
|
||
| le tableau des réglages et de leurs valeurs par défaut | la même définition que lit l'interface |
|
||
| le tableau des colonnes CSV reconnues et de leurs synonymes (§ 10.1) | le module d'import |
|
||
| la liste des commandes et de leurs libellés d'historique (§ 8.2) | le registre des commandes |
|
||
| les raccourcis clavier | la table des raccourcis |
|
||
| le tableau des ratios de contraste, **par thème** (§ 14.5) | les styles relevés au pilotage |
|
||
| les grandeurs des démonstrations et leurs plafonds a priori (§ 15.1) | les fichiers de démonstration livrés, lus, jamais recopiés |
|
||
| le tableau des constantes mesurées (§ 14.1) | le banc du § 19.10 |
|
||
| le **numéro de version**, partout où il paraît, **nom de l'exécutable compris** | `version.json` (§ 18.3) |
|
||
|
||
Un tableau recopié à la main diverge du logiciel en un commit et personne ne s'en
|
||
aperçoit : c'est le même mode de défaillance que la double arithmétique du § 13.2,
|
||
transposé à la prose.
|
||
|
||
**Le mécanisme d'insertion.** Les sources de documentation portent des **marqueurs
|
||
nommés** ; la construction remplace le contenu entre marqueurs et ne touche à rien
|
||
d'autre. Elle dispose d'un **mode de vérification** qui échoue quand une région
|
||
engendrée diffère de ce qu'elle produirait : une correction faite à la main dans une
|
||
région engendrée fait échouer la construction au lieu d'être perdue au passage
|
||
suivant (§ 14.3).
|
||
|
||
**`GUIDE-WINDOWS.md` sort de la boucle** (§ 16.1). Le pilotage s'exécute sur
|
||
`web` sous Linux ; les écrans que ce guide décrit — obtenir l'exécutable, le
|
||
premier lancement, le blocage par un antivirus, l'emplacement des fichiers dans
|
||
l'explorateur — appartiennent à `electron` sous Windows et **aucun scénario ne les
|
||
produit**. Le guide les décrit en toutes lettres, ou les illustre par des images
|
||
faites à la main qui portent la mention de leur origine. Prétendre les engendrer
|
||
livrerait des images de Linux dans un guide Windows.
|
||
|
||
### 19.9 La section sur l'algorithme
|
||
|
||
Son lecteur est l'opérateur. Il l'ouvre avec **deux questions, toujours les
|
||
mêmes** : pourquoi deux propositions issues du même plan diffèrent, et pourquoi
|
||
relancer aide parfois et jamais d'autres fois. La section répond à ces deux
|
||
questions et s'arrête.
|
||
|
||
**Ce qu'elle contient, dans cet ordre.**
|
||
|
||
1. **Le problème, en une phrase** : asseoir chacun à chaque tour de sorte que le
|
||
plus mal servi rencontre le plus de monde possible.
|
||
2. **Pourquoi il n'a pas de réponse évidente**, par un compte, pas par un mot
|
||
savant. Douze personnes se répartissent sur quatre tables **numérotées** de
|
||
trois de **369 600 façons pour un seul tour** — 12! divisé par 6 quatre fois —
|
||
et sur quatre tours le nombre dépasse **10²²**. Le logiciel n'essaie pas toutes
|
||
les combinaisons, et la petite démonstration est le plus petit cas du catalogue.
|
||
Ce compte est vérifiable et remplace avantageusement toute affirmation sur la
|
||
difficulté du problème.
|
||
3. **Comment le logiciel cherche** : il part d'un placement complet dont le point
|
||
de départ dépend de la **graine**, **échange** des personnes entre tables, garde
|
||
ce qui améliore, et s'arrête sur un **compte** de mouvements sans amélioration
|
||
(§ 5.10). D'où **la réponse à la première question** : deux recherches parties
|
||
de deux points différents s'arrêtent sur deux placements différents, et aucun
|
||
des deux n'est « le bon ». Cette phrase est **vérifiable, et elle est
|
||
vérifiée** : un test sous `node` affirme que deux graines donnent deux résultats
|
||
et qu'une même graine donne deux fois le même. Un énoncé vérifiable vieillit
|
||
mieux qu'un énoncé vague : si le moteur est réécrit et que la phrase cesse
|
||
d'être vraie, le test le dit.
|
||
4. **Ce que les contraintes font** : chacune est un **objectif**, pas un interdit
|
||
(§ 5.3). Quand elles ne tiennent pas ensemble, le logiciel produit le meilleur
|
||
compromis et dit lequel, au lieu de refuser et de ne rien montrer.
|
||
5. **Le plafond**, expliqué sur la petite démonstration : quatre tours, deux
|
||
voisins par tour, **huit rencontres au plus sur onze personnes**. Personne ne
|
||
peut faire mieux, et ce n'est pas un défaut du logiciel. Puis le même calcul sur
|
||
une table de huit — quatre tours, sept voisins, **28** — et le cas des quatre
|
||
animateurs des tables de sept de la grande démonstration, plafonnés à **24** :
|
||
**la relance ne déplace jamais un plafond.**
|
||
6. **Ce que « minimum atteint » signifie** : le logiciel l'écrit quand la personne
|
||
la plus mal servie atteint ce que l'arithmétique autorise au mieux, **mesuré
|
||
contre le plafond a priori** (§ 5.5) — raison pour laquelle la grande
|
||
démonstration ne le décerne jamais. Relancer alors ne sert plus à rien. **Et ce
|
||
que son absence ne signifie pas** : « atteignabilité inconnue » dit que le
|
||
logiciel ne sait pas si mieux existe, pas qu'il existe.
|
||
7. **Ce que relancer ne peut pas** : effacer une collision portée au segment
|
||
« prouvé inévitable » (§ 12.4), ni rendre une table qui n'existe pas. Le
|
||
diagnostic (§ 5.6) dit **avant** la recherche ce qui est hors d'atteinte et quel
|
||
changement de configuration le lèverait — une table de plus, un tour de moins,
|
||
une appartenance redistribuée.
|
||
8. **Comment comparer deux propositions** : par le **manque**, et pourquoi les
|
||
nombres bruts ne se comparent pas dès qu'une table est incomplète (§ 12.10.1).
|
||
La section explique les trois chiffres de l'en-tête et, surtout, pourquoi un
|
||
manque nul sur un itinéraire bas n'est pas une bonne nouvelle.
|
||
9. **Une lecture de l'écart entre propositions d'une même génération**, donnée pour
|
||
ce qu'elle est : **un indice, pas une preuve**. Quand la meilleure et la pire
|
||
proposition d'une même génération portent le même minimum, c'est le plus souvent
|
||
qu'une borne le tient — et la tuile du § 12.2 qui affiche le plafond à côté du
|
||
minimum le dit en un coup d'œil, là où l'écart ne fait que le suggérer.
|
||
|
||
**Ce qu'elle ne contient pas** : aucun pseudo-code, aucun nom de fonction, aucun
|
||
nom de module, aucune complexité asymptotique, aucun nom de famille d'algorithmes.
|
||
Ces éléments n'apprennent rien à l'opérateur, se périment à la première réécriture
|
||
du moteur, et **font croire au lecteur que le texte ne lui est pas destiné** —
|
||
après quoi il ne lit plus la section, qui est la seule réponse écrite à la question
|
||
qu'il posera.
|
||
|
||
La section s'appuie sur les captures d'écran `10`, `11`, `12`, `16`, `17` et `18` :
|
||
le diagnostic, les propositions classées, le profil trié, « minimum atteint » sur
|
||
la petite démonstration, et les deux vues de comparaison. Le contraste de la
|
||
variante « conflit inévitable » (§ 15.3) — une personne déplacée d'une appartenance
|
||
à l'autre, et la mention bascule — est le plus court exposé de toute la section, et
|
||
il tient en deux images.
|
||
|
||
### 19.10 Le banc de mesure
|
||
|
||
La spécification exige à plusieurs endroits un seuil « mesuré » — le nombre de
|
||
sièges au-delà duquel le dessin cesse d'être lisible (§ 6.2), la taille de
|
||
caractère en deçà de laquelle le plan se découpe en pages (§ 11.7), le seuil de
|
||
participants sous lequel la matrice se dessine (§ 12.5), la largeur de troncature
|
||
des noms longs (§ 12.6), le seuil de mouvement qui sépare un clic d'un glisser
|
||
(§ 8.4), les constantes de géométrie du texte (§ 14.1), le nombre de valeurs
|
||
distinctes de manque sous lequel la courbe cède la place à une phrase (§ 12.6), le
|
||
seuil de zoom qui retire les listes de noms (§ 17, point 6). Le niveau navigateur
|
||
est le seul endroit qui le peut, puisqu'une mesure sur le rendu réel est
|
||
précisément ce que `jsdom` ne sait pas faire (§ 14.4).
|
||
|
||
Le logiciel porte donc, **au même niveau que le scénario et avec le même cadre
|
||
d'affichage fixé**, un banc de mesure qui produit un tableau de constantes **avec,
|
||
pour chacune, ce qu'elle mesure, sa valeur et la configuration sur laquelle elle a
|
||
été relevée** (§ 14.1). Ce tableau est une région engendrée de la documentation.
|
||
|
||
Deux mesures qu'il faut distinguer :
|
||
|
||
- **Le seuil de 24 px du § 7.3** — en deçà duquel les sièges cessent d'être des
|
||
cibles — est une **géométrie**. Deux captures d'écran l'encadrent délibérément, à
|
||
cadre d'affichage fixé, et la documentation montre la bascule.
|
||
- **Le seuil de zoom qui retire les listes de noms** est un **temps par image**.
|
||
Aucune capture d'écran ne le mesure : une image est muette sur la durée qui l'a
|
||
produite. Le banc relève le temps par image pendant un déplacement de vue à
|
||
plusieurs niveaux de zoom, sur la grande démonstration et ses 33 tables avec
|
||
listes, et c'est cette courbe qui fixe la constante. La mesure est permise ici :
|
||
le § 14.7 interdit `performance.now` dans le moteur et dans le générateur, pas
|
||
dans un banc.
|
||
|
||
Le même banc répond au § 17, point 4 : le coût de la maximisation du plafond a
|
||
priori sur les itinéraires admissibles, annoncé immédiat pour une dizaine de tables
|
||
et jamais mesuré sur 33 tables et 4 tours, se relève **sous `node`** sur la grande
|
||
démonstration et sa variante.
|
||
|
||
**Deux mesures lui échappent et appartiennent au niveau manuel** : le décalage des
|
||
imprimantes visées et leur marge non imprimable (§ 11.3, § 14.1). Un banc de
|
||
navigateur ne mesure pas une imprimante.
|
||
|
||
### 19.11 Ce que le pilotage attrape, et ce qu'il n'attrape pas
|
||
|
||
Un scénario de captures d'écran **n'est pas une suite de tests**, et le confondre
|
||
avec elle donne une couverture imaginaire.
|
||
|
||
**Ce qu'il attrape réellement :**
|
||
|
||
- un écran devenu inatteignable, un bouton renommé, une étape du parcours qui
|
||
n'existe plus — c'est-à-dire **un guide d'usage devenu faux** ;
|
||
- une page qui ne se dessine pas, une erreur non rattrapée, une promesse rejetée
|
||
sans gestionnaire, un échec de construction de l'interface ;
|
||
- une tentative de requête hors de la boucle locale, que le § 2 interdit ;
|
||
- un **libellé manquant** — une clé affichée telle quelle, un élément vide ;
|
||
- un style qui ne s'applique pas, et un contraste tombé sous le seuil du § 14.5 ;
|
||
- une capture d'écran périmée, par le § 19.7 ;
|
||
- un chemin portant un nom de compte affiché dans l'interface.
|
||
|
||
**Ce qu'il n'attrape pas :**
|
||
|
||
- **un libellé écrit en dur.** Comparer le texte affiché au texte attendu ne
|
||
distingue pas une chaîne venue de la table de traduction d'une chaîne écrite dans
|
||
le composant. Le § 14.6 se garde par deux autres moyens, tous deux hors du
|
||
parcours.
|
||
- **la justesse d'un placement.** Elle s'éprouve sous `node`, sur le moteur pur. Un
|
||
parcours vert sur un moteur faux est exactement aussi vert. En particulier, **que
|
||
la petite démonstration atteigne son minimum est une propriété du moteur** :
|
||
l'étape `16` la photographie, elle ne l'établit pas.
|
||
- **le contenu d'un PDF.** Le § 11.1 l'éprouve en extrayant la couche de texte sous
|
||
`node`. L'image tramée du § 19.6 montre une mise en page, elle ne dit rien des
|
||
accents présents dans le fichier produit.
|
||
- **la géométrie du pointeur dans ses modes de défaillance.** Le pilote produit des
|
||
événements que le navigateur traite comme réels, donc un glissement se conduit et
|
||
se photographie — et le **comportement** du seuil du § 8.4 s'éprouve bel et bien.
|
||
Ce qui échappe est ailleurs : **la valeur** du seuil, qui relève du banc ;
|
||
`pointercancel`, qui n'arrive que d'un geste système ou d'une paume ; et
|
||
`touch-action`, qui ne se juge que sous un vrai doigt. Ce sont les pannes que le
|
||
§ 7.3 nomme, et elles restent au niveau manuel.
|
||
- **les garanties de panne du § 8.8.** Elles sont des propriétés de
|
||
l'implémentation `electron` ; le pilotage s'exécute sur `web`. **Aucune
|
||
capture d'écran n'établit quoi que ce soit sur la résistance à une panne.**
|
||
- **la lisibilité.** Une image nette d'un plan illisible est une image nette.
|
||
- **la justesse du français**, et le fait qu'une phrase du guide dise bien ce que
|
||
l'écran fait.
|
||
- **le fait que le parcours soit celui de l'opérateur.** Un scénario vert prouve que
|
||
le script et le logiciel s'accordent. Qu'ils décrivent tous deux la bonne soirée
|
||
reste la charge du niveau manuel (§ 14.4) et de la relecture par quelqu'un qui n'a
|
||
pas écrit le logiciel (§ 16.1).
|
||
|
||
**La règle qui empêche le faux vert** : une étape qui ne trouve pas son élément
|
||
échoue, elle ne passe pas son tour ; une exécution qui produit zéro capture d'écran
|
||
échoue ; une construction dont une étape a échoué ne republie pas les images du
|
||
passage précédent ; et chaque étape ajoutée est montrée en train d'échouer sur le
|
||
code d'avant (§ 14.2). Un scénario qui avale ses échecs pour « toujours produire
|
||
les images » rend exactement le service inverse de celui qu'on lui demande.
|
||
|