# Ce qu'il reste à vérifier à l'œil

Écrit le 5 août 2026 contre `dev` à `1e8f756`. En français parce que c'est une
liste de travail pour la personne qui vérifie ; les clés, les chaînes et les URL
sont verbatim.

**Pourquoi ce document existe.** Tout ce qui a été construit ces trois jours a
été vérifié en appelant les services directement et en SQL. Cela démontre la
donnée et ne démontre rien de l'écran. Les deux seules pages réellement
regardées sont `/login` et `/login/help`, qui sont publiques. Tout le reste
demande une session.

## Avant de commencer

```
bun run db:seed && bun run db:demo && bun run db:panel && bun run job:daily
d3k
```

**L'application est sur http://advmfr.localhost:1355** (d3k gère le serveur ;
journal consolidé dans `~/.d3k/advmfr-557fad/logs/`).

Les onze comptes et ce que chacun démontre sont dans `docs/test-accounts.csv`.
Les principaux ci-dessous.

| compte | mot de passe | niveau | langue | pourquoi lui |
|---|---|---|---|---|
| `narin` | `admin1` | admin | en | voit les 193 postes, dont les deux vacants |
| `hr.manager` | `admin3` | admin | th | admin lisant le thaï, assiette vide |
| `malai` | `manager1` | manager | th | responsable d'Accessory Center, 7 en attente de check |
| `wanida` | `manager3` | manager | en | responsable lisant l'anglais, Casting, 48 tâches |
| `nattikan` | `staff1` | staff | en | 41 tâches, 10 manquées, 11 reportées |
| `narisara` | `staff2` | staff | th | exécutante lisant le thaï, vérificatrice |
| `ram` | `staff3` | staff | th | rien du tout, pour les états vides |
| `somsak.left` | `staff5` | — | — | partie, refusée à la connexion |

**Tous les chiffres de ce document dérivent.** La donnée de démonstration
vieillit chaque nuit. Ils ont été mesurés le 5 août. Si l'écran vous contredit,
c'est l'écran qui a raison.

---

## 0. Les deux points de votre capture, déjà corrigés

**Le menu restait bloqué sur My tasks.** `isActive: true` était écrit en dur sur
cette entrée, rien ne lisait l'URL. Corrigé en `84d041e` : la règle est le plus
long préfixe, dans `lib/nav-active.ts`, pure et couverte par huit tests. Il
fallait le plus long préfixe et pas le premier, parce que `/tasks` et
`/tasks/new` sont deux entrées dans deux groupes différents et que la première
aurait allumé « My tasks » pendant qu'on écrit une consigne.

**À vérifier :** ouvrez chaque entrée à tour de rôle et confirmez qu'une seule
est surlignée à la fois, que `/tasks/<id>` surligne bien My tasks, et que
`/tasks/new` surligne « Create & assign » (ou « My recurring to-dos » en staff)
et **pas** My tasks.

**Le thème.** Il existait déjà, mais le seul moyen de basculer était d'appuyer
sur `d`. Ajouté en `1e8f756` : trois boutons à côté du sélecteur de langue dans
le pied de la barre latérale, et une carte sur `/settings`. Clair, sombre, ou
suivre la machine.

Le raisonnement du placement : la langue est là plutôt que dans le menu de la
personne parce que qui ne sait pas lire l'interface ne doit pas ouvrir un menu
écrit dans la langue qu'il ne lit pas. Qui ne voit pas l'écran ne doit pas
chercher un menu qu'il ne voit pas.

**Le stockage est l'inverse de la langue, exprès.** La langue vit sur la
personne, parce qu'une préférence laissée sur un terminal partagé est héritée
par le suivant. Le thème décrit la lumière de la pièce, la même pour tout le
monde à ce poste, donc il reste dans le navigateur.

**À vérifier :** les trois boutons rendent, celui qui est actif se distingue,
et le choix survit à un rechargement.

**Resté en place, à décider :** la touche `d` bascule toujours le thème depuis
n'importe où hors d'un champ de saisie. Sur un terminal partagé, une frappe
parasite qui retourne l'écran est un appel au support. Je ne l'ai pas retirée
parce que c'était un choix délibéré de quelqu'un, mais c'est une question pour
le déploiement.

---

## 1. L'inventaire des URL

