État partagé : le journal des choix de vignettes passe dans la base SQLite (STUD-1111, t_b7b6c623) #1689
Loading…
Reference in a new issue
No description provided.
Delete branch "hermes/migration-choix-vignettes-sqlite"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Carte : t_b7b6c623 (kanban atelier) — migrer
/opt/studio/choix-vignettes.jsonlvers SQLite, via bascule et retour arrière (STUD-1111).Ce que la branche porte
choix_vignettesdansetat_partage.sql: une ligne par ENTRÉE du journal,rangpour garder l'ordre (tne le donne pas : 86 des 233 lignes réelles partagent la même seconde), un champ par colonne, les champs absents à NULL — c'est ce NULL qui rend le journal tel qu'il était, donc la comparaison avant bascule porte sur les deux mêmes états. Les 233 lignes réelles du 132 relues depuis la base sont identiques, entrée par entrée (mesuré).etat_partage.py:choix_vignettes_lire(),choix_vignettes_detail(),choix_vignettes_remplacer()(tout le journal en une transaction ; un journal identique n'est pas réécrit),choix_vignettes_ajouter()(des entrées à la fin, en une transaction — une transaction parce qu'un avis sur la troisième vignette ajoute aussi le choix automatique « les trois validées »)._infra/etat-service.py:GET /choix-vignettes,GET /choix-vignettes/detail,POST /choix-vignettes/ajouter,POST /choix-vignettes/remplacer.etat_client.py:choix_vignettes(),choix_vignettes_detail(),choix_vignettes_ajouter(),choix_vignettes_remplacer(),basculer choix-vignettes [--essai] [--attendre <s>]/revenir choix-vignettes, sur la recette de STUD-1107 : comparaison avant bascule, refus tant qu'un montage tourne, ancien gardé en.bak-sqlite-<date>, retour arrière en une commande. Chemin du journal forçable par$ETAT_CHOIX_VIGNETTES. Deux gardes en plus : un journal absent ou vide ne remplace jamais celui de la base, et le fichier est réécrit depuis la base juste après la bascule (sinonexiger(), la publication, ne trouverait plus aucun choix)._tableau/socle.py:choix_vignettes_ajouter(entrees, chemin)— passé la bascule, les entrées partent dans la base ET s'ajoutent au fichier, qui reste la copie lue par la page du 132, le relevé etexiger(). La source est rendue et tracée : « service », « fichier », « fichier-muet » (bascule faite, service muet : l'entrée n'est que dans le fichier, et c'est dit), « refusee » (le service a répondu et le schéma a refusé). Sur le 132,etat_client.pyn'est pas livré : le socle appelle le service enurllib, commecollecte.py, et hors de/opt/studiosansSTUDIO_ETAT_URLil n'essaie même pas (un banc ne parle pas au service de production)._tableau/serveur_vignettes.py: les trois écritures du 132 (avis de vignette, avis de planche, suivi du test A/B) passent par le socle ; la trace du journal de l'unité dit où la ligne fait foi.Ce qui n'est PAS changé : la LECTURE. La page du 132 (
etat.py,atelier/at_onglet.py), le relevé (retours_ordres.py) et la publication (choix_vignette.exiger) lisent toujours le fichier, qui reste écrit à chaque geste. Une page qui interrogerait le service dépendrait d'un 130 muet à chaque rendu. Aucune route/api/...n'est touchée : rien à signaler au front.À dire au chef : le nom
choix-vignettesa besoin du motif debascules.nomet de_reparer_bascules(SQLite ne modifie pas unCHECK, une base ouverte avant aujourd'hui refuserait le nom). Ce même bout est déjà porté par la branche sœurhermes/migration-reseaux-sqlite(PR #1682), copié à l'identique ici pour que les deux PR se fusionnent sans conflit.Bancs :
python3 _tableau/test_choix_vignettes_bascule.py(8 cas, contre un vrai service sur 127.0.0.1 et une base jetable : essai, ligne illisible sautée et dite, écriture avant et après la bascule, service muet, service qui refuse,.bak-sqlite, retour arrière puis re-bascule, journal absent refusé),python3 _infra/test_etat_service.py(les quatre routes + les refus en 400),python3 controle/test_etat_partage.py, et la suite du tableau (_tableau/lancer-tests.py --tout).fusion-auto: pas de fusion, la CI est rouge (Front Svelte). Le chef de l'atelier renvoie la carte à l'agent ; l'étiquette tombera d'elle-même une fois corrigé.f194acf3d0ac1271185cfusion-auto: pas de fusion, la CI est rouge (Front Svelte). Le chef de l'atelier renvoie la carte à l'agent ; l'étiquette tombera d'elle-même une fois corrigé.Preuves du 2026-10-15 (commit
ac127118, rebasé surmainbabcc421).Bancs du changement
Suite du tableau (
python3 _tableau/lancer-tests.py --tout) : 170 bancs, 169 « ok » — il reste un banc rouge etle cliquet, tous deux hérités de
main:Ces deux-là n'ont rien à voir avec la branche : le même banc, à la même assertion et avec les mêmes quatre clés
(
fin_seuil,monetisation,courbes,seuil_heures_apres), est rouge surorigin/main(babcc421) pris sansaucune modification ; le même cliquet tombe aussi sur
main. La carte sœurhermes/porte-ci-tableau(run 296) lesprend déjà en charge.
Note d'environnement :
/tmp(tmpfs de 16 G) était saturé par les restes des autres cartes (df: 16 G / 16 G,0 octet libre) ; la première suite est morte en « database or disk is full » — 161 bancs rouges pour cette seule
raison. La suite ci-dessus a tourné avec
TMPDIRsur le NVMe (/home/atelier/tmp-tests-t_b7b6c623) : c'est laseule condition à connaître pour rejouer.
Rust (inchangé par cette branche, mais la carte le demande) :
cargo test --release --locked→ 77 passed,0 failed ;
cargo build --release --locked→ aucune erreur, aucun avertissement.CI Forgejo (
ac127118) : jobRust (_tableau/rust)success (push et PR) ; jobFront Svelte (_tableau/front)failure, hérité lui aussi — la branche ne touche aucun fichier de
_tableau/front/, et le même banc tombe surmain:vitest run→tests/stockage.test.js > la jauge de stockage, en mots et en chiffres (STUD-1238),195 passed / 1 failed. Rien à corriger ici, mais la fusion automatique attendra que ce job soit vert.
Prochain geste, après la fusion et le déploiement du 132 (aucun geste sur les données n'est fait par la
branche) :
python3 etat_client.py basculer choix-vignettes --essaipour voir la comparaison sans rien écrire,puis sans
--essai. Retour arrière :python3 etat_client.py revenir choix-vignettes. Le journal du 132 n'esttouché qu'à ce moment-là, et son ancien reste en
.bak-sqlite-<date>.Relecture du 2026-10-15 (tête
ac127118, basemain) — Relu, conforme, étiquettereluposée et revérifiée sur l'API (['ci-rouge','relu'], mergeable). Tout ce qui suit est mesuré par moi sur un worktree détaché, pas repris du compte rendu.1. La branche.
babcc421(=origin/mainà l'heure de la mesure) est ancêtre deac127118. 13 fichiers, +842/−33 : documentation (OUTILS.md,STANDARD-PRODUCTION.md,VIGNETTES.md,_tableau/LXC-132.md), service et schéma (_infra/etat-service.py,etat_partage.py,etat_partage.sql), socle et écriture du 132 (_tableau/socle.py,_tableau/serveur_vignettes.py), client (etat_client.py), bancs. Aucun fichier de production (01-textes/à07-publication/), aucun fichier front/svelte/js/ts.2. Les suites.
Rejoué sur
babcc421nu (sans la PR) : même fichier, même ligne, même message (attendu « relevé à », rendu « relevé hier à 22 h 44 »). Le rouge front est hérité demain— la PR ne touche aucun fichier front.npx vite build: code 0.3. Le point risqué, la réparation d'une base ancienne. J'ai fabriqué une base avec l'
etat_partage.sqldemain(CHECKnom IN ('sorties', 'file_montage')), posé deux lignes, puis ouvert avec le code de la PR : les deux lignes sont gardées,choix-vignettesest ensuite accepté (l'ancienne contrainte l'aurait refusé), et la tablechoix_vignettesest créée avec ses 18 colonnes (rang, t, type, chaine, episode, lettre, decision, commentaire, fichier, auto, par, motif, video, gagnante, ctr, impressions, retention, maj). Une coupure au milieu (bascules_avantlaissée en place) est reprise sans doublon ni perte.4. Aucune entrée réelle ne sera refusée par le schéma. Les cinq formes écrites par
_tableau/serveur_vignettes.py(avis, choix auto « les trois validées », planche, ab-pose, ab-verdict) n'emploient que des champs deCHOIX_CHAMPS, tous textuels, longueurs dans les bornes.5. La migration ne peut pas être un no-op silencieux. La garde de
socle.choix_vignettes_basculeestdirname(__file__) != "/opt/studio":_tableau/deploy-132.shaplatit bien_tableau/*.pydans/opt/studio/(le préfixe est retiré au tar), donc la machine qui écrit le journal est bien celle où l'appel au service se fait.6. Rien de retiré, rien de caché. Aucun onglet ni fonction visible touché : aucun fichier front, et le front ne lit pas le journal (aucune occurrence de
choix-vignettesdans_tableau/front/). Il n'y a donc pas d'interface à regarder — la lecture reste au fichier, la base ne concerne que l'écriture. Sur les 842 lignes ajoutées : aucun emoji, aucun secret, aucun base64 long. L'écriture dit toujours où la ligne fait foi (service,fichier,fichier-muet,refusee) : un service muet ne coûte pas l'entrée, il la garde au fichier et le dit.Ce que je n'ai pas mesuré. La comparaison
--essaisur le journal réel du 132 (233 entrées) : le 132 (192.168.1.86) n'accepte aucune de mes clés ssh (root,agent,stephane,hermes: publickey refusée) et/opt/studio/n'existe pas ici. Je n'ai mesuré que la copie du dépôt (2 entrées) et le schéma contre les formes produites. Le journal de la CI n'est pas lisible par l'API Forgejo (200 avec 0 octet) : l'héritage du rouge est mesuré localement, surbabcc421, ce qui revient au même commit de base.fusion-auto: pas de fusion, la PR est en conflit avec main : à rebaser. Le chef de l'atelier renvoie la carte à l'agent ; l'étiquette tombera d'elle-même une fois corrigé.ac1271185cc5a8a16aa1Rebasé sur
mainet poussé —c5a8a16a. Le conflit venait d'un rebasage en retard :socle.py,etat_partage.pyetetat_client.pyavaient bougé sous la branche (instagram-filefusionnée en3a985afb, puis quatre fusions).Ce qui a été résolu, en gardant les deux apports :
etat_client.fichier_ancien: une seule définition, la cléchoix-vignettess'ajoute àsorties/file_montage/atelier(la fusion en avait laissé deux, la seconde masquaitatelier—KeyErrorsur l'essai de bascule de l'atelier) ;--essai) :choix-vignettestraité commeatelier, le bloc qui relisaitsorties.jsonest retiré ;revenir:sorties/atelierpassent parecrire_json_etat(même écriture que la branche, un seul appel) ;socle.pyvers_tableau/choix_vignettes_donnees.py:socle.pyà 599 lignes (plafond 600, sans dérogation) ;_tableau/LXC-132.mdet_infra/test_etat_service.py: la liste est renumérotée, le journal des choix est le point 2, « hôte du LXC 130 » devient « LXC 132 ».Mesures :
python3 _tableau/lancer-tests.py --tout→ 208 banc(s) verts, code 0, cliquet de couverture passé (la garde « liste vide » du module est désormais couverte, cas 9 du banc) ;_tableau/test_garde_couches.py,test_garde_doublons.py,test_garde_plafond.py→ ok ;_infra/test_etat_service.py→ ok ;_tableau/test_choix_vignettes_bascule.py→ TOUT PASSE (9 cas).Fusionnée automatiquement par
fusion-auto(forge) : CI verte, sans conflit.