Documente les lots Q à Y, et corrige ce que la série a rendu faux
Nouvelle section « Lots Q à Y » dans CLAUDE.md : les décisions et les pièges, pas la description du code. Elle dit aussi, en tête, ce qui n'est pas éprouvé — aucun écran de la série n'a été vérifié en navigateur. ⚠️ Quatre passages étaient devenus FAUX, et un fichier qui fait autorité et qui ment est pire qu'un fichier incomplet : - le lot H actait « le retour passe par l'historique du navigateur, jamais par une destination calculée » et décrivait js/navigation.js, supprimé depuis. Le texte d'origine est conservé en citation — son motif reste vrai — sous le renversement qui explique pourquoi il n'a pas suffi, et pourquoi toute route nouvelle doit entrer dans la table de RemonteeRoutes ; - « Cinq instantanés » : ils sont neuf, et une vue d'API sans instantané est un écran mort hors-ligne ; - le modèle de données ignorait Livre.NombrePages, Livre.UrlNotice et l'entité RevueSouhaitee, dont Utilisateur est une frontière comme pour LivreSouhaite ; - la fiche d'une série ne porte plus de barre d'actions flottante en consultation ; elle ne subsiste que sur l'écran d'ordre. IDEES.md marque les lots Q à Y traités, en gardant le texte qui dit le besoin, et ouvre trois points que la série laisse : la vérification en navigateur de tous les écrans touchés, le réordonnancement des envies de revues, et un éventuel signalement « déjà possédée » pour elles — plus douteux qu'il n'y paraît, une envie portant souvent la revue entière. Ce qui était déjà ouvert (A5, K1, N2, homonymes, OpenLibrary, EAN-2) reste intact. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -348,9 +348,13 @@ l'utilisateur, et la douchette tape des chiffres.
|
||||
| `Services/FiltreLivresLocal.cs` | Recherche, filtres et tri hors-ligne sur l'instantané |
|
||||
| `Services/ServiceLivresApi.cs` | Lectures avec repli sur le cache, écritures refusées |
|
||||
|
||||
**Cinq instantanés, un par vue de l'API**, jamais un par requête : `catalogue` (le catalogue
|
||||
**entier**, sans filtre), `auteurs`, `prets-en-cours`, `utilisateur`, et `souhaits` depuis le
|
||||
2026-08-18 (voir « La liste d'envies est le cinquième instantané »). C'est exactement ce qui
|
||||
**Un instantané par vue de l'API**, jamais un par requête. Ils étaient cinq le 2026-08-18 ;
|
||||
ils sont **neuf** depuis le 2026-08-21 : `catalogue` (le catalogue **entier**, sans filtre),
|
||||
`auteurs`, `prets-en-cours`, `utilisateur`, `souhaits` (voir « La liste d'envies est le cinquième
|
||||
instantané »), `series`, `revues`, `souhaits-revues` et `version`. ⚠️ **Toute nouvelle vue de
|
||||
l'API doit en recevoir un**, faute de quoi l'écran correspondant est mort hors-ligne — c'est
|
||||
exactement ce qui était arrivé à la liste d'envies, et le symptôme (un `404` en plein écran) ne
|
||||
désignait pas la cause. C'est exactement ce qui
|
||||
justifie IndexedDB plutôt que le cache du service worker : un cache de réponses HTTP ne
|
||||
restituerait que les URL déjà visitées, donc **une recherche jamais tapée en ligne ne rendrait
|
||||
rien**. Vérifié en exécution — hors-ligne, chercher un auteur jamais affiché auparavant remonte
|
||||
@@ -717,7 +721,9 @@ Livre
|
||||
├── Editeur
|
||||
├── Format : Physique | Numerique
|
||||
├── TypeDocument : NonPrecise | Roman | BandeDessinee (défaut = NonPrecise, PAS Roman)
|
||||
├── NombrePages : int? (⚠ NULL = « on ne sait pas » ; 0 est REFUSÉ, pas stocké)
|
||||
├── CoverUrl
|
||||
├── UrlNotice (notice BnF ou OpenLibrary ayant prérempli la fiche)
|
||||
├── DateAjout
|
||||
└── AjoutePar (YNH_USER — traçabilité, PAS un cloisonnement)
|
||||
⚠ pas de colonne Statut, pas de colonne Auteur
|
||||
@@ -806,6 +812,20 @@ LivreSouhaite (la liste d'envies est PERSONNELLE — voir sa sec
|
||||
├── Rang (ordre CHOISI, 0 = le plus désiré — personnel comme le reste)
|
||||
└── UNIQUE (Utilisateur, TitreNormalise, AuteurNormalise)
|
||||
une œuvre par personne ; deux personnes peuvent souhaiter le même livre
|
||||
|
||||
RevueSouhaitee (table SŒUR de LivreSouhaite — PERSONNELLE elle aussi)
|
||||
├── Id
|
||||
├── Utilisateur (YNH_USER — une FRONTIÈRE, comme pour LivreSouhaite)
|
||||
├── Titre (« Médor », sans son numéro)
|
||||
├── TitreNormalise (NormalisationTexte, PAS CleOeuvre : une revue n'a pas de sous-titre)
|
||||
├── Numero : string? (NULL = la revue entière, et non un numéro précis)
|
||||
├── NumeroNormalise (jamais NULL — chaîne vide, sinon l'unicité ne tient pas)
|
||||
├── Issn (facultatif, CANONISÉ AVEC SON TIRET)
|
||||
├── Note
|
||||
├── DateAjout
|
||||
├── Rang (numéroté indépendamment de LivreSouhaite.Rang)
|
||||
└── UNIQUE (Utilisateur, TitreNormalise, NumeroNormalise)
|
||||
⚠ pas de CoverUrl, pas d'auteur, aucune FK vers Revue ni NumeroRevue
|
||||
```
|
||||
|
||||
Garder `Pret` comme table séparée (pas un champ sur `Livre`) pour conserver l'historique complet des prêts passés, pas juste l'état actuel.
|
||||
@@ -2353,14 +2373,21 @@ Vérifié en exécution : `crossOrigin === "use-credentials"` sur le lien, manif
|
||||
« Le déplacement est foireux » : d'un écran profond (bibliographie, fiche de tome, ajout
|
||||
d'envie), il fallait deviner quelle destination du menu ramenait en arrière.
|
||||
|
||||
Le retour passe par l'**historique du navigateur**, jamais par une destination calculée :
|
||||
un même écran est atteignable par plusieurs chemins — une fiche livre s'ouvre depuis le
|
||||
catalogue, une bibliographie, une série ou une recherche.
|
||||
⚠️ **Ce qui suit a été RENVERSÉ le 2026-08-21** — voir « S — le retour est une remontée
|
||||
hiérarchique ». Conservé parce que le motif d'origine reste vrai, et qu'il faut savoir
|
||||
pourquoi il n'a pas suffi.
|
||||
|
||||
⚠️ `history.back()` **seul ne suffit pas** : ouverte depuis l'écran d'accueil du téléphone,
|
||||
la PWA démarre sur une pile d'un seul cran et « retour » **sortirait de l'application**.
|
||||
D'où `js/navigation.js`, qui teste `history.length` — sans équivalent côté C# — et retombe
|
||||
sur le catalogue.
|
||||
> Le retour passe par l'**historique du navigateur**, jamais par une destination calculée :
|
||||
> un même écran est atteignable par plusieurs chemins — une fiche livre s'ouvre depuis le
|
||||
> catalogue, une bibliographie, une série ou une recherche.
|
||||
>
|
||||
> ⚠️ `history.back()` **seul ne suffit pas** : ouverte depuis l'écran d'accueil du téléphone,
|
||||
> la PWA démarre sur une pile d'un seul cran et « retour » **sortirait de l'application**.
|
||||
> D'où `js/navigation.js`, qui teste `history.length` — sans équivalent côté C# — et retombe
|
||||
> sur le catalogue.
|
||||
|
||||
⚠️ `wwwroot/js/navigation.js` **n'existe plus** : le retour est calculé en C# par
|
||||
`Services/RemonteeRoutes.cs`. Ne pas le recréer.
|
||||
|
||||
### L'icône cassée du bandeau
|
||||
|
||||
@@ -2516,6 +2543,10 @@ numéro de rang, lui, reste visible en consultation — c'est une information, p
|
||||
Le lien « Changer l'ordre » n'apparaît qu'à partir de deux tomes ou deux sous-séries :
|
||||
réordonner un élément unique n'a pas de sens.
|
||||
|
||||
⚠️ Depuis le 2026-08-21, « Modifier » et « Changer l'ordre » ne sont plus au **bas** de la
|
||||
fiche : ils sont groupés à droite de la ligne du titre, et `.actions-flottantes` ne subsiste
|
||||
sur cet écran que dans le mode ordre (le bouton « Terminé »). Voir « T et W ».
|
||||
|
||||
### Une seule entrée d'ajout : « Ajouter un ouvrage » (décidé avec l'utilisateur)
|
||||
|
||||
Question posée telle quelle par l'utilisateur : refaire une page « ajouter une revue »
|
||||
@@ -2793,3 +2824,237 @@ API lancée sur une base neuve, migration `ImagesEtUnesDesNumeros` appliquée au
|
||||
scan depuis une série ou depuis les envies, du « Tout cocher » et de l'indicateur d'attente
|
||||
repose sur la compilation et la relecture, pas sur une exécution. À regarder au prochain
|
||||
passage sur un vrai appareil.
|
||||
|
||||
## Lots Q à Y — retours d'usage du 2026-08-21, traités le 2026-08-21
|
||||
|
||||
Neuf lots, quatre commits. Ce qui suit ne redit pas ce que le code montre : seulement les
|
||||
décisions, les invariants qu'elles ont mis à l'épreuve, et ce qui a failli être fait de travers.
|
||||
|
||||
⚠️ **Aucun écran n'a été vérifié en navigateur sur toute la série.** Le rendu — formulaire
|
||||
réordonné, menu plein écran, actions en tête de fiche, page « À propos », deux sections d'envies
|
||||
— repose sur la compilation et la relecture, pas sur une exécution. Ce qui **est** vérifié : les
|
||||
services et l'API (618 tests au vert, 506 au début de la série) et les points d'entrée éprouvés à
|
||||
la main, listés en fin de section. À regarder au prochain passage sur un vrai appareil.
|
||||
|
||||
### S — le retour est une remontée hiérarchique, et non plus l'historique
|
||||
|
||||
⚠️ **Ceci renverse la décision du 2026-08-20** (« le retour passe par l'historique du navigateur,
|
||||
jamais par une destination calculée »). Le motif d'alors reste vrai — un même écran s'atteint par
|
||||
plusieurs chemins — **et il ne suffisait pas** : l'historique remonte aussi les **allers-retours**
|
||||
(ouvrir un filtre, changer un ordre, entrer puis sortir d'une édition). On cliquait cinq fois sans
|
||||
quitter le même écran. La question posée par la flèche est « où suis-je ? », pas « qu'ai-je fait
|
||||
juste avant ? ».
|
||||
|
||||
`MaBibli.Client/Services/RemonteeRoutes.cs` porte une **table de parenté explicite**, et une
|
||||
fonction **pure** donc testable sans navigateur.
|
||||
|
||||
⚠️ **Pas un découpage naïf de l'URL** : toutes les routes n'ont pas la forme d'une arborescence.
|
||||
`/souhaits/ordre` remonte bien à `/souhaits`, mais `/auteurs/{id}/bibliographie` remonte à
|
||||
`/auteurs` — **la fiche d'un auteur n'existe pas**. Un `..` d'URL aurait mené sur une page
|
||||
inexistante.
|
||||
|
||||
⚠️ **Toute route nouvelle doit être inscrite dans la table.** Le repli — la destination de menu de
|
||||
la branche — existe pour qu'une route oubliée reste utilisable, **pas** pour dispenser de la
|
||||
ligne : sans elle, `/revues/{id}/edition` retombait sur la *liste* des revues au lieu de la fiche
|
||||
qu'on venait de quitter (constaté, et corrigé au lot W). L'ordre de lecture de la table compte
|
||||
aussi : le premier modèle qui correspond gagne, donc `/revues/ajout` est écrit **avant**
|
||||
`/revues/{id}`.
|
||||
|
||||
Le garde-fou « ne jamais sortir de l'application » vit désormais là aussi : la fonction rend
|
||||
**toujours** un chemin interne. C'est ce qui remplace le test de `history.length`, qui ne disait
|
||||
pas ce qu'on croyait dans une PWA `standalone` — la pile d'une session y contient aussi ce qui
|
||||
précède l'application. `wwwroot/js/navigation.js` est **supprimé**.
|
||||
|
||||
### R — le menu colle en haut, et occupe l'écran entier sur téléphone
|
||||
|
||||
**Un seul conteneur collant** portant bandeau *et* menu, et non deux éléments collants
|
||||
superposés : le second aurait exigé d'écrire en dur la hauteur d'un bandeau qui **varie** avec la
|
||||
pastille hors-ligne et le nom d'utilisateur. C'est la même famille de piège que
|
||||
`--mb-onglets-hauteur`, disparue avec la barre du bas.
|
||||
|
||||
Sur téléphone, le menu déployé prend **tout l'écran** : sous le bandeau, il partageait la place
|
||||
avec la liste qu'on venait de quitter, et l'on choisissait sa destination au milieu d'autre chose.
|
||||
|
||||
⚠️ **Un calque plein écran sans porte de sortie est un piège.** Trois s'ajoutent à la fermeture
|
||||
déjà en place sur `LocationChanged` : la croix, **Échap** (le calque prend le focus à l'ouverture
|
||||
— sans focus, aucun `keydown` ne lui parviendrait, exactement comme le calque d'agrandissement des
|
||||
couvertures) et le clic hors des liens.
|
||||
|
||||
⚠️ Le plein écran est **explicitement annulé au-delà de 40 rem** : un menu ouvert au doigt puis
|
||||
une fenêtre agrandie laisserait sinon un calque sans bascule pour le refermer.
|
||||
|
||||
⚠️ **Les règles du menu restent en feuille GLOBALE** (`wwwroot/css/app.css`) : une règle scopée
|
||||
n'atteint pas ce que rend un `NavLink` (défaut du lot H, à ne pas refaire), et la navigation est
|
||||
ce qui doit le moins pouvoir tomber. Le point de rupture de **40 rem** reste écrit aux **deux**
|
||||
endroits (`app.css` et `MainLayout.razor.css`) et les deux doivent rester identiques.
|
||||
|
||||
### Q — l'ordre des champs du formulaire livre est une décision
|
||||
|
||||
Titre, **type de document**, thèmes, auteurs, rôles, puis le reste. Le type **commande la suite de
|
||||
la saisie** — les rôles ne se posent que là — et il fallait descendre tout le formulaire pour dire
|
||||
« c'est une BD », c'est-à-dire après avoir saisi ce qui en dépend.
|
||||
|
||||
Deux invariants **maintenus**, et il fallait résister à les entamer :
|
||||
|
||||
- **rien n'est présélectionné** : `NonPrecise` ne prétend toujours rien. Remonter le champ le rend
|
||||
visible, ce qui est le but ; le préremplir écrirait quelque chose de faux ;
|
||||
- **les rôles n'apparaissent qu'à partir de deux auteurs**, BD ou non. IDEES.md proposait « BD
|
||||
**et** deux auteurs » : ç'aurait été un revirement, un album signé d'une seule personne n'ayant
|
||||
pas plus de rôle à distinguer qu'un roman.
|
||||
|
||||
⚠️ Le champ des noms se relie **à chaque frappe** et reconstruit la liste entière : des tests
|
||||
verrouillent la préservation des rôles, que ce réordonnancement ne doit pas entamer.
|
||||
|
||||
### T et W — les actions remontent à droite du titre, la fiche revue prend deux modes
|
||||
|
||||
Sur `/series/{id}` comme sur `/revues/{id}`, les actions sont **groupées à droite de la ligne du
|
||||
titre**, dans le même bloc d'en-tête — disposition déjà tenue par la bibliographie et la liste des
|
||||
auteurs. Reléguées en bas, les actions d'une saga de vingt tomes ou d'une collection de trente
|
||||
numéros ne se découvraient qu'après avoir tout déroulé. La barre `.actions-flottantes` disparaît
|
||||
donc de la **consultation** d'une série ; elle ne subsiste que sur l'écran d'ordre.
|
||||
|
||||
`/revues/{id}` consulte, `/revues/{id}/edition` modifie — troisième écran à prendre cette forme
|
||||
après la fiche livre et la fiche série.
|
||||
|
||||
⚠️ **Modifier et retirer un numéro n'existent QUE dans l'édition** : en consultation, ces boutons
|
||||
se déclenchaient sous le pouce en faisant défiler la liste. Même raison que les flèches d'ordre
|
||||
d'une série, reléguées sur leur propre écran. Un retrait de numéro se **confirme** désormais, comme
|
||||
la suppression d'un livre ou d'une revue.
|
||||
|
||||
⚠️ **« Ajouter un numéro » RESTE en consultation, et ce n'est pas une entorse** : le geste ne
|
||||
touche pas à la fiche de la revue, il range un objet de plus — même exception que « Prêter » et le
|
||||
statut de lecture sur une fiche livre. Le formulaire se replie derrière son bouton et s'ouvre de
|
||||
lui-même quand on arrive du scanner avec un numéro lu sur l'add-on EAN-2 : demander un clic de plus
|
||||
pour saisir ce qu'on tient en main serait un détour.
|
||||
|
||||
⚠️ Les deux routes partagent le paramètre `{id}` : le routeur ne redessine rien en passant de
|
||||
l'une à l'autre, d'où l'abonnement à `LocationChanged` — piège déjà rencontré sur la fiche livre.
|
||||
Quitter l'édition referme au passage ce qui n'a de sens que là : formulaire de numéro ouvert,
|
||||
retrait ou suppression en attente de confirmation.
|
||||
|
||||
### V — toute la ligne d'une revue ouvre sa fiche
|
||||
|
||||
Comme la carte entière au catalogue. ⚠️ **Le titre reste un vrai lien** — adresse, clavier,
|
||||
clic-milieu — et son clic **ne remonte pas** jusqu'à la ligne, qui naviguerait une seconde fois.
|
||||
Toute action posée un jour sur cette ligne devra faire de même, sinon la ligne avalera le clic du
|
||||
bouton.
|
||||
|
||||
### U — le nombre de pages, et pourquoi `0` est refusé
|
||||
|
||||
`Livre.NombrePages` est **nullable, sans valeur par défaut** : « 0 page » se lirait comme une
|
||||
donnée là où l'on veut dire « on ne sait pas ». Même raison que `TypeDocument.NonPrecise` — un
|
||||
défaut qui ne prétend rien n'a rien à reprendre, d'où une migration réduite à un `AddColumn`. Le
|
||||
service **refuse** un zéro avec un message plutôt que de l'écrire ; **effacer le champ** reste la
|
||||
façon de revenir à « inconnu ».
|
||||
|
||||
Le préremplissage vient de `dc:format`, que rien ne lisait jusqu'ici. ⚠️ **Ce champ n'est pas un
|
||||
nombre mais une phrase décrivant le support** : « 1 vol. (113 p.) : ill., couv. ill. en coul. ;
|
||||
18 cm ». Les règles de `NettoyageIsbd.NombrePages` sont donc **étroites** — un nombre suivi de
|
||||
`p.` ou de `page(s)`, rien d'autre — et tout le reste rend `null`.
|
||||
|
||||
⚠️ **L'erreur n'est pas symétrique** : un champ vide se remplit à la main en trois secondes, un
|
||||
chiffre faux s'enregistre sans que personne ne le voie. « 1 vol. » ne vaut pas 1, « 30 cm » ne vaut
|
||||
pas 30, et « (p. 45-90) » — une pagination de contribution — ne vaut rien. La valeur reste
|
||||
**proposée** dans un champ modifiable, et rien n'est déduit pour un ebook (le nombre de pages y
|
||||
varie avec la police : s'il est saisi, c'est une indication).
|
||||
|
||||
### X — éditer une envie, et souhaiter une revue
|
||||
|
||||
#### `PUT /api/souhaits/{id}`
|
||||
|
||||
- **Les formes normalisées se recalculent.** Sans cela, le rapprochement « déjà au catalogue »
|
||||
continuerait de se faire sur l'ancienne forme, et **le signalement mentirait sans le dire**.
|
||||
- **Le filtre sur l'appelant fait partie de la clé de recherche**, pas d'une vérification
|
||||
ultérieure : l'envie d'un autre est **introuvable** (404), jamais refusée (403).
|
||||
- ⚠️ **Une édition peut heurter l'unicité `(Utilisateur, TitreNormalise, AuteurNormalise)`**, ce
|
||||
qu'un ajout ne peut pas. Renommer une envie en une autre déjà présente répond par un message
|
||||
lisible, jamais par « UNIQUE constraint failed ». **400 et non 409**, contrairement au doublon du
|
||||
catalogue : là-bas posséder deux exemplaires est légitime et l'appel se reconfirme, ici l'index
|
||||
l'interdit et il n'y a **rien à confirmer**.
|
||||
- ⚠️ **La couverture n'est écrite que si la charge utile en porte une.** Aucun écran n'offre de
|
||||
champ « URL de couverture » pour une envie (décision actée) : un remplacement inconditionnel
|
||||
l'aurait effacée à la première faute de frappe corrigée.
|
||||
- Le **rang n'est pas touché** — l'ordre a son propre point d'entrée.
|
||||
|
||||
#### `RevueSouhaitee` : une table sœur, pour la quatrième fois
|
||||
|
||||
Même argument que `Revue` face à `Livre`, et que `LivreSouhaite` face au catalogue. Une envie de
|
||||
livre est identifiée par un couple **(œuvre, auteur)** : c'est ce que porte son index unique et ce
|
||||
qui permet de la rapprocher du catalogue. Un numéro de revue **n'a pas d'auteur** et se distingue
|
||||
par son **numéro** — deux notions que la clé de `LivreSouhaite` ne sait pas exprimer sans devenir
|
||||
fausse pour tout le monde.
|
||||
|
||||
⚠️ `NumeroNormalise` est **NOT NULL avec un défaut vide** : SQLite tient deux `NULL` pour
|
||||
distincts, et « Médor, sans numéro » s'ajouterait autant de fois qu'on cliquerait. Piège déjà
|
||||
rencontré sur `LivreSouhaite.AuteurNormalise` — il se repose à chaque table personnelle.
|
||||
|
||||
⚠️ **Le coût d'une table sœur se paie partout où les deux listes se rejoignent** : affichage,
|
||||
export `.txt`, export `.csv`, instantané hors-ligne `souhaits-revues`, et
|
||||
`ServiceRenormalisation`. C'est là que le sujet se rate s'il est bâclé — une envie de revue
|
||||
invisible en librairie ne sert à rien.
|
||||
|
||||
Décisions prises en chemin :
|
||||
|
||||
| Point | Décision | Pourquoi |
|
||||
|---|---|---|
|
||||
| Affichage | **deux sections**, jamais un classement entrelacé | chaque table numérote son rang **indépendamment** ; mélanger deux suites sans rapport produirait un ordre que personne n'a choisi |
|
||||
| `.txt` | livres groupés par auteur, puis les revues en **section à part, à la fin** | une revue n'a pas d'auteur : la ranger sous « Auteur non précisé » la mêlerait à des **livres** dont l'auteur est simplement inconnu. Au kiosque comme en librairie, ce ne sont pas les mêmes rayons |
|
||||
| `.csv` | colonne **« Type »**, en-tête **« ISBN / ISSN »** | sans elles, un tri par titre rendrait revues et livres indiscernables, et la colonne des codes mêlerait deux natures de code en silence |
|
||||
| Réordonnancement des envies de revues | **non fait** | la section est courte ; à rouvrir si l'usage le demande (noté dans IDEES.md) |
|
||||
| Signalement « déjà possédé » | **non fait** | demanderait de rapprocher un numéro d'un `NumeroRevue`, et l'envie porte souvent la revue entière (noté dans IDEES.md) |
|
||||
| `CoverUrl` | **absent** | aucune source ne donne l'image d'un numéro souhaité — et donc **rien à ajouter au garde de `GET /api/couvertures`** |
|
||||
|
||||
⚠️ **L'ISSN y est canonisé à part dans `ServiceRenormalisation`** : `Renormaliser` n'applique rien
|
||||
quand la clé ne bouge pas, donc un ISSN mal formé sur une ligne au titre inchangé lui échapperait.
|
||||
C'est la même précaution que `CanoniserLesIssn` sur `Revue` (lot K2).
|
||||
|
||||
### Y — l'application dit sa version, et sous quelle licence
|
||||
|
||||
`/a-propos` : manuel annoncé à venir, `mailto:info@limonier.be`, site de l'auteur, version publiée
|
||||
avec sa date de build, licence **AGPL v3** et lien vers le dépôt. ⚠️ **L'AGPL attend que les
|
||||
utilisateurs d'un service en réseau puissent en obtenir la source** : ce lien n'est pas un
|
||||
ornement, c'est ce que la licence demande.
|
||||
|
||||
L'application ne connaissait pas sa version : elle vivait dans le `manifest.toml` du dépôt
|
||||
`mabibli_ynh` et dans les tags git, **jamais dans le binaire**. `build/publier-release.sh` pose
|
||||
désormais `-p:Version` et `-p:MaBibliDateBuild` (en respectant `SOURCE_DATE_EPOCH`), et l'API rend
|
||||
les deux par `GET /api/version`.
|
||||
|
||||
⚠️ **L'horodatage de build est le TÉMOIN de l'injection, et rien ne le calcule côté MSBuild.**
|
||||
Sans lui, la version lue serait le `1.0.0` que le SDK .NET pose par défaut dans tout assembly : il
|
||||
se lirait comme une vraie version alors qu'il ne désigne rien — et c'est **exactement** la valeur
|
||||
qu'on ira chercher pour diagnostiquer un appareil au cache dépareillé. Mieux vaut ne rien annoncer :
|
||||
un binaire compilé à la main se déclare « version de développement », ce qui est vrai.
|
||||
`VersionApplication.Depuis` est une fonction **pure**, éprouvable sans fabriquer d'assembly, et
|
||||
elle coupe le `+empreinte` que le SDK ajoute parfois à `AssemblyInformationalVersion`.
|
||||
|
||||
Trois décisions d'écran :
|
||||
|
||||
- **L'entrée du menu est DÉTACHÉE des six destinations** — filet au-dessus sur téléphone, à gauche
|
||||
en rangée sur PC : « À propos » est une **annexe**, pas une septième destination. Ses règles
|
||||
vivent en feuille **globale**, comme tout le menu.
|
||||
- **Le `mailto:` reste un lien même hors-ligne** : il ne charge aucune page et passe la main au
|
||||
client de messagerie, qui sait mettre un message en attente. Le site et le dépôt, eux, basculent
|
||||
en **boutons désactivés portant leur motif**, comme les liens d'export des envies — un `<a>`
|
||||
laissé actif quitterait l'application pour une page d'erreur du navigateur.
|
||||
- **La version est mise en instantané hors-ligne** : on lit ce numéro justement quand quelque chose
|
||||
ne va pas, et un appareil qu'on soupçonne est souvent celui qui n'a plus de réseau. L'écran dit
|
||||
alors que c'est la **dernière version vue du serveur**, et non la version courante.
|
||||
|
||||
### ⚠️ `dotnet ef` et les migrations de la série
|
||||
|
||||
Deux migrations, toutes deux sans reprise de données : `NombreDePages` (un `AddColumn`, le défaut
|
||||
`NULL` ne prétendant rien) et `EnviesDeRevues` (une table neuve). C'est ce que permet un défaut qui
|
||||
n'affirme rien — comparer avec `RangDesEnvies`, qui avait dû reconduire un ordre en SQL.
|
||||
|
||||
### Vérifié en exécution le 2026-08-21 (API, pas navigateur)
|
||||
|
||||
| Cas | Résultat |
|
||||
|---|---|
|
||||
| ISSN d'envie de revue saisi « 24666718 » | rangé **`2466-6718`** |
|
||||
| `PUT /api/souhaits/{id}` sur l'envie d'un autre | **404** |
|
||||
| Exports `.txt` et `.csv` | portent bien **les deux moitiés** de la liste |
|
||||
| `GET /api/version` sans injection | `{"numero":null,"publiee":false}` |
|
||||
| `GET /api/version` avec `-p:Version=0.4.1 -p:MaBibliDateBuild=…` | `{"numero":"0.4.1","publiee":true}` |
|
||||
|
||||
**618 tests au vert** (506 au début de la série), aucun avertissement de compilation.
|
||||
|
||||
Reference in New Issue
Block a user