`requireUser` renvoie un déconnecté vers `/login`. `requireAtLeast(niveau)`
renvoie silencieusement un niveau inférieur vers `/tasks`, sans message : un
responsable qui tape `/positions` atterrit sur sa liste de tâches sans savoir
pourquoi.

| URL | paramètres lus | garde | avec quel compte |
|---|---|---|---|
| `/login` | — | public | déconnecté |
| `/login/help` | — | public | déconnecté |
| `/tasks` | `view=day\|week\|month` | `requireUser` | `nattikan` |
| `/tasks/[id]` | — | `requireUser` puis relation, 404 sans relation | `aranya` |
| `/tasks/new` | — | `requireUser`, la permission est dans le service | `nattikan` puis `wanida` |
| `/review` | — | `requireUser` | `malai` (7) ou `narisara` (2 audits) |
| `/kpi` | `dept=` répétable, ignoré sous admin | `requireUser` | `malai` puis `nattikan` |
| `/notifications` | — | `requireUser` | voir §4 |
| `/settings` | — | `requireUser` | n'importe qui |
| `/team` | `dept=` répétable · `tab=late\|today\|waiting` · `sort=oldest\|most\|person` · `group=off` · `person=` · `duty=` | `requireAtLeast("manager")` | `wanida` |
| `/positions` | `q=` · `dept=` répétable · `vacant=1` | `requireAtLeast("admin")` | `narin` |
| `/positions/[id]` | — | `requireAtLeast("admin")` | `narin` |
| `/admin/people` | `q=` | `requireAtLeast("admin")` | `narin` |
| `/admin/people/[id]` | — | `requireAtLeast("admin")` | la ligne de `somsak.left` |
| `/admin/holidays` | `year=` | `requireAtLeast("admin")` | n'importe quel admin |
| `/admin/recurrence` | — | `requireAtLeast("admin")` | n'importe quel admin |

**Deux entrées pointent sur `#` et ne sont pas construites :** « My job
description » et « Reminders ». Ne cliquez pas dessus en attendant un écran.

**Deux états ne sont vides que parce que rien ne les remplit au départ :**
`/notifications` (aucun seed n'écrit de notification, il faut agir d'abord) et
la carte « Standing in » de `/positions/[id]` (aucun seed ne pose de
remplacement).

**Deux routes API :** `/api/auth/[...all]` (le mécanisme derrière `/login`, rien
à regarder) et `/api/notifications/stream`, qui rend du `text/event-stream`
visible en brut dans le navigateur si vous êtes connecté, et un 401 sinon.

---

## 2. `/kpi` — l'écran le plus neuf, jamais ouvert

Livré en `9b3b412`. Une entrée « KPI » qui pointait sur `#` sous Administration
et qui apparaît maintenant une seule fois : sous My work pour un exécutant,
sous My department pour les autres.

**Le cœur de l'issue, à vérifier en premier.** Connectez-vous en `malai`,
ouvrez KPI, notez les quatre chiffres de la ligne de **นัทติกานต์ พุฒพันธ์**.
Connectez-vous en `nattikan`, ouvrez KPI. Ses quatre tuiles doivent porter les
mêmes chiffres **au chiffre près**, pourcentage compris. J'ai prouvé l'identité
au niveau du service ; personne n'a mis les deux écrans côte à côte.

À regarder ensuite :

- **La table n'est pas paginée**, volontairement, parce qu'un département est
  petit. Un **admin** qui sélectionne cinq ou six départements dans le sélecteur
  obtient toutes leurs personnes d'un bloc, sans pagination ni compteur. C'est
  l'endroit où je m'attends au pire.
- **La tuile « Wait for a check » peut n'avoir aucun chiffre** quand rien n'a
  encore été approuvé : seule la phrase rend, et il reste un trou dans une
  rangée de quatre.
- **Deux phrases de vide qui ne doivent pas être interverties.** `ram` doit
  avoir la version « rien **de vous** n'était dû », `hr.manager` la version
  département.
- **L'en-tête en thaï** : `6 ก.ค. → วันพุธที่ 5 สิงหาคม 2569`. L'année
  bouddhiste doit être sur la borne lointaine et pas sur la proche.
- **Le tri sur Completion trie le ratio et pas le pourcentage affiché** : 3 sur
  4 doit passer devant 30 sur 60.
- **Une personne partie garde sa ligne avec un badge « Archived », un poste
  vacant montre son intitulé en grisé.** Il faut un admin sur **Center 3** pour
  voir les deux.

---

