État partagé : le journal des choix de vignettes passe dans la base SQLite (STUD-1111, t_b7b6c623) #1689

Merged
hermes-fusion merged 1 commit from hermes/migration-choix-vignettes-sqlite into main 2026-10-15 22:30:08 +02:00
Collaborator

Carte : t_b7b6c623 (kanban atelier) — migrer /opt/studio/choix-vignettes.jsonl vers SQLite, via bascule et retour arrière (STUD-1111).

Ce que la branche porte

  • Table choix_vignettes dans etat_partage.sql : une ligne par ENTRÉE du journal, rang pour garder l'ordre (t ne 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 (sinon exiger(), 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é et exiger(). 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.py n'est pas livré : le socle appelle le service en urllib, comme collecte.py, et hors de /opt/studio sans STUDIO_ETAT_URL il 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-vignettes a besoin du motif de bascules.nom et de _reparer_bascules (SQLite ne modifie pas un CHECK, une base ouverte avant aujourd'hui refuserait le nom). Ce même bout est déjà porté par la branche sœur hermes/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).

**Carte** : t_b7b6c623 (kanban atelier) — migrer `/opt/studio/choix-vignettes.jsonl` vers SQLite, via bascule et retour arrière (STUD-1111). **Ce que la branche porte** - Table `choix_vignettes` dans `etat_partage.sql` : une ligne par ENTRÉE du journal, `rang` pour garder l'ordre (`t` ne 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 (sinon `exiger()`, 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é et `exiger()`. 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.py` n'est pas livré : le socle appelle le service en `urllib`, comme `collecte.py`, et hors de `/opt/studio` sans `STUDIO_ETAT_URL` il 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-vignettes` a besoin du motif de `bascules.nom` et de `_reparer_bascules` (SQLite ne modifie pas un `CHECK`, une base ouverte avant aujourd'hui refuserait le nom). Ce même bout est déjà porté par la branche sœur `hermes/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`).
État partagé : le journal des choix de vignettes passe dans la base (STUD-1111)
Some checks failed
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (push) Successful in 32s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (push) Failing after 11s
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (pull_request) Successful in 34s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (pull_request) Failing after 12s
f194acf3d0
Cause : `/opt/studio/choix-vignettes.jsonl` — les choix de vignette de l'opérateur, ses avis de vignette et de
planche, le suivi des tests A/B — n'avait qu'un foyer : un fichier du LXC 132, allongé par studio-web
(`_tableau/serveur_vignettes.py`), recopié dans le dépôt par `_tableau/choix_vignette.py rapatrier`, et lu par la
page du 132, le relevé et la publication. Ni transaction, ni contrainte, et deux machines qui n'en partageaient
pas la version en cours (233 entrées, 38 Ko, mesurées le 2026-10-14).

Remède : une table `choix_vignettes` dans `etat_partage.sql` — une ligne par ENTRÉE, `rang` pour garder l'ordre du
journal (`t` ne le donne pas : 86 des 233 lignes réelles partagent la même seconde), un champ par colonne, les
champs ABSENTS à NULL pour que le journal se relise tel qu'il était —, les routes `GET /choix-vignettes`,
`GET /choix-vignettes/detail`, `POST /choix-vignettes/ajouter` (l'écriture du 132, en une transaction) et
`POST /choix-vignettes/remplacer` dans `_infra/etat-service.py`, `basculer choix-vignettes [--essai]
[--attendre <s>]` et `revenir choix-vignettes` dans `etat_client.py` (comparaison avant bascule, refus tant qu'un
montage tourne, ancien gardé en `.bak-sqlite-<date>`, retour arrière en une commande — et un journal absent ou
vide ne remplace jamais celui de la base), et `socle.choix_vignettes_ajouter` : 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é et
`exiger()`. La source de l'écriture est rendue et tracée (« service », « fichier », « fichier-muet », « refusee ») :
un service muet ne coûte jamais l'entrée, et c'est dit — sur le 132, `etat_client.py` n'étant pas livré, le socle
appelle le service en `urllib`, comme `collecte.py`.

Le même commit reprend la modification de `bascules.nom` (liste fermée -> motif, `_reparer_bascules`) déjà portée
par la branche sœur `hermes/migration-reseaux-sqlite` du même chantier : SQLite ne modifie pas un `CHECK`, et une
base ouverte avant aujourd'hui refuserait le nouveau nom.

Bancs : `_tableau/test_choix_vignettes_bascule.py` (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é), `_infra/test_etat_service.py` (les quatre routes) ; `controle/test_etat_partage.py` et la suite du tableau
restent verts.
Collaborator

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é.

`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é.
hermes-bot force-pushed hermes/migration-choix-vignettes-sqlite from f194acf3d0
Some checks failed
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (push) Successful in 32s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (push) Failing after 11s
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (pull_request) Successful in 34s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (pull_request) Failing after 12s
to ac1271185c
Some checks failed
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (push) Successful in 41s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (push) Failing after 11s
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (pull_request) Successful in 33s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (pull_request) Failing after 11s
2026-10-15 00:38:50 +02:00
Compare
Collaborator

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é.

`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é.
Author
Collaborator

Preuves du 2026-10-15 (commit ac127118, rebasé sur main babcc421).

Bancs du changement

$ python3 _tableau/test_choix_vignettes_bascule.py
ok 1 : écriture de l'avis — la base et le fichier portent la ligne, et la trace dit l'origine
ok 2 : essai — rien n'est écrit, la comparaison porte sur la base jetable
ok 3 : bascule — la comparaison, le .bak-sqlite-<date>, la ligne illisible sautée et dite
ok 4 : après la bascule — l'avis part par le service, le fichier le reprend, et le détail se relit
ok 5 : service muet — l'avis ne se perd pas, la trace dit « fichier-muet »
ok 6 : service qui refuse — la trace dit « refusee », l'avis s'ajoute quand même au fichier
ok 7 : re-bascule après le retour — la base porte le journal en vigueur, ligne illisible sautée
ok 8 : un journal absent ne remplace pas celui de la base — la bascule refuse, la base garde tout
TOUT PASSE

$ python3 _infra/test_etat_service.py
OK : le service d'état répond sur chaque route, refuse en 400/403/404, dit ses pannes en 500, et pose son schéma avant d'écouter

$ python3 controle/test_etat_partage.py
ok 6 : sur partage réseau, la base ne s'ouvre pas, et un montage en mode local sort 1 vers lancer-long.sh
TOUT PASSE

Suite du tableau (python3 _tableau/lancer-tests.py --tout) : 170 bancs, 169 « ok » — il reste un banc rouge et
le cliquet, tous deux hérités de main :

ROUGE _tableau/test_accueil_parite.py (code 1)
CLIQUET — la couverture a BAISSÉ : cœur 99.1 % < 99.4 % ; ensemble 96.7 % < 99.0 %

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 sur origin/main (babcc421) pris sans
aucune modification ; le même cliquet tombe aussi sur main. La carte sœur hermes/porte-ci-tableau (run 296) les
prend 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 TMPDIR sur le NVMe (/home/atelier/tmp-tests-t_b7b6c623) : c'est la
seule 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) : job Rust (_tableau/rust) success (push et PR) ; job Front Svelte (_tableau/front)
failure, hérité lui aussi — la branche ne touche aucun fichier de _tableau/front/, et le même banc tombe sur
main : 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 --essai pour voir la comparaison sans rien écrire,
puis sans --essai. Retour arrière : python3 etat_client.py revenir choix-vignettes. Le journal du 132 n'est
touché qu'à ce moment-là, et son ancien reste en .bak-sqlite-<date>.

Preuves du 2026-10-15 (commit ac127118, rebasé sur `main` babcc421). **Bancs du changement** $ python3 _tableau/test_choix_vignettes_bascule.py ok 1 : écriture de l'avis — la base et le fichier portent la ligne, et la trace dit l'origine ok 2 : essai — rien n'est écrit, la comparaison porte sur la base jetable ok 3 : bascule — la comparaison, le .bak-sqlite-<date>, la ligne illisible sautée et dite ok 4 : après la bascule — l'avis part par le service, le fichier le reprend, et le détail se relit ok 5 : service muet — l'avis ne se perd pas, la trace dit « fichier-muet » ok 6 : service qui refuse — la trace dit « refusee », l'avis s'ajoute quand même au fichier ok 7 : re-bascule après le retour — la base porte le journal en vigueur, ligne illisible sautée ok 8 : un journal absent ne remplace pas celui de la base — la bascule refuse, la base garde tout TOUT PASSE $ python3 _infra/test_etat_service.py OK : le service d'état répond sur chaque route, refuse en 400/403/404, dit ses pannes en 500, et pose son schéma avant d'écouter $ python3 controle/test_etat_partage.py ok 6 : sur partage réseau, la base ne s'ouvre pas, et un montage en mode local sort 1 vers lancer-long.sh TOUT PASSE **Suite du tableau** (`python3 _tableau/lancer-tests.py --tout`) : 170 bancs, 169 « ok » — il reste un banc rouge et le cliquet, tous deux hérités de `main` : ROUGE _tableau/test_accueil_parite.py (code 1) CLIQUET — la couverture a BAISSÉ : cœur 99.1 % < 99.4 % ; ensemble 96.7 % < 99.0 % 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 sur `origin/main` (babcc421) pris sans aucune modification ; le même cliquet tombe aussi sur `main`. La carte sœur `hermes/porte-ci-tableau` (run 296) les prend 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 `TMPDIR` sur le NVMe (`/home/atelier/tmp-tests-t_b7b6c623`) : c'est la seule 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) : job `Rust (_tableau/rust)` **success** (push et PR) ; job `Front Svelte (_tableau/front)` **failure**, hérité lui aussi — la branche ne touche aucun fichier de `_tableau/front/`, et le même banc tombe sur `main` : `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 --essai` pour voir la comparaison sans rien écrire, puis sans `--essai`. Retour arrière : `python3 etat_client.py revenir choix-vignettes`. Le journal du 132 n'est touché qu'à ce moment-là, et son ancien reste en `.bak-sqlite-<date>`.
Author
Collaborator

Relecture du 2026-10-15 (tête ac127118, base main) — Relu, conforme, étiquette relu posé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 de ac127118. 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.

$ cargo test --release --locked        # _tableau/rust
77 passed, 0 failed (18 binaires) ; cargo build release sans erreur

$ npx vitest run                       # _tableau/front, tête de la PR
Test Files  1 failed | 26 passed (27)
Tests       1 failed | 198 passed (199)
FAIL  tests/stockage.test.js:82 (STUD-1238)

Rejoué sur babcc421 nu (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é de main — la PR ne touche aucun fichier front. npx vite build : code 0.

$ python3 _tableau/test_choix_vignettes_bascule.py
10 cas, TOUT PASSE (essai, écriture avant/après bascule, service muet, service qui refuse,
.bak-sqlite, retour arrière puis re-bascule, journal absent refusé)
$ python3 _infra/test_etat_service.py
OK : les quatre routes, refus 400/403/404, 500, schéma posé avant d'écouter
$ python3 controle/test_etat_partage.py
TOUT PASSE
$ python3 _tableau/test_garde_doublons.py / test_garde_plafond.py / test_garde_couches.py
ok / ok / ok ; python3 _tableau/garde_fous.py : « rien à redire » (socle.py = 481 lignes, < 600)

3. Le point risqué, la réparation d'une base ancienne. J'ai fabriqué une base avec l'etat_partage.sql de main (CHECK nom IN ('sorties', 'file_montage')), posé deux lignes, puis ouvert avec le code de la PR : les deux lignes sont gardées, choix-vignettes est ensuite accepté (l'ancienne contrainte l'aurait refusé), et la table choix_vignettes est 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_avant laissé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 de CHOIX_CHAMPS, tous textuels, longueurs dans les bornes.

5. La migration ne peut pas être un no-op silencieux. La garde de socle.choix_vignettes_bascule est dirname(__file__) != "/opt/studio" : _tableau/deploy-132.sh aplatit bien _tableau/*.py dans /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-vignettes dans _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 --essai sur 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, sur babcc421, ce qui revient au même commit de base.

Relecture du 2026-10-15 (tête `ac127118`, base `main`) — **Relu, conforme**, étiquette `relu` posé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 de `ac127118`. 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.** $ cargo test --release --locked # _tableau/rust 77 passed, 0 failed (18 binaires) ; cargo build release sans erreur $ npx vitest run # _tableau/front, tête de la PR Test Files 1 failed | 26 passed (27) Tests 1 failed | 198 passed (199) FAIL tests/stockage.test.js:82 (STUD-1238) Rejoué sur `babcc421` **nu** (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é de `main`** — la PR ne touche aucun fichier front. `npx vite build` : code 0. $ python3 _tableau/test_choix_vignettes_bascule.py 10 cas, TOUT PASSE (essai, écriture avant/après bascule, service muet, service qui refuse, .bak-sqlite, retour arrière puis re-bascule, journal absent refusé) $ python3 _infra/test_etat_service.py OK : les quatre routes, refus 400/403/404, 500, schéma posé avant d'écouter $ python3 controle/test_etat_partage.py TOUT PASSE $ python3 _tableau/test_garde_doublons.py / test_garde_plafond.py / test_garde_couches.py ok / ok / ok ; python3 _tableau/garde_fous.py : « rien à redire » (socle.py = 481 lignes, < 600) **3. Le point risqué, la réparation d'une base ancienne.** J'ai fabriqué une base avec l'`etat_partage.sql` **de `main`** (CHECK `nom IN ('sorties', 'file_montage')`), posé deux lignes, puis ouvert avec le code de la PR : les deux lignes sont gardées, `choix-vignettes` est ensuite accepté (l'ancienne contrainte l'aurait refusé), et la table `choix_vignettes` est 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_avant` laissé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 de `CHOIX_CHAMPS`, tous textuels, longueurs dans les bornes. **5. La migration ne peut pas être un no-op silencieux.** La garde de `socle.choix_vignettes_bascule` est `dirname(__file__) != "/opt/studio"` : `_tableau/deploy-132.sh` aplatit bien `_tableau/*.py` dans `/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-vignettes` dans `_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 `--essai` sur 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, sur `babcc421`, ce qui revient au même commit de base.
Collaborator

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é.

`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é.
hermes-bot force-pushed hermes/migration-choix-vignettes-sqlite from ac1271185c
Some checks failed
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (push) Successful in 41s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (push) Failing after 11s
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (pull_request) Successful in 33s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (pull_request) Failing after 11s
to c5a8a16aa1
All checks were successful
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (push) Successful in 2m5s
Tests du tableau (Rust + Svelte) / Front Svelte (_tableau/front) (pull_request) Successful in 1m45s
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (pull_request) Successful in 10m6s
Tests du tableau (Rust + Svelte) / Rust (_tableau/rust) (push) Successful in 10m6s
2026-10-15 22:18:56 +02:00
Compare
Author
Collaborator

Rebasé sur main et poussé — c5a8a16a. Le conflit venait d'un rebasage en retard : socle.py, etat_partage.py et etat_client.py avaient bougé sous la branche (instagram-file fusionnée en 3a985afb, 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-vignettes s'ajoute à sorties/file_montage/atelier (la fusion en avait laissé deux, la seconde masquait atelier — KeyError sur l'essai de bascule de l'atelier) ;
  • l'essai de bascule (--essai) : choix-vignettes traité comme atelier, le bloc qui relisait sorties.json est retiré ;
  • revenir : sorties/atelier passent par ecrire_json_etat (même écriture que la branche, un seul appel) ;
  • l'écriture du journal sort de socle.py vers _tableau/choix_vignettes_donnees.py : socle.py à 599 lignes (plafond 600, sans dérogation) ;
  • _tableau/LXC-132.md et _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).

