The specification covered the placement and little else; it now covers the session where an operator spends an evening — several events, automatic saving, history, a read-only default. Three points decide the design rather than describe it: a title belongs to a place and never to a person, so removing someone leaves the role visible instead of silently vacant; the read/write mode is orthogonal to the four states, not a fifth; a perfect reduced instance does not prove the complete ceiling, which an enumeration establishes. Checked: 29 tables of 8 and 4 of 7 seat exactly 260, and four facilitators cap at 24 — a minimum no rerun improves. --- FR --- [UPD] spec : événements, réservations, diversité, page qualité, Svelte La spécification couvrait le placement et peu d'autre chose ; elle couvre désormais la séance où un opérateur passe une soirée — plusieurs événements, enregistrement automatique, historique, lecture par défaut. Trois points décident de la conception au lieu de la décrire : un titre appartient à une place et jamais à une personne, si bien que retirer quelqu'un laisse le rôle visible plutôt que vacant en silence ; le mode lecture/écriture est orthogonal aux quatre états, non un cinquième ; un réduit parfait ne prouve pas le plafond complet, ce qu'établit une énumération. Vérifié : 29 tables de 8 et 4 de 7 asseyent exactement 260, et quatre animateurs plafonnent à 24 — un minimum qu'aucune relance n'améliore. Assisted-by: Claude Opus 5
1791 lines
89 KiB
Markdown
1791 lines
89 KiB
Markdown
# Gestion table tournante Libre — spécification
|
||
|
||
Exécutable livré : `gestion_table_tournante_libre_v#_#.exe`
|
||
|
||
---
|
||
|
||
## 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.
|
||
|
||
## 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 : un identifiant long quadruplerait 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 numéro à 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.
|
||
|
||
**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 est sa liste de travail |
|
||
| 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 |
|
||
| **paires de même appartenance** | paires distinctes assises ensemble |
|
||
| **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 |
|
||
|
||
**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.
|
||
|
||
**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 noms.** Le plafond dépend de l'itinéraire :
|
||
|
||
- le **plafond a priori**, maximisé sur les itinéraires admissibles, affiché par
|
||
le diagnostic **avant** la recherche ;
|
||
- le **plafond réalisé**, calculé sur l'itinéraire de la proposition, affiché à
|
||
côté de la mesure.
|
||
|
||
Sans ce partage, le diagnostic annonce 28, la page de qualité affiche 27, et
|
||
l'opérateur conclut à une régression du moteur.
|
||
|
||
**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.
|
||
|
||
Quand aucun chemin ne conclut, le logiciel affiche l'écart 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 paires de
|
||
collègues » ;
|
||
- 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.
|
||
|
||
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 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 : minimum de personnes rencontrées, puis paires de même
|
||
appartenance, puis rencontres répétées, puis **redondance d'appartenance comme
|
||
départage**. Ce 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.
|
||
|
||
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.
|
||
|
||
**Une commande assistée, « Poser les animateurs »**, prend les participants
|
||
portant un titre pressenti donné et crée pour chacun une réservation de portée
|
||
« tous les tours » **et** un titre sur la place visée. Un geste, une entrée
|
||
d'historique, tous les refus évalués avant d'appliquer quoi que ce soit.
|
||
|
||
### 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 : refusé, avec le
|
||
nombre de places manquantes.
|
||
- 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.
|
||
|
||
Le logiciel **éprouve l'incrémental contre le recalcul complet** sur chaque
|
||
configuration de démonstration.
|
||
|
||
---
|
||
|
||
## 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 :**
|
||
|
||
| document | règle |
|
||
|---|---|
|
||
| le plan de salle, à l'écran **comme au PDF** | **abrège** avec une marque, la largeur d'un bloc de noms étant la même contrainte des deux côtés |
|
||
| la fiche, la feuille par table, la liste d'accueil | **ne tronquent jamais** : elles replient |
|
||
|
||
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 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.
|
||
|
||
**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.
|
||
|
||
**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é** et ses exports, la comparaison des propositions, la lecture
|
||
de l'historique, l'impression. 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 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.
|
||
|
||
**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 réglage local de
|
||
la machine indexé par l'identifiant de l'événement. É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.
|
||
|
||
### 8.6 Les fichiers
|
||
|
||
**Emplacement** : un dossier nommé d'après le produit, dans les *Documents* de
|
||
l'opérateur — pas dans un répertoire de données caché. Il doit pouvoir copier un
|
||
événement sur une clé, le joindre à un courriel, le restaurer depuis sa
|
||
sauvegarde, **sans le logiciel**.
|
||
|
||
**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 des deux suffixes** (sinon l'état tient et son journal
|
||
déborde), et **retombe sur un nom générique** quand la dérivation ne laisse
|
||
rien. **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.
|
||
|
||
**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 `browser`, 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 et sa révision ;
|
||
l'analyse syntaxique seule ne distingue pas un fichier complet d'un fichier
|
||
plausible.
|
||
- **Sérialisation canonique** : ordre de clés fixé, aucune valeur dérivée de
|
||
l'horloge ou d'un tirage. Sans elle, deux états identiques produisent deux
|
||
fichiers différents et un correctif enregistre du bruit à chaque geste.
|
||
|
||
---
|
||
|
||
## 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.
|
||
|
||
---
|
||
|
||
## 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 ; 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é ; sinon décodage
|
||
UTF-8 strict, et repli sur windows-1252 en cas d'échec.
|
||
|
||
> L'étape 3 n'est pas une précaution de principe : **un texte UTF-16 dont le
|
||
> contenu est latin est valide en UTF-8**, chaque octet nul s'y décodant comme
|
||
> le caractère nul. Un ordre à trois temps produirait des noms entrelardés de
|
||
> caractères nuls, qui ne font échouer aucun calcul. Le mode de défaillance est
|
||
> muet : « Benoît » importé en « Benoît » se découvre 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**.
|
||
|
||
> 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. Sur 260 lignes, un
|
||
refus global coûterait l'import entier pour une faute en ligne 213.
|
||
|
||
Trois cas restent des refus **globaux** : aucun en-tête reconnaissable, aucune
|
||
colonne associée à `nom`, zéro ligne valide.
|
||
|
||
**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,
|
||
et une ligne refusée faute de nom.
|
||
|
||
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 bibliothèque embarquée, jamais l'impression du navigateur
|
||
|
||
Le logiciel produit ses PDF **en mémoire, par une bibliothèque embarquée**.
|
||
|
||
**L'argument décisif est l'épreuve.** Une bibliothèque embarquée s'exécute sous
|
||
`node` : un test de quelques secondes affirme qu'une feuille de table tient sur
|
||
une page, que 260 fiches comptent le nombre de pages attendu, et que les accents
|
||
sont présents — **en extrayant la couche de texte du PDF produit**, pas en
|
||
regardant une image. L'impression du navigateur exige de démarrer l'application
|
||
et un moteur de rendu : elle sort du cycle « à chaque modification », et **un
|
||
contrôle qui ne tourne qu'avant une livraison ne tourne pas**.
|
||
|
||
S'y ajoute que la pagination de l'impression dépend des règles CSS, dont le
|
||
comportement diffère d'un moteur à l'autre : l'opérateur découvre le saut de
|
||
page au milieu d'une table une fois la feuille sortie.
|
||
|
||
Conséquences à tenir :
|
||
|
||
- **La police est embarquée** dans le PDF, couvrant le latin étendu du français,
|
||
ligatures comprises. Un test échoue si un caractère d'un nom d'épreuve ne se
|
||
retrouve pas dans la couche de texte.
|
||
- **Le PDF se construit depuis le modèle, jamais depuis le DOM** : l'écran et la
|
||
feuille lisent la même source.
|
||
- **Le plan s'exporte en vectoriel.** Le SVG inséré est la **forme d'export**,
|
||
avec son `viewBox` calculé — que le document vivant n'a pas — et il porte le
|
||
**cadrage ajusté à la page**, non celui de l'écran. Sans le `viewBox`, un SVG
|
||
autonome se rend à 300 × 150 pixels ; sans le cadrage de page, deux
|
||
impressions du même plan diffèrent selon le zoom laissé à l'écran.
|
||
- Le rendu du plan **s'interdit tout élément HTML enchâssé dans le SVG**, que le
|
||
PDF ne sait pas reprendre.
|
||
|
||
**À confirmer dans la première semaine : que la bibliothèque accepte le SVG que
|
||
le plan produit**, avec ses `<use>`, ses `<symbol>` et ses `<tspan>` à décalage
|
||
explicite. C'est le seul point de l'architecture de sortie qui repose sur une
|
||
supposition.
|
||
|
||
### 11.2 Ce qui s'exporte
|
||
|
||
| sortie | contenu |
|
||
|---|---|
|
||
| **Fiches participants** | nom, appartenance, titre, et pour chaque tour la table et le siège |
|
||
| **Feuilles par table** | une table, tous les tours, les convives numérotés |
|
||
| **Liste d'accueil** | tous les participants par ordre alphabétique, table de chaque tour en regard |
|
||
| **Plan de salle** | vectoriel, listes comprises, un tour par page |
|
||
| **Rapport de placement** | indicateurs, écart au plafond, diagnostic |
|
||
| **Page de qualité** | l'en-tête chiffré, les graphiques, les tableaux |
|
||
|
||
La **liste d'accueil** est la feuille la plus manipulée de la soirée : sans elle,
|
||
retrouver une personne oblige à parcourir les feuilles de table.
|
||
|
||
**Le plan ne tient pas toujours sur une page.** Le logiciel calcule l'échelle qui
|
||
le ferait tenir ; si la taille de caractère tombe sous le seuil **mesuré** de
|
||
lisibilité, il découpe en pages contiguës, chacune portant un cartouche de
|
||
repérage. Le choix est offert, le découpage étant proposé par défaut.
|
||
|
||
**Chaque page porte en pied** le nom de l'événement, sa date, le tour, et
|
||
**l'horodatage du placement exporté** — 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.
|
||
|
||
Un plan qui n'est ni **retenu** ni **bloqué** s'exporte avec la mention
|
||
**BROUILLON** en filigrane. Le logiciel n'interdit pas d'imprimer un brouillon ;
|
||
il interdit qu'on le confonde avec le plan définitif.
|
||
|
||
---
|
||
|
||
## 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.
|
||
|
||
**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, **le minimum en premier**,
|
||
chacun avec son **plafond réalisé** et la mention de la population mesurée.
|
||
|
||
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 que la courbe ne dépasse jamais son plafond.** Si elle le
|
||
dépasse, 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.
|
||
|
||
**Comparer deux placements** sur le même axe. **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.
|
||
|
||
**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.
|
||
|
||
### 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 |
|
||
| 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 |
|
||
|
||
### 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.
|
||
- **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.**
|
||
|
||
### 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.
|
||
|
||
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.
|
||
|
||
---
|
||
|
||
## 13. Architecture
|
||
|
||
### 13.1 Forme du projet
|
||
|
||
Une application **Cordova**, dont le code vit dans `www/` et ne dépend d'aucun
|
||
service.
|
||
|
||
| plateforme | rôle |
|
||
|---|---|
|
||
| `electron` | la livraison : un exécutable Windows |
|
||
| `browser` | le développement et les tests sous Linux |
|
||
| `android` | ouvert, non requis pour la première livraison |
|
||
|
||
### 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.
|
||
|
||
### 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 Cordova.** 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.
|
||
|
||
### 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.
|
||
- **Le PDF vectoriel** : la bibliothèque du § 11.1 ingère une géométrie
|
||
vectorielle, 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 et pourquoi**,
|
||
afin que personne ne la « corrige » plus tard par le calcul.
|
||
|
||
### 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 ».
|
||
|
||
### 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 trois niveaux d'épreuve
|
||
|
||
| niveau | ce qu'il couvre | quand |
|
||
|---|---|---|
|
||
| `node` | moteur, géométrie, analyseur CSV, stockage, PDF | à chaque modification, quelques secondes |
|
||
| navigateur | ce que l'interface dessine, ce que les gestes produisent, les coordonnées | avant chaque livraison |
|
||
| manuel | une soirée complète, de la saisie à l'impression | avant chaque livraison au client |
|
||
|
||
**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.
|
||
|
||
### 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.
|
||
|
||
### 14.7 Le déterminisme
|
||
|
||
Aucune source non reproductible — `Math.random`, `Date.now`, `performance.now`,
|
||
`crypto.getRandomValues` — dans le **moteur** ni dans le **générateur de
|
||
démonstrations**. Un test de l'arborescence le refuse, **et échoue si son
|
||
balayage ne trouve aucun fichier**. L'interdit ne porte pas sur le reste de
|
||
l'application : l'historique horodate ses entrées.
|
||
|
||
---
|
||
|
||
## 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.
|
||
|
||
### 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é
|
||
≤ nombre de groupes — puis **construit un tour témoin**. 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 »** et tous ses compteurs à 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 paire par tour, quatre sur quatre
|
||
tours**, quel que soit l'algorithme. Une personne déplacée d'un groupe à l'autre
|
||
fait basculer le logiciel de « minimum atteint » à « quatre paires
|
||
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.
|
||
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é. **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.
|
||
|
||
### 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**. 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 la plateforme `electron` de
|
||
Cordova, qui s'appuie sur `electron-builder`.
|
||
|
||
**Cette capacité est à vérifier dès la première semaine**, sur un squelette
|
||
vide, au même titre que l'ingestion du SVG par la bibliothèque PDF (§ 11.1).
|
||
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.
|
||
|
||
### 16.1 Les deux guides livrés
|
||
|
||
| document | contenu |
|
||
|---|---|
|
||
| `GUIDE-WINDOWS.md` | **un seul fichier** : obtenir l'exécutable, le lancer, où vivent les fichiers d'événements, comment les sauvegarder, que faire si l'antivirus bloque, et comment signaler un problème |
|
||
| `GUIDE-USAGE.md` | le parcours d'une soirée, de la liste de participants à l'impression, en une dizaine d'écrans commentés |
|
||
|
||
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.
|
||
|
||
---
|
||
|
||
## 17. Ce que ce document ne tranche pas
|
||
|
||
1. **Le format de stockage des placements à l'intérieur du fichier** — la forme
|
||
lisible et la forme compacte n'ont pas le même coût, et le § 8.6 pose un
|
||
ordre de grandeur, pas une structure.
|
||
2. **La bibliothèque PDF**, et la confirmation qu'elle ingère le SVG produit.
|
||
3. **Le mécanisme exact de la commande « Poser les animateurs »** : quelle table
|
||
reçoit quel animateur. Un tour par numéro de table est le plus simple ; rien
|
||
n'établit que c'est ce que l'opérateur attend quand les appartenances sont
|
||
inégalement réparties.
|
||
4. **Le coût de la maximisation du plafond a priori** sur les itinéraires
|
||
admissibles, quand des réservations de tour désigné fixent une partie du
|
||
parcours. L'énumération est annoncée immédiate pour une dizaine de tables ;
|
||
aucune mesure n'a été faite sur 33 tables et 4 tours.
|
||
5. **Comment la page de qualité compare deux propositions dont les escaliers de
|
||
plafond diffèrent**, cas que produit la variante de la grande démonstration.
|
||
6. **Le seuil de zoom qui retire les listes de noms** : il se fixe sur une
|
||
mesure du temps par image, pas sur une intuition.
|
||
7. **Les dégagements de référence d'une salle**, sans lesquels le mot
|
||
« conforme » reste absent de l'interface.
|