## 3. `/tasks/new` vu en exécutant — « My recurring to-dos »

Livré en `3130aae`. Le même écran qu'un responsable atteint sous « Create &
assign », avec un titre différent, **un** poste offert, **une** personne comme
checker (elle-même), et **quatre** occurrences au lieu de six.

**Le point que je crois cassé, à tester en premier.** `submitDuty` n'appelle
aucun `revalidatePath` et le formulaire n'appelle aucun `router.refresh()`,
alors que la carte « My recurring to-dos » est rendue côté serveur. Donc après
avoir pressé Create, la liste en dessous ne devrait pas bouger. Si elle n'avait
aucune consigne avant, la carte n'existe pas et créer sa première aura l'air de
ne rien faire. `toggleOwnDuty`, trois fonctions plus bas, fait bien son
`revalidatePath`. **Créez une consigne quotidienne, ne touchez à rien, et
regardez si une carte apparaît. Puis rechargez.**

À regarder ensuite :

- Les deux listes n'ayant qu'un élément, poste et checker doivent arriver
  **déjà remplis avec le libellé visible**, pas une case vide avec une valeur
  cachée.
- **La boîte de confirmation dit deux phrases différentes** selon le sens :
  arrêter dit « demain n'apporte aucune tâche nouvelle, tout ce qui est déjà
  produit reste » ; relancer dit « elle recommence à produire à partir de sa
  prochaine échéance ». Testez les deux sens.
- Une consigne arrêtée **garde sa ligne avec un badge « Off »**.
- **La phrase de planification en thaï**, en `narisara`. Le thaï place le
  numéro de jour et le mot de jour ailleurs que l'anglais.
- **Une description thaïe longue** : la ligne est en `flex-wrap justify-between`
  avec le bouton à droite.
- **Les consignes que sa responsable lui a assignées ne doivent avoir aucun
  bouton d'arrêt.** C'est toute la propriété de sécurité de cette feature.

---

## 4. `/positions/[id]` — le remplacement temporaire

Livré en cinq commits, `49a5580` à `16e217f`. Sous le titulaire, séparé par un
filet : qui remplace, son poste habituel, les deux dates, la raison, un bouton
pour y mettre fin. Et un formulaire pour en ouvrir un.

- **La liste des remplaçants tient environ 192 entrées**, libellées
  `nom · poste · département`, majoritairement en thaï, sans virtualisation.
  C'est l'endroit le plus susceptible de casser visuellement dans toute la
  feature. Ouvrez-la, tapez trois caractères thaïs, regardez si elle se réduit.
- **Le calendrier est sur deux mois** (`numberOfMonths={2}`) sans repli
  responsive. C'est l'élément le plus large de l'application.
- **L'habillage thaï du calendrier** doit dire **สิงหาคม 2569** et pas 2026.
- **Deux badges** : « In force » et « Not started yet », deux variantes
  différentes. Vérifiez qu'on les distingue et que ce ne sont pas deux gris.
- **La confirmation dit deux phrases opposées**, et les inverser serait
  invisible pour tous les tests existants. En cours : « le remplacement se
  termine aujourd'hui, le travail de demain revient au titulaire, tout ce qui a
  déjà été confié garde son nom ». Pas encore commencé : « il n'a rien produit,
  il est donc supprimé plutôt que raccourci ».
- **Le refus de chevauchement.** Ouvrez-en un second qui déborde le premier : il
  doit rendre une phrase rouge sous le formulaire et **pas** une page d'erreur.
  Avant `81d38a8` il s'échappait en exception.
- **Est-ce que le remplacement déplace vraiment le travail ?** Ouvrez-en un à
  partir d'aujourd'hui sur un poste dont le titulaire a des consignes, lancez
  `bun run job:daily`, connectez-vous en remplaçant : les instances de demain
  doivent être chez lui et plus chez le titulaire. Celles déjà générées ne
  bougent pas.

---

## 5. Les notifications — le direct et la pastille

Livré en `328b64f`. **Il faut deux fenêtres** (deux navigateurs ou une fenêtre
privée : les sessions sont des cookies).

1. `malai` dans l'une sur `/review`, `nattikan` dans l'autre sur `/tasks`.
2. En `nattikan`, terminer une tâche avec une note → la pastille de `malai`
   doit s'incrémenter **sans navigation**, en une seconde environ.
3. En `malai`, renvoyer une tâche avec une note → la pastille de `nattikan`
   bouge et la note est lisible dans sa liste.
4. Approuver → encore. Envoyer une relance → encore.

- **Le rafraîchissement ne doit rien perdre.** Mettez `nattikan` sur
  `/tasks/new` avec une description à moitié tapée, déclenchez une
  notification, et vérifiez que le texte est toujours là.
- **La pastille quand la barre est repliée en icônes** : elle doit disparaître
  avec les mots, pas se dessiner sur la cloche.
- **Ouvrir la liste marque comme lu, et les deux états doivent s'accorder sur
  un seul rendu** : les lignes portent encore leur badge « New » alors que la
  pastille de la barre a déjà disparu. Si la pastille est encore là pendant que
  les lignes sont neuves, c'est un vrai défaut.
- **Les quatre phrases dans les deux langues.** Il n'y en a que quatre :
  `notify.finished`, `notify.sentBack`, `notify.reminded`, `notify.approved`.
- **Une note multiligne** : elle est rendue sans `whitespace-pre-line` ici,
  contrairement à la carte de la page tâche. Les retours à la ligne s'écrasent
  probablement. À juger.

---

## 6. La semaine de six jours

Rien ne l'affiche nulle part : aucun écran ne montre un rythme de travail. Elle
n'est visible que par l'existence de tâches **le samedi**.

- Mettez `/tasks` en vue **semaine** ou **mois** en `nattikan` ou `wanida` et
  cherchez des lignes datées **samedi 8** et **samedi 15 août 2026**. Si les
  samedis sont vides, quelque chose a régressé.
- **Le dimanche doit rester vide.**
- **Le cas qui se cache.** Pour une consigne sur un poste **vacant ou à deux
  titulaires**, le générateur n'a personne dont lire le rythme et retombe sur
  une constante. Cette constante a dit `mon_fri` pendant une journée après la
  bascule, supprimant silencieusement les samedis. La donnée de démonstration
  contient exprès un poste vacant (la chaise de `somsak.left`, Center 3) et un
  poste à deux titulaires (Metal Center). Vérifiez que du travail existe le
  samedi pour ces deux-là aussi.
- `/admin/recurrence` a un cas « A fixed day on a Saturday » à lire, en thaï
  aussi.

---

## 7. `/tasks` — le report et le compte

- **Le compte dans le titre compte des dates, pas des lignes.** `nattikan` avait
  11 reportées réparties sur moins de consignes. Si le titre affiche 4, il dit à
  quelqu'un qu'il doit moins que ce qu'il doit.
- Le chevron pivote, la ligne se déplie, les dates sont indentées. Dépliez-en
  deux à la fois.
- **Une plage d'un seul jour** doit afficher une date, pas « 6 juil → 6 juil ».
- **Le nom accessible du bouton bascule** entre « Show dates » et « Hide dates ».
- Jour / semaine / mois : le bloc reporté ne bouge pas, il est hors fenêtre. Il
  ne doit pas être compté deux fois.

---

## 8. La relecture thaïe

**183 des 193 personnes lisent le thaï, et aucune chaîne thaïe de ce dépôt n'a
été écrite par un thaïphone.** `lib/i18n/messages.ts` a été créé le 4 août :
l'intégralité du fichier, environ 260 clés, est à relire. Le tableau complet
clé / anglais / thaï, groupé par écran, est trop long pour ce document et a été
produit ; les points ci-dessous sont ceux qui méritent l'attention en premier.

**Les phrases entières avant les étiquettes.** Une étiquette fausse se voit, une
phrase fausse se croit. Environ 85 clés sont de vraies phrases : les
`consequence.*` de la page tâche, les `kpi.*Means`, les `cover.end*Consequence`,
les `people.*Consequence`, les `taskForm.invalid.*`, les `settings.*` du
changement de mot de passe, et les refus.

**Cinq points précis relevés dans la traduction :**

| clé | ce qu'il faut confirmer |
|---|---|
| `nav.myWork` et `nav.myTasks` | les deux valent `งานของฉัน`, or l'une est l'en-tête de section et l'autre l'entrée dessous |
| `kpi.checkWaitMeans` | le thaï dit littéralement « c'est le chiffre du contrôleur, pas de l'exécutant ». Écran sensible |
| `taskForm.schedule.monthlyOn` | l'anglais dit « weekends and holidays », le thaï compresse en `วันหยุด` seul |
| `holidays.importHint` | l'anglais dit « et laisse les autres tranquilles », le thaï le sous-entend sans le dire. Avertissement destructif |
| `recurrence.line.clamped` | `แต่เดือน{month}สั้นกว่านั้น` se lit d'un bloc, à lire à voix haute |