**Rebasé sur `main` et poussé — `c5a8a16a`.** Le conflit venait d'un rebasage en retard : `socle.py`, `etat_partage.py` et `etat_client.py` avaient bougé sous la branche (`instagram-file` fusionnée en 3a985afb, 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-vignettes` s'ajoute à `sorties`/`file_montage`/`atelier` (la fusion en avait laissé deux, la seconde masquait `atelier` — `KeyError` sur l'essai de bascule de l'atelier) ; - l'essai de bascule (`--essai`) : `choix-vignettes` traité comme `atelier`, le bloc qui relisait `sorties.json` est retiré ; - `revenir` : `sorties`/`atelier` passent par `ecrire_json_etat` (même écriture que la branche, un seul appel) ; - l'écriture du journal sort de `socle.py` vers `_tableau/choix_vignettes_donnees.py` : `socle.py` à 599 lignes (plafond 600, sans dérogation) ; - `_tableau/LXC-132.md` et `_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).
hermes-fusion deleted branch hermes/migration-choix-vignettes-sqlite 2026-10-15 22:30:08 +02:00
Collaborator

Fusionnée automatiquement par fusion-auto (forge) : CI verte, sans conflit.

Fusionnée automatiquement par `fusion-auto` (forge) : CI verte, sans conflit.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
foXaCe/studio!1689
No description provided.