**Quatre endroits où une phrase thaïe est assemblée en morceaux**, donc où
personne n'a relu la phrase entière :

- un nom de jour traduit inséré dans une autre phrase traduite
  (`taskForm.schedule.everyWeekday` → `ทุกวันจันทร์`), et un mot de rythme de
  travail inséré dans six phrases de `/admin/recurrence` ;
- une liste de champs manquants jointe par des virgules et insérée dans
  `taskForm.stillNeeded` ;
- une liste d'années jointe par des virgules dans `holidays.loaded` ;
- sur la carte de remplacement, le nom et `cover.normally` sont deux `<span>`
  destinés à se lire comme une phrase.

**Trois chaînes anglaises en dur qui atteignent des lecteurs thaïs :**

| endroit | texte | portée |
|---|---|---|
| `lib/levels.ts` `LEVEL_LABELS` | Admin, Manager, Assistant Manager, Supervisor, Line Leader, Staff | **en production**, colonne « Level » de `/positions` et sous-titre de `/positions/[id]`. Vérifié, c'est bien de l'anglais en dur |
| `components/brand-mark.tsx` et `components/signed-out-shell.tsx` | « Task Tracker » / « ADVMFR » | sur toutes les pages. À décider : marque, ou interface à traduire ? Le texte est dupliqué dans deux fichiers |
| `components/ui/sidebar.tsx` | « Sidebar » / « Displays the mobile sidebar » | `sr-only`, donc lu par un lecteur d'écran sur mobile uniquement |

**`components/task-history.tsx` porte un titre anglais en dur et n'est importé
nulle part.** Deux agents se contredisaient là-dessus, j'ai vérifié : la page
tâche utilise bien la clé traduite `task.whatHasHappened`, ce composant est du
code mort. Il est un piège pour plus tard, pas un défaut aujourd'hui. Même
chose pour la clé `position.vacantTitle`, définie et jamais utilisée.

**Enfin, l'écran de connexion.** `/login` et `/login/help` écrivent leur thaï en
dur, exprès, parce que personne n'a encore de langue. Ce thaï-là n'a pas été
relu non plus, et c'est la toute première chose que voient les 193 personnes.

---

## 9. Visuel et accessibilité, par ordre de gravité réelle

Mesuré sur le code et sur la police, pas deviné. Ce qui suit demande néanmoins
un écran, parce que rien ici ne remplace un rendu.

**1. Toutes les cellules de tableau sont `whitespace-nowrap`**
(`components/ui/table.tsx:86`), sans exception nulle part. `/team` en a six
colonnes, `/kpi` six, `/positions` cinq, `/admin/people` cinq, chacune tenant
des descriptions thaïes, des noms thaïs et des dates bouddhistes écrites en
toutes lettres, dont rien ne peut passer à la ligne. Le tableau devient plus
large que l'écran et défile horizontalement, et **il n'y a d'en-tête collant
nulle part** : une fois défilé à droite on ne sait plus quelle colonne on lit.

**2. Le pied de la barre latérale coupe le nom et le titre de la personne
connectée** (`components/nav-user.tsx:62-72`). Deux lignes en `leading-tight`
plus le padding font 51 px dans un `h-12` = 48 px avec `overflow-hidden`. La
seconde ligne est de la vraie donnée du roster : jusqu'à 60 caractères
(« Department Manager (HR) · Human Resources and Administration ») dans une
colonne d'environ 200 px. **C'est sur tous les écrans pour tout le monde.**

**3. La grille n'a pas de structure visible.** `--border` est à **1,26:1** sur
blanc. Dans une lumière moyenne, les séparateurs de lignes sont invisibles, ce
qui est exactement ce qu'on ne peut pas se permettre sur une ligne de six
colonnes qui défile.

**4. Rien dans les écrans de travail n'atteint 44 px.** Les boutons d'action
d'une tâche (Start, Finish, Approve, Send back) font 32 px. Les contrôles qui
**changent un état** sont les plus petits : `duty-switch` et le bouton de
remplacement sont à 28 px, les boutons d'effacement des recherches à 24 px. Sur
un terminal d'atelier, avec des gants.

**5. Sous 640 px, le bouton destructif est rendu au-dessus de Cancel**
(`components/ui/alert-dialog.tsx:88`, `flex-col-reverse`), à 8 px l'un de
l'autre, tous deux à 32 px. Et « Archive » ou « Reset password » se confirment
avec l'apparence d'un bouton ordinaire.

**6. Le badge de statut est à une fraction de pixel de couper le thaï.**
`h-5` + `overflow-hidden` + `text-xs`. L'encre à 12 px demande 17,15 px, la
boîte en fait 18. `เริ่มแล้ว` porte une marque empilée, `เข้าสู่ระบบได้` porte
une marque empilée **et** une voyelle basse. Le calcul dit « à la limite », pas
« cassé » : à regarder en zoomant sur `/admin/people` en thaï.

**7. `leading-tight` et `leading-snug` autour du thaï.** La police Prompt a une
boîte naturelle de 1,512 em ; `leading-tight` vaut 1,25 et `leading-snug` 1,375,
**tous deux plus petits que la boîte de la police**. Concerné : le pied de barre
latérale, et `CardTitle`, c'est-à-dire la description de la tâche, le nom de la
personne sur sa fiche, et l'intitulé du poste.

**8. Deux paires de contraste échouent au niveau AA :**

- `text-destructive` sur `bg-destructive/10` = **3,99:1** en `text-xs`, soit les
  badges **Missed** et **Late**. Le statut le plus important de l'application,
  au contraste le plus faible, dans la plus petite taille.
- `text-foreground/50` = **3,71:1**, utilisé pour le nom d'un **poste vacant**
  sur `/team`, précisément le cas qu'un responsable doit remarquer.

**9. En mode sombre, `--muted-foreground` est identique à `--foreground`**
(`app/globals.css:97`, les deux à `oklch(0.985 0 0)`). Donc aucune hiérarchie,
et surtout **un champ vide a l'air rempli**, puisque le texte indicatif a la
couleur du texte saisi. Sur un terminal partagé, c'est une source d'erreur.

**10. Le focus est perdu quatre fois**, toujours parce que l'élément cliqué est
démonté. Le pire : `app/admin/people/[id]/login-panel.tsx:78` remplace le
panneau au moment exact où un mot de passe est affiché **et ne le sera plus
jamais**, et le composant qui l'affiche n'a ni `role="status"`, ni titre, ni
rien de focusable.

**11. Un `aria-label` écrase la seule information qui distingue le contrôle.**
Sur `/tasks` et `/team`, le bouton de dépliage annonce « Show the dates » à la
place de la description de la consigne : **tous les groupes s'annoncent de
façon identique**. Idem pour les liens de date, tous annoncés « Open the task ».

**12. La couleur seule.** Ils ont été prudents dans l'ensemble : les cinq
statuts portent tous un mot. Restent : la pastille de 6 px de
`/admin/recurrence` (dû / férié / rien en noir, rouge, transparent), le fait
qu'une consigne **active** se signale par l'**absence** de badge, et le
sélecteur de vue de `/tasks` distingué par la seule couleur de fond.

**13. Aucun message de résultat n'est annoncé.** Ni `role="alert"` ni
`aria-live` sur les phrases d'erreur, **y compris dans les dialogues ouverts**.
Le motif correct existe pourtant dans le dépôt, sur les formulaires de connexion
et de mot de passe. Et `duty-switch` comme `covers` ne remettent jamais à zéro
l'erreur à la fermeture : rouvrir un dialogue affiche une erreur portant sur une
pression qui n'a pas eu lieu.

**14. La barre latérale disparaît sous 768 px** et les panneaux industriels sont
couramment en 1024×768 ou 800×480. À 1024, la barre prend 256 px et laisse
768 px pour un tableau de six colonnes insécables.

**15. Tous les dialogues sont coincés à 320 ou 384 px.** Une règle du composant
(`data-[size=default]:sm:max-w-sm`, spécificité 0,2,0) bat le `sm:max-w-md` que
tous les appelants demandent (0,1,0). Ces dialogues contiennent des phrases de
conséquence en thaï écrites en toutes lettres. À confirmer dans l'inspecteur.

**16. Sur `/notifications`, la note est tronquée à deux lignes** sans aucun
moyen de la déplier, alors que la note est précisément la raison d'être de la
ligne.

**La question qui décide de la moitié de cette section : personne n'a dit quelle
taille fait l'écran.** Les points 1, 5, 14 et 15 changent de réponse entre
800×480, 1024×768 et un panneau 1080p. Mesurer un vrai terminal vaut plus que
toute lecture de code supplémentaire.

---

## 10. Ce qui ressemble à un défaut et n'en est pas

Ne remontez pas ces points, ils sont délibérés et documentés.

- **Une tâche manquée reste dans la liste.** Le client a répondu le 4 août :
  le travail dû reste dû. La balayeuse nocturne la re-marque manquée chaque
  nuit, même après qu'on l'a commencée.
- **« Renvoyer » remet la tâche en `started` et pas en `todo`**, et garde
  l'heure du premier envoi. Le bouton Start ne réapparaît pas. Un commentaire du
  schéma dit l'inverse ; c'est le commentaire qui est périmé.
- **`verified` n'est pas un statut.** Une tâche vérifiée reste affichée
  « checked ». C'est un audit après coup, enregistré en deux horodatages.
- **Sur une tâche manquée, Finish est proposé en premier et Start en dessous.**
- **`/review` n'est pas réservé aux responsables** : trois des sept contrôleurs
  du roster sont des exécutants.
- **`/kpi` n'est pas réservé aux responsables non plus**, c'est tout le sujet.
- **Une consigne personnelle n'est marquée nulle part comme telle** et apparaît
  sur `/team` comme n'importe quelle autre. Visible par la responsable, pas
  approuvable par elle.
- **Un remplacement ne copie pas les lignes de consigne.** La fiche de poste du
  remplaçant ne gagne rien.
- **Les remplacements terminés ne sont pas affichés** sur la carte.
- **Aucune notification pour les tâches manquées.** L'événement est écrit,
  personne n'est prévenu : la balayeuse en marque des dizaines par nuit.
- **On n'est jamais notifié de son propre geste.**
- **Le menu à trois points de `/positions` a été supprimé**, ses trois entrées
  ne faisaient rien. Idem pour le sélecteur de département de la barre latérale.
- **`/login` n'a pas de sélecteur de langue** : personne n'est connecté pour
  avoir une préférence, donc les deux langues sont empilées.
- **La connexion donne trois messages d'échec différents** là où la plupart des
  systèmes en donnent un. Compromis assumé, ELEVA-133.
- **Une session dure 8 heures.** Une fenêtre laissée la nuit sera déconnectée.
- **Les couleurs ne sont pas la maquette.** La palette n'est pas tranchée ; ne
  remontez pas les choix de couleur un par un.

---

## 11. Ce que je n'ai pas pu vérifier, et pourquoi

- **Tout ce qui demande une session.** Je ne saisis pas de mot de passe dans un
  formulaire. Les seules pages que j'ai vraiment vues sont `/login` et
  `/login/help`.
- **Le rafraîchissement de `/tasks/new` après création.** J'ai lu le code et je
  suis sûr qu'aucun `revalidatePath` n'est appelé ; je ne suis pas sûr de ce que
  Next 16 fait de lui-même après une Server Action. À tester, pas à croire.
- **Si le badge coupe vraiment le thaï.** 0,4 px de marge est dans l'erreur
  d'arrondi.
- **Quelle règle `max-w` gagne dans les dialogues.** La spécificité dit le
  composant. À confirmer dans l'inspecteur.
- **Où le focus atterrit** après chacun des quatre démontages.
- **Ce qu'annonce un lecteur d'écran.** Rien n'a jamais été écouté.
- **Si le bouton de connexion est sous la ligne de flottaison** : le formulaire
  rend chaque libellé deux fois, ce qui double à peu près sa hauteur.

---

## 12. Les dix questions à poser au client

Elles bloquent du travail et `docs/demonstration.md` les rattache chacune à
l'écran qui les rend évidentes. En résumé : le calendrier 2026 complet
(ELEVA-150), quelles consignes continuent un jour férié (ELEVA-151), ce qu'un
exécutant remet en terminant (ELEVA-121), un poste à plusieurs titulaires
(ELEVA-137), la date limite d'une consigne hebdomadaire (ELEVA-223), la durée
d'une session (ELEVA-220), la longueur d'un mot de passe (ELEVA-221), la règle
de nommage des 193 identifiants (ELEVA-159), les vraies coordonnées des bureaux
Admin et RH (ELEVA-234), et si l'employé 00000001 doit vraiment être Staff
(ELEVA-158).
