v3.5.0 — audit des zones jamais couvertes : ce que Lanterne dit vs ce qu'il fait - #1
v3.5.0 — audit des zones jamais couvertes : ce que Lanterne dit vs ce qu'il fait#1pymenvert wants to merge 24 commits into
Conversation
docs/TIERS.md affirme deux fois etre « inclus dans chaque archive de release » et « livre avec chaque archive ». Aucune des quatre archives de release.yml ne le contenait, ni LICENSE. Le correctif de la v3.4.1 avait ete pose sur ci.yml (artefact de l'onglet Actions, avec le commentaire « obligation LGPL (GStreamer) et point de decision GPL ») et jamais sur release.yml -- c'est-a-dire partout SAUF sur les fichiers que les gens telechargent reellement, puisque release.yml dit lui-meme servir la page « publique, sans compte GitHub, contrairement aux artefacts de l'onglet Actions ». Consequence concrete : la mention IJG, que la licence de jpeg-encoder EXIGE de faire accompagner le logiciel, ne partait avec aucun binaire publie. Et le pack le plus expose -- windows-x64-gstreamer, qui embarque les DLL LGPL et les plugins GPL de gst-plugins-ugly -- partait lui aussi sans rien. Les quatre archives embarquent desormais docs/TIERS.md et LICENSE. Les deux archives Windows recuperent au passage deploy/installer-windows.ps1, que le README presente comme l'installateur a profils alors qu'il n'etait livre nulle part. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
Premier lot de l'audit des zones jamais couvertes. Toutes ces corrections repondent a la meme question : ce que Lanterne DIT correspond-il a ce qu'il FAIT ? 1. LES BINAIRES LINUX ET PI NE LISENT AUCUNE VIDEO. `gstreamer` n'est pas une feature par defaut : linux-x64 et windows-x64 sont compiles sans, et raspberrypi-arm64 part en --no-default-features -- donc sans fenetre de sortie, sans MIDI et sans GStreamer. Cette archive ne peut RIEN projeter. Le manuel disait pourtant, pour Ubuntu comme pour le Pi, qu'il suffisait d'installer les paquets gstreamer1.0-* : Pym aurait suivi la procedure, branche le videoprojecteur et vu du noir. La carte Windows legere, elle, annoncait honnetement « sans lecture video » depuis toujours. Les cartes du manuel, la ligne de depannage et les notes de release disent maintenant ce que chaque archive sait faire, et comment obtenir la video (compiler avec --features gstreamer, mode KMS sur Pi OS Lite). 2. UN REGLAGE MORT DEPUIS TOUJOURS. `[paths] shaders` etait documente comme reglable, cree par les trois installeurs... et lu par personne : les shaders sont embarques par include_str!. Champ retire ; les installeurs creent `luts/` a la place, qui lui est bien reel. Un `shaders = …` reste dans un ancien node.toml est ignore sans bruit (pas de deny_unknown_fields), et node.toml.example precise que `luts/` n'est PAS deplacable. 3. REINSTALLER SOUS WINDOWS CASSAIT LE PACK VIDEO. `Copy-Item lib cible` cree cible\lib quand cible existe deja : des la deuxieme installation, les plugins GStreamer atterrissaient dans lib\lib\gstreamer-1.0, la ou le binaire ne regarde jamais, pendant que les DLL de la racine etaient bien mises a jour. Version melangee, panne difficile a comprendre. On copie desormais le CONTENU, apres avoir vide lib\ (sinon les plugins fantomes de l'ancienne version restaient). 4. UN PREFIXE RELATIF PRODUISAIT UNE UNITE SYSTEMD INVALIDE. systemd exige un chemin absolu ; mkdir, install et chown s'accommodent tres bien d'un chemin relatif. `./install.sh --prefix lanterne` se deroulait donc de bout en bout sans un mot, et seul `systemctl start` echouait, plus tard. Le prefixe est rendu absolu des l'analyse des options. Verifie en reel : « Prefixe relatif converti en chemin absolu : /…/lanterne ». 5. METTRE A JOUR LAISSAIT TOURNER L'ANCIEN BINAIRE. install.sh remplacait l'executable sans jamais toucher au service : le processus en cours gardait l'ancien inode, et le conseil final `systemctl start` ne faisait rien puisque le service tournait deja. La mise a jour semblait faite et ne l'etait pas. Le service actif est desormais redemarre explicitement. Le README decrivait par ailleurs un depot qui n'existe plus : dossier webui/ fantome, « GStreamer a venir », et quatre crates bien reelles absentes (render, gst, artnet, ndi). Verifie : 254 tests, clippy -D warnings, fmt, shellcheck. Installation a froid rejouee en reel (prefixe relatif, luts/ cree, node.toml genere). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
Deuxieme lot. Trois defauts qui se rejoignent : une fonction existe, elle
repond, mais l'operateur n'a aucun moyen de la trouver.
1. DES ADRESSES OSC QUI MARCHENT SANS ETRE PUBLIEES. Le namespace OSCQuery
est la SEULE source que Chataigne lit pour construire son interface. Il
annoncait 45 adresses et taisait /rate, /blending, /cue/go, /dmx/scene
et /dmx/chaser -- soit la vitesse de lecture, le fondu de bords, et la
TOTALITE du declenchement de spectacle (cues du sequenceur et scenes
lumieres). Toutes acceptees en entree OSC, aucune visible.
Verifie en reel sur un node : `/rate 0.5` en UDP fait bien passer la
lecture a 0,5x, alors que le module OSCQuery n'offre aucun curseur de
vitesse. Les cinq sont declarees.
2. UN RETOUR D'ETAT SANS DESTINATAIRE. event_to_osc emet /transport et
/media -- les deux voyants les plus elementaires d'un lecteur : ce qu'il
fait, ce qu'il a charge. Aucun des deux n'existait dans l'arbre :
Chataigne recevait les messages sans avoir de parametre ou les ranger.
Ajoutes en LECTURE SEULE (ACCESS 1), avec /playlist/position, via une
nouvelle feuille_lecture() -- leaf() ne savait produire que de
l'ecriture. /preset/loaded, /mapping/loaded et /sync/scheduled restent
volontairement absents : ce sont des notifications sans etat
correspondant dans NodeState, les publier imposerait une VALUE
perpetuellement vide qui se lirait « aucun preset ».
3. LE SELECTEUR DE MEDIAS LISAIT UN CHAMP QUI N'EXISTE PAS. /api/media
renvoie des MediaInfo { path, bytes } ; l'UI lisait `m.name` a trois
endroits, donc la chaine « undefined ». Le plus grave n'est pas la liste
de « undefined » affichee dans « Fichiers du parc » : c'est l'onglet
Sequences, ou l'action « Charger un media » enregistrait
{cmd:"load", path:"undefined"} dans la conduite. Rien ne le refusait --
« undefined » est un chemin relatif parfaitement licite -- et la panne
n'apparaissait que le soir, a l'heure de la cue. Le selecteur du parc
filtre en plus les sous-dossiers, que validate_upload_name refuse.
4. LES LUT ETAIENT PLAFONNEES A 2 Mo AU LIEU DE 64. lut_upload extrait le
corps avec `Bytes` : axum applique alors DefaultBodyLimit (2 Mio) tant
qu'on ne le releve pas, et aucun DefaultBodyLimit n'existait dans le
routeur. Le garde maison « 64 Mo max » etait donc du code mort, et une
LUT 64 points (~7 Mo, taille courante d'un pack d'etalonnage) repartait
en 413 sans corps JSON -- l'UI n'affichait qu'une erreur generique.
Contre-epreuve : le test echoue bien (413) quand on retire la couche.
Trois tests ajoutes (adresses pilotables, etat en lecture seule, plafond
de corps). 254 tests, clippy -D warnings, fmt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
`apt-get update` sortait en 100 sur les runners : E: The repository 'https://packages.microsoft.com/ubuntu/24.04/prod noble InRelease' is no longer signed. Sous `set -o pipefail`, le step echouait et emportait le job « check » entier -- fmt, clippy, tests, smoke -- alors qu'aucun de ces jobs n'a besoin du depot Microsoft : ils veulent libasound2-dev, GStreamer, shellcheck ou le linker ARM64, tous dans le depot Ubuntu. La panne ne vient pas du code et se reproduit sur main : c'est la signature d'un depot preinstalle sur l'image GitHub qui a expire de son cote. Les huit steps apt du depot retirent desormais les listes microsoft* avant `apt-get update`. On garde la strictitude : un vrai echec d'installation fait toujours tomber le job. Le job shellcheck gagne au passage le `apt-get update` qui lui manquait (il installait shellcheck sur les listes preinstallees). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
L'unite est ordonnee sur multi-user.target et ne recoit aucune variable d'affichage (DISPLAY, WAYLAND_DISPLAY, XDG_RUNTIME_DIR). Or le mode de sortie par DEFAUT est `fenetre`, et install.sh ne propose jamais le mode KMS. Un processus lance dans ce contexte n'a aucun serveur d'affichage a contacter. Le node ne plante pas pour autant -- il journalise « fenetre de sortie indisponible » et continue sans projeter (comportement verifie en reel sur une machine sans affichage). C'est justement ce qui rend la panne sournoise : le service est actif, l'UI repond, tout parait normal, et le videoprojecteur reste noir. L'en-tete promettait pourtant « un Pi branche a un videoprojecteur reprend son show sans intervention ». L'unite dit maintenant a quoi elle sert reellement -- pilotage seul, ou sortie DRM/KMS -- et porte le bloc a decommenter pour un kiosque en mode fenetre sur Pi OS Desktop, avec la marche a suivre pour trouver les bonnes valeurs. PAS active par defaut, et volontairement : basculer sur graphical.target empecherait le service de demarrer sur une machine sans bureau (Pi OS Lite), et l'UID comme le type de session dependent de l'installation. Cela demande un Pi reel -- ce que CLAUDE.md reserve deja au retour de Pym. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
Troisieme lot. La mesure de performance annoncait « une absence, pas un faux zero rassurant » -- elle ne tenait cette promesse que sur la moitie des chiffres. 1. `fps` RESTAIT UN ZERO DUR LA OU `rendu` DEVIENT null. Le drapeau mesure_disponible (= feature `render` compilee ET mode != KMS) ne protegeait QUE la cle `rendu`. La ligne juste en dessous publiait `fps` sans condition -- immediatement apres le commentaire qui explique pourtant que « des zeros se lisent comme tout va bien, une absence se lit comme une absence ». Or dans les deux cas ou le drapeau est faux -- mode KMS, et binaire sans fenetre, c'est-a-dire l'artefact officiel raspberrypi-arm64 -- personne ne peuple le compteur. Un run d'endurance de 4 h sur un Pi qui projette sans le moindre defaut ressortait donc « Sortie sans aucune image : 100 % du run ». Exactement le defaut corrige en sens inverse par 2cee3ea, et reintroduit par l'autre bout. `fps` suit desormais la meme regle que `rendu`, aux QUATRE endroits qui le publient (/api/system, ZIP de diagnostic, /api/outputs, evenement position du WebSocket). Le depouillement ignore alors ces points au lieu de les compter comme morts, et le badge img/s se masque au lieu d'afficher « 0 img/s ». 2. Ctrl+C N'ARRETAIT PAS LE HARNAIS D'ENDURANCE. `trap nettoyer EXIT INT TERM HUP` : un gestionnaire de signal qui ne se termine pas par `exit` rend la main au script, qui REPREND ou il en etait. Ctrl+C eteignait donc la charge et restaurait le mapping, puis la boucle continuait a interroger le node pendant les heures restantes en empilant des lignes dans le meme CSV -- et `nettoyer` repassait une seconde fois a la sortie. Le meme SIGHUP d'une session SSH coupee produisait cela sur un Pi distant. Reproduit sur un cas minimal (le nettoyage s'affiche, la boucle va jusqu'au bout, le nettoyage s'affiche encore), puis verifie corrige : sortie immediate, nettoyage une seule fois, code 130. 3. LE README DU HARNAIS DISAIT ENCORE QUE LE p95 ATTRAPE L'A-COUP. Le commit ba41112 avait corrigé cette idee partout (mesure.rs, UI, manuel, CHANGELOG) SAUF dans le seul document que l'operateur lit pour interpreter son CSV. Un figement de 200 ms par minute ne fait pas bouger le p95 d'une microseconde -- c'est max_us qui le voit. Les deux se lisent ensemble, et la colonne fps documente sa cellule vide. Corriges au passage, meme famille : la table JSON/OSC de command.rs se donne pour la reference du vocabulaire et ignorait 8 des 40 commandes (dont blackout_set, cue_go et dmx_scene, qui ONT une adresse OSC) -- completee et desormais tenue par un test qui compare la table a l'enum ; l'evenement DmxChaserDemande n'etait jamais miroite en retour OSC, si bien qu'une scene rappelee allumait le voyant et un chaser lance n'allumait rien ; trois cles [modules] (sequencer, sync, ndi) que rien ne lisait, commentees « phase 2 » et « phase 3 » pour des fonctions livrees en v2.0.0 ; la liste des ecrans, lue une seule fois au chargement de la page, qui laissait une tablette de regie afficher « aucun ecran detecte » apres le branchement du videoprojecteur ; docs/TIERS.md qui ouvrait sur « logiciel proprietaire » alors que LICENSE et Cargo.toml sont MIT ; et deploy/README qui annoncait en « phase 4 » l'OTA et le mot de passe UI, livres depuis deux versions. 256 tests, clippy -D warnings, fmt, shellcheck. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
Quatrieme lot : ce qui se passe quand une installation EXISTANTE change de version. Le scenario n'avait jamais ete audite. 1. L'OTA REMPLACAIT LE BINAIRE PAR UNE VARIANTE PLUS PAUVRE. Le choix de l'archive se faisait sur la seule plateforme, jamais sur la variante reellement installee. Sous Windows il visait toujours `toolbox-node-windows-x64.zip`, le binaire LEGER compile sans `gstreamer` -- alors que l'installation recommandee, celle que l'installateur pose et que la page Releases met en avant, est le pack `…-gstreamer.zip`. Un clic sur « Mettre a jour » depuis l'onglet Systeme retirait donc la lecture video a une machine de spectacle : les DLL GStreamer toujours la, plus aucun code pour s'en servir. Sur un Pi, pire : la seule facon de projeter est de compiler sur place (l'archive officielle est en --no-default-features), et l'OTA proposait precisement cette archive -- ni video, ni fenetre de sortie. Le node declare desormais ses capacites reelles au demarrage (lui seul connait ses features), et l'archive visee en decoule. Quand aucune archive publiee ne fait autant que le binaire en place -- un Linux ou un Pi compile sur place -- la mise a jour est REFUSEE avec la raison, au lieu de degrader l'installation. Sans declaration, on suppose le binaire le plus pauvre : jamais l'inverse. 2. « REVENIR A LA VERSION PRECEDENTE » ARRETAIT LE NODE SANS LE RELANCER, sous Windows. `appliquer()` pose un .bat qui attend, remplace et relance ; `revenir_en_arriere()` renommait les fichiers et rendait la main, puis le handler tuait le process. Or sous Windows il n'y a ni service ni Restart=always : le demarrage automatique n'est qu'un .bat du dossier Demarrage, execute a l'ouverture de session. Le node ne revenait donc pas, alors que le message promet « le node redemarre, reconnexion automatique » -- et c'est le bouton qu'on utilise justement quand une mise a jour se comporte mal EN PLEIN SPECTACLE. Meme mecanique que `appliquer()`, et le script est nettoye au demarrage suivant comme son homologue. 3. TROIS FICHIERS D'ETAT REJETAIENT LE PREMIER CHAMP AJOUTE. NodeConfig, Reglages et FeatureFlags portent leur filet `#[serde(default)]` ; EtatLumieres et OutputSettings ne l'avaient pas -- tous leurs champs etaient obligatoires. Le jour ou une version ajoute un champ, le `lumieres.json` du client part en `.corrompu` et la console repart vide : scenes et chasers du spectacle perdus, et ceux-la ne se reconstituent pas de memoire. 4. UNE CUE INCOMPRISE FAISAIT PERDRE TOUTE LA CONDUITE. Le filet `actions_tolerantes` existait pour les ACTIONS d'une cue, pas pour les cues elles-memes : `Declencheur` etant un enum etiquete sans repli, un declencheur inconnu -- conduite ecrite par une version plus recente, relue apres un retour arriere -- faisait echouer le fichier ENTIER. Meme patron applique aux cues : on ne perd plus que celle qu'on ne comprend pas. Cinq tests ajoutes, dont la table de correspondance archive/capacites pour Windows, Linux et Pi (logique rendue pure : testable pour toutes les plateformes depuis n'importe quelle machine). 261 tests, clippy -D warnings, fmt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
La section « Non publie » devient la 3.5.0 et couvre les DEUX chantiers : la mesure de performance (deja ecrite) et l'audit des zones jamais couvertes. Le manuel etant deja passe en v3.5, Cargo.toml devait suivre -- sinon l'UI affichait une version et le manuel une autre. Corrige au passage, meme famille que le reste de l'audit : LES REGLAGES DE PERFORMANCE ANNONCAIENT UNE RESOLUTION QU'ILS N'APPLIQUAIENT PAS. `config.resolution` n'est lu qu'a UN endroit du depot : la construction de la sortie DRM/KMS. En mode fenetre -- le defaut, et le seul que pose install.sh -- la fenetre rend a la taille de sa surface, et la valeur est purement decorative. Or l'installateur detecte un Pi 3, ecrit 960×540 et annonce « Reglages de performance pi3 ecrits », le journal confirmait « reglages appliques (largeur=960 hauteur=540 ) », et la carte Systeme proposait les deux champs sans reserve. Personne ne pouvait deviner que rien ne changeait. Plutot que de faire semblant : le journal ne mentionne la resolution que lorsqu'elle sert (mode KMS) et emet sinon un warn! explicite ; les deux champs de l'UI portent la mention « (KMS) » et l'infobulle correspondante ; le conseil Pi 3 de l'UI, celui de install.sh et celui du manuel disent « 960×540 en sortie sans bureau » au lieu de le promettre partout. Implementer vraiment la resolution de rendu en mode fenetre serait un changement du chemin de rendu, a valider sur un Pi : c'est note en limite assumee dans le CHANGELOG, pas fait en aveugle. Restent dits ailleurs : node.toml.example previent que [output] et [midi] sont sans effet sur l'archive ARM64 officielle, et deploy/README pointe enfin la page Releases (publique) plutot que les seuls artefacts de l'onglet Actions -- en disant ce que chaque archive sait faire. Le nombre de tests du README est desormais le chiffre reel (261), pas une estimation. 261 tests, clippy -D warnings, fmt, shellcheck, smoke. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
L'UI visait le node distant directement, en `mode: "no-cors"`. Depuis la v3.4.0, toute requete mutatrice dont l'Origin ne correspond pas au Host est refusee -- et le navigateur JOINT toujours Origin sur un POST inter-origines. La cible repondait donc 403, trace en WARN dans SON journal (que personne ne regarde). Comme la reponse d'un appel no-cors est opaque, le `.catch` ne se declenchait jamais : l'UI affichait « Mire coins envoyee a <node> » a tous les coups. Le geste sert justement a savoir quel videoprojecteur correspond a quelle machine -- il repondait toujours oui, sans rien faire. Relais serveur-a-serveur, sur le modele de `fleet_media` : la cible doit etre un node connu du parc, et c'est le jeton de parc -- jamais le mot de passe de l'UI -- qui authentifie. L'anti-CSRF n'est pas affaibli. `/api/identify` n'est VOLONTAIREMENT pas ajoute aux routes ouvertes par le jeton de parc : ce jeton est commun a toutes les machines et les cibles viennent d'un mDNS non authentifie, alors que la mire « coins » couvre la sortie pendant 4 secondes -- donc en plein spectacle. Identifier un node protege par mot de passe reste refuse, mais le message le DIT desormais, au lieu d'annoncer un succes ; et il ne conseille pas de poser un fleet_token, qui n'y changerait rien. Verifie en reel sur le binaire 3.5.0 : - OSCQuery publie 53 adresses contre 45 auparavant ; /rate, /blending, /cue/go, /dmx/scene, /dmx/chaser, /transport, /media et /playlist/position sont tous presents, /transport avec ACCESS 1. - En mode KMS, /api/system et /api/outputs renvoient bien `fps: null` (et non plus 0.0, qui faisait passer la sortie pour morte) en meme temps que `rendu: null`. - En mode fenetre avec un reglages.json pi3, le journal n'annonce plus « largeur=960 hauteur=540 appliques » mais un WARN « resolution de rendu IGNOREE : elle ne s'applique qu'a la sortie KMS » ; en mode KMS, aucun warn -- elle sert vraiment. 261 tests, clippy -D warnings, fmt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
Le manuel promet depuis la v1, section Effets : « une intensite 0..1 par effet — un fader MIDI ou OSC suffit a l'activer et le doser ». En OSC, oui. En MIDI, non : `ScaleTarget` ne comptait que 9 cibles (le volume et les 8 reglages couleur). Ni les cinq effets, ni la vitesse de lecture. Et il n'y avait pas de contournement : un binding CC sans `scale` ne peut renvoyer qu'une commande FIXE, donc une valeur CONSTANTE -- tourner le fader rejouait la meme valeur. Un operateur qui suivait le manuel ecrivait `scale = "pixelate"`... et le node REFUSAIT DE DEMARRER, sur une erreur de desserialisation d'un enum, pour une simple cible inconnue. Deux corrections, donc : 1. Six cibles ajoutees -- pixelate, posterize, noise, sharpen, mirror (bornees 0..1, la valeur du CC passe telle quelle) et rate (mise a l'echelle sur les bornes de SetRate, 0,25×–4×, pour que le bus n'ait aucune raison de refuser la commande a fond de course ; verifie par contre-epreuve en appliquant les deux extremes a l'etat). 2. `scale` recoit le meme filet que `command` : une cible mal orthographiee est ignoree avec un avertissement nommant la faute et la liste des cibles possibles, au lieu d'empecher le demarrage. C'est exactement la panne que la tolerance sur `command` avait ete ecrite pour eviter -- elle n'avait simplement jamais ete etendue au champ d'a cote. Les cibles sont enfin ecrites la ou on les cherche : node.toml.example et la section MIDI du manuel les listent toutes les quinze. 263 tests, clippy -D warnings, fmt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
1. LE CLIENT MJPEG ETAIT ANNONCE SANS JAMAIS ETRE VERIFIE. Les deux collecteurs lancaient curl en arriere-plan et n'examinaient jamais son sort. Or /flux.mjpg repond 404 si la fonction « Apercu » est coupee -- ce que le profil Pi 3 conseille explicitement --, 503 au-dela de 4 clients et 401 si un mot de passe est pose. Dans les trois cas curl rendait la main en quelques millisecondes, le compositeur partage n'etait pas sollicite de tout le run, et « Charge : ~N commandes/s + client MJPEG » s'affichait quand meme. Le run mesurait autre chose que ce qu'il annoncait. Les deux harnais sondent maintenant le flux avant de l'annoncer, et disent clairement « charge reduite » avec le code HTTP sinon. Sonde verifiee en reel sur les trois cas : 200 flux sain, 404 apercu coupe, 000 node injoignable. Premiere version ecrite avec un `|| echo 000` de repli : elle rendait « 200000 » sur un flux sain, un flux MJPEG ne se terminant jamais, curl sort toujours en timeout apres avoir deja ecrit le code. Corrige en `|| true`. 2. LE VERDICT MEMOIRE IGNORAIT LES REDEMARRAGES. Le bandeau annonce « tendances sans valeur » quand le node a redemarre, et les images perdues sont bien neutralisees -- mais quarante lignes plus bas, la section memoire calculait sa pente A TRAVERS les vies du process. Une RSS qui passe de 380 Mo a 90 Mo puis remonte a 120 donne une regression NEGATIVE : le script imprimait « VERDICT : stable. » sur un run traverse par un plantage, et par une fuite possible. Le verdict est desormais « non calculable » avec la raison. 3. LES SCRIPTS POWERSHELL N'ETAIENT VERIFIES NULLE PART. shellcheck ne couvre que les .sh ; l'installateur Windows et le collecteur d'endurance ne tournent jamais en CI. Une coquille de syntaxe n'y etait decouverte que par l'utilisateur, sur sa machine, au moment ou il en a besoin. Le job check-windows analyse maintenant tous les .ps1 du depot avec le parseur PowerShell et echoue en nommant les lignes fautives. (C'est ce controle qui manquait pour valider les modifications ci-dessus : elles ne sont pas executables ailleurs que sous Windows.) Au passage : la sonde PowerShell neutralise $PSNativeCommandUseErrorActionPreference le temps de l'appel. Le script tourne sous $ErrorActionPreference = "Stop" et, depuis PowerShell 7.4, un code de retour non nul d'une commande native est une erreur terminante -- la sonde aurait tue le run qu'elle prepare, curl sortant toujours en timeout sur un flux sans fin. 263 tests, clippy -D warnings, fmt, shellcheck. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
Le ZIP contenait la LISTE des noms de presets -- pas leur contenu -- et aucun des fichiers d'etat du node : ni lumieres.json (la console), ni sequences.json (la conduite du spectacle), ni fonctions.json, reglages.json ou demarrage.json. C'est pourtant le seul export que le produit propose, donc la seule sauvegarde qu'un operateur non developpeur puisse faire avant une mise a jour -- et le manuel annonce « une archive avec […] les presets ». Le scenario qui fait mal : coupure de courant pendant une ecriture, fichier tronque, `charger_ou_mettre_de_cote` met lumieres.json de cote en `.corrompu` au redemarrage, la console repart vide. L'archive exportee « au cas ou » ne permettait pas de revenir en arriere. Ce sont justement les fichiers qu'on ne reconstitue pas de memoire. L'archive porte desormais `presets/<nom>.json`, `presets/mapping/<nom>.json` et `etat/<fichier>.json`. Les fichiers d'etat sont lus BRUTS, pas re-serialises : si l'un est corrompu, c'est l'original qu'on veut examiner. Tout est en meilleur effort -- un preset illisible ne doit pas faire echouer l'export, c'est justement quand ca va mal qu'on le demande. Verifie en reel sur un node : apres un preset_save et un mapping_save, l'archive contient presets/soiree.json (1165 o) et presets/mapping/salon.json (376 o) ; avec lumieres.json, sequences.json et reglages.json sur le disque, elle contient les trois sous etat/, au contenu identique a l'original. Le manuel dit ce que l'archive contient vraiment, et qu'elle sert de sauvegarde avant mise a jour. 263 tests, clippy -D warnings, fmt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
…perdu La fenetre ne detenait qu'un Receiver sur les reglages de sortie : aucun Sender, donc aucune republication possible. F11 et Echap changeaient l'etat de plein ecran directement sur la fenetre winit, et personne ne le disait au reste du node. Consequences en chaine : - /api/outputs continuait d'annoncer l'ancienne valeur ; - la case « Plein ecran » de l'UI restait desynchronisee ; - et surtout, comme l'UI renvoie TOUJOURS les deux champs, le reglage suivant reappliquait le `fullscreen` perime. Mettre la sortie en plein ecran avec F11 sur la machine (le geste que l'infobulle de la carte « Sortie » recommande), puis changer d'ecran cible depuis une tablette, faisait sortir la sortie du plein ecran toute seule -- en plein spectacle, et sans que personne ne comprenne pourquoi. La fenetre recoit desormais le MEME canal en ecriture et publie son etat reel. Le piege etait dans le retour : republier declenche le relais, donc Wake::SettingsChanged, donc apply_settings -- dont la branche « fenetre » execute set_outer_position(). Sans precaution, la fenetre aurait saute a l'origine de l'ecran juste apres chaque Echap. La fenetre retient donc le dernier reglage qu'elle a REELLEMENT applique et ignore l'echo de ses propres publications ; apply_settings devient idempotent. La decision de publication est isolee dans une fonction pure (`publication_plein_ecran`) pour etre testable : le reste demande un event loop winit, donc un serveur graphique. Le test verifie qu'on publie bien dans les deux sens, que l'ecran cible n'est pas emporte au passage, et qu'on ne publie RIEN quand l'etat est deja le bon -- sinon chaque bascule reveillerait l'event loop pour rien et relancerait un cycle d'echo. 264 tests, clippy -D warnings, fmt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
`CommandeLumieres` compte 12 variantes, mais seules les scenes et les
chasers avaient un pont vers le bus. Le grand master et les niveaux de
fader n'existaient dans AUCUN point du vocabulaire `Command` : ils ne
passaient que par POST /api/dmx, donc uniquement depuis l'interface web.
Consequence : poser un fader de surface MIDI sur le grand master -- le
geste le plus canonique d'une console lumiere -- etait impossible. Un
binding CC sans `scale` ne peut envoyer qu'une valeur constante, et aucune
cible n'existait. Depuis Chataigne, meme impasse : pas d'adresse OSC. Et
une cue du sequenceur ne pouvait pas davantage poser un niveau.
La chaine complete est posee, dans l'ordre ou elle est traversee :
- `Command::DmxMaster { valeur }` et `Command::DmxFader { id, valeur }`
(ajout PUR : aucun format JSON existant n'est modifie), avec leurs deux
lignes dans la table de documentation -- que le test ajoute plus tot
dans cette branche exige desormais ;
- les evenements correspondants, et leur traduction en `CommandeLumieres`
dans l'abonne Art-Net (il consomme des Event, pas des Command) ;
- les adresses OSC `/dmx/master <0..255>` et `/dmx/fader <id> <0..255>`,
en entree comme en retour d'etat ;
- les deux feuilles OSCQuery, sans quoi Chataigne ne les verrait pas --
c'est le defaut que cette branche a deja corrige pour la regie ;
- la cible de fader MIDI `dmx_master` (0..127 → 0..255, et 127 donne bien
255, pas 254).
Le parseur OSC accepte un entier 0..255 (la convention de la console) ET
un flottant FRACTIONNAIRE entre 0 et 1, parce que beaucoup de surfaces OSC
envoient leurs faders normalises : sans cela, un 0.5 devenait 0, c'est-a-
dire un noir au lieu d'un demi-niveau. Hors bornes : refuse, jamais
tronque en silence.
Verifie en reel de bout en bout : `/dmx/master 77` envoye en UDP a un node
fait bien passer le master de 255 a 77 dans /api/dmx, et les deux adresses
apparaissent dans l'arbre OSCQuery.
264 tests, clippy -D warnings, fmt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
La 3.5.0 gagne une section « Ce qui devient pilotable » : corriger ce qui etait DIT a mis au jour des fonctions reellement injoignables (faders MIDI sur les effets et la vitesse, master et faders lumieres, /transport et /media en lecture seule dans OSCQuery, export diagnostic devenu une vraie sauvegarde). CLAUDE.md porte l'etat de la version, ses deux limites assumees (unite systemd qui ne projette pas en mode fenetre ; resolution de reglages.json limitee a la sortie KMS) et le point laisse a Pym sur la formulation de deny.toml -- la politique elle-meme n'a pas ete touchee, elle touche a la decision sur gst-plugins-ugly qu'on ne rouvre pas. Les prochaines etapes gagnent deux points precis pour le retour au materiel : valider le bloc DISPLAY/graphical.target du service systemd sur Pi OS Desktop, et decider si l'on publie une archive Linux/Pi AVEC GStreamer. Le chiffre « 45 a 55 adresses OSCQuery » est mesure sur un node reel, pas estime. 264 tests, clippy -D warnings, fmt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
Defaut introduit par le commit precedent, trouve en relisant mes propres correctifs -- exactement le genre que la v3.4.1 avait ete creee pour attraper. `octet_arg` distinguait « niveau DMX » et « fader normalise » sur `fract() != 0.0`. Or 1.0 n'a pas de partie fractionnaire : un fader normalise pousse A FOND repartait sur le chemin entier et donnait 1, pas 255. La discontinuite tombait donc precisement la ou personne ne la cherche : Float(0.5) -> 128 Float(0.999) -> 255 Float(1.0) -> 1 <-- quasi noir Mesure sur le parseur reel avant correction, pas deduite. C'est desormais le TYPE qui decide, jamais la valeur : un entier est un niveau DMX 0..255 (ce que declare OSCQuery, donc ce que Chataigne envoie), un flottant est un fader normalise 0..1 (la convention des surfaces OSC). Regle simple, sans cas particulier, et enoncee telle quelle dans le manuel. Le test couvre maintenant les extremes des deux conventions -- 0.0, 1.0, et les refus hors bornes dans les deux sens (Int(-1), Int(300), Float(1.5)) -- au lieu du seul 0.5 qui passait des deux facons. 264 tests, clippy -D warnings, fmt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
Suite de la relecture de mes propres correctifs. `publier_plein_ecran` lisait les reglages courants, modifiait sa copie, puis la publiait avec `send_replace` : une lecture-modification-ecriture NON atomique. Course possible, etroite mais reelle : F11 presse sur la machine pendant qu'un changement d'ecran cible arrive de la tablette. La copie lue avant le changement etait republiee apres, et ramenait la sortie sur l'ancien ecran -- le symptome meme que ce correctif etait cense faire disparaitre. `send_modify` fait la modification SOUS le verrou du canal : le champ `fullscreen` est le seul touche, les autres gardent la valeur qu'ils ont au moment de l'ecriture. Le marquage « deja applique » est pris APRES, depuis la valeur reellement retenue, pour que l'echo renvoye par le relais reste reconnu comme le notre. 264 tests, clippy -D warnings, fmt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
`l_enchainement_suit_le_nom_pas_l_index` a echoue sur check-windows (97213ec) apres avoir passe cinq executions de suite sur cette meme branche : aucun code en cause, une marge de temps trop courte. Le test dormait 400 ms pour un enchainement programme a 300 ms, soit 100 ms de marge -- sur un runner Windows charge, ou chaque enregistrement de cue passe par un fsync de sequences.json, la marge est parfois depassee. Il ATTEND desormais l'evenement au lieu de le parier : sonde toutes les 20 ms, jusqu'a 5 s, qu'une cue ENCHAINEE ait joue. Le detail qui compte : la condition porte sur « `derniere` n'est plus A », pas sur « une cue a joue » -- A est la cue lancee a la main, elle devient `derniere` immediatement, et une premiere version de ce correctif sortait donc de la boucle avant meme l'enchainement (le test l'a dit tout de suite : left: Some("A")). La garantie du test est intacte, et c'est verifie et non suppose : contre-epreuve en remettant volontairement l'enchainement par INDEX -- le test echoue bien, en nommant le coupable (left: Some("C")). Puis 20 executions consecutives du test corrige : 20 ok, 0 echec. Les deux assertions sont aussi inversees pour que la plus parlante tombe en premier : savoir QUELLE cue a joue explique la panne, un volume inattendu la fait seulement soupconner. 264 tests, clippy -D warnings, fmt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
Relecture adversariale des correctifs de cette branche -- la demarche meme qui avait justifie la v3.4.1. Quatre defauts confirmes DANS mes propres corrections, dont deux regressions que j'avais introduites. 1. LE MASTER POSE PAR OSC/MIDI/CUE N'ETAIT JAMAIS PERSISTE. Le commit f188a89 annoncait « la chaine complete est posee ». Elle s'arretait une ligne trop tot : le bras `commandes` (web UI) fait `if appliquer(...) { a_sauver = true }`, le bras `evenements` -- celui que j'avais etendu -- jetait la valeur de retour. Deux entrees vers la meme console, deux comportements : un master pose depuis Chataigne ou un fader MIDI vivait en memoire et disparaissait au redemarrage, pendant que le meme geste depuis la tablette survivait. Verifie en reel : `/dmx/master 77` en UDP, puis arret du node -> lumieres.json porte bien master 77, et le node relance le relit. 2. `/dmx/chaser ""` N'ARRETAIT AUCUN CHASER. Le retour d'etat emet la chaine vide pour un arret, « symetrique de /lut » disait le commentaire ; l'entree, elle, gardait `Some("")` et cherchait un chaser nomme « ». Or vider le champ est le SEUL geste d'arret possible depuis Chataigne, qui ne sait pas envoyer un message sans argument sur un parametre type « s ». La symetrie n'existait que dans un sens. 3. octet_arg REFUSAIT `90.0`. Ma correction precedente decidait sur le seul TYPE : entier = niveau DMX, flottant = fader normalise. Mais `int_arg`, dix lignes plus haut, documente que « Chataigne envoie volontiers 90.0 pour 90 » -- l'emetteur le plus courant du projet se voyait donc refuser ses messages. Troisieme version, et la bonne : un flottant FRACTIONNAIRE est normalise, un flottant a valeur entiere est un niveau DMX, et la seule ambiguite irreductible (1.0 : niveau 1, ou fader a fond ?) est tranchee en faveur du fader a fond -- un master a 1/255 ne se vise pas a la main, et reste atteignable en entier. Le choix est ecrit dans le code, pas laisse implicite. 4. LE FADER MIDI « rate » NE POUVAIT PAS RENDRE LA VITESSE NORMALE. Mise a l'echelle lineaire sur [0,25 ; 4] : 1x tombait a CC 25,4, une position qu'aucun controleur ne peut emettre. Les deux crans encadrants donnaient 0,988x et 1,018x -- assez pour faire deriver une boucle video d'une bande son, et sans retour possible depuis la surface. Echelle GEOMETRIQUE desormais : 0,25x / 0,5x / 1x / 2x / 4x aux quarts, comme se pense un fader de vitesse. Corrige aussi : la liste des cibles de fader affichee au demarrage oubliait `dmx_master`, ajoutee la veille -- elle est maintenant unique (ScaleTarget::NOMS) et tenue par un test, au lieu d'etre recopiee a la main ; et la regle « entier ou flottant » n'etait ecrite que dans le manuel : elle l'est aux quatre points de contrat (table de command.rs, deux feuilles OSCQuery, manuel). 265 tests, clippy -D warnings, fmt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
Deuxieme lot de la relecture adversariale de mes propres correctifs. 1. L'OTA ANNONCAIT « A JOUR » ALORS QU'UNE VERSION PLUS RECENTE EXISTAIT. En apprenant a refuser une mise a jour degradante, j'ai fait rendre `asset: None` dans des cas REELS (tout Linux/Pi compile avec GStreamer, tout aarch64 compile sur place). Or l'UI, que je n'avais pas touchee, teste `if (etat.plus_recente && etat.asset)` et tombe sinon dans la branche « A jour (vX) ». Le refus prudent se transformait donc en mensonge tranquille : le node savait qu'une v3.6.0 existait et affirmait etre a jour. `EtatMiseAJour` porte desormais une `raison`, et l'UI l'affiche : « v3.6.0 existe, mais : ce binaire a ete compile sur place… ». 2. LE MESSAGE DE REFUS PARLAIT DE LA VIDEO A QUI N'EN A PAS. Le meme texte servait aux trois cas de refus. Sur un Pi compile sur place SANS gstreamer -- le cas que decrit install.sh -- il annoncait « vous ferait perdre la lecture video » a quelqu'un qui n'en a jamais eu, et taisait ce qu'il perdrait vraiment : la fenetre de sortie, donc toute projection. Le message est construit depuis les capacites reelles. 3. L'EXPORT DIAGNOSTIC SAUTAIT LE PRESET QU'IL FALLAIT SAUVER. J'avais applique la lecture BRUTE aux cinq fichiers d'etat, et laisse les presets passer par `load()` -- donc par la validation ET une re-serialisation. Trois consequences : un preset au JSON abime etait silencieusement absent de l'archive (c'est-a-dire exactement celui qu'on exporte pour le recuperer), un preset refuse par la validation aussi, et tout champ ecrit par une version plus recente disparaissait a la re-serialisation. `PresetStore::octets` lit le fichier tel qu'il est. Verifie en reel : avec un `presets/casse.json` volontairement tronque, l'archive le contient desormais octet pour octet. 4. LE FILTRE D'ECHO DE LA FENETRE POUVAIT AVALER UN REGLAGE LEGITIME. `send_modify` avait supprime l'ecrasement DANS le canal, mais le marqueur « deja applique » recopiait ensuite le canal entier. Si un reglage venu de l'UI y etait deja sans que l'event loop l'ait traite (il peint une frame), un F11 le faisait passer pour notre propre echo : l'ecran cible choisi depuis la tablette n'etait jamais applique. Le marqueur part maintenant de ce que la fenetre a REELLEMENT applique. 5. Le rapport.txt EMBARQUE dans l'archive -- le premier fichier qu'on ouvre, et le seul qui voyage avec elle -- enumerait encore l'ancien contenu. Il annonce les dossiers presets/ et etat/, et dit que l'archive sert de sauvegarde avant mise a jour. 265 tests, clippy -D warnings, fmt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
`go_joue_et_enchaine` a echoue sur check-windows apres que `l_enchainement_suit_le_nom_pas_l_index` a echoue au run precedent : deux tests differents du MEME module, tour a tour, sans qu'aucun code soit en cause. J'avais corrige le premier en concluant qu'il n'y avait pas lieu de toucher aux autres. C'etait la mauvaise lecture : ce n'est pas UN test fragile, c'est une famille -- les tests du sequenceur dorment une duree fixe pour attendre un enchainement dont l'echeance est du meme ordre, sur un service qui fsync sequences.json a chaque enregistrement de cue. Une aide commune `attendre(etat, condition)` remplace les paris : elle sonde l'etat publie toutes les 20 ms, jusqu'a 5 s. Les trois attentes des deux tests concernes y passent. Le cas interessant est le dernier bloc de `go_joue_et_enchaine`, qui affirme qu'il ne se passe RIEN apres un Stop. Une attente ne peut pas y remplacer le delai -- mais elle garantit l'etat de DEPART, et c'est la que le bat blessait : dormir 100 ms en esperant que l'enchainement soit arme, c'etait risquer d'envoyer Stop avant qu'il y ait quoi que ce soit a annuler. Le test aurait alors reussi sans rien prouver, ce qui est pire qu'un echec. Il attend maintenant que `en_attente` soit rempli, l'affirme, puis annule et verifie l'annulation avant de laisser passer l'echeance. Verifie : 25 executions consecutives de tous les tests du sequenceur, puis 4 suites en parallele pour reproduire la charge d'un runner -- 0 echec. Les autres `sleep` fixes du depot (player, bascules, sync, http) sont courts et sans echeance concurrente ; ils restent en l'etat plutot que d'etre reecrits en aveugle. 265 tests, clippy -D warnings, fmt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
`build-windows-gstreamer` echoue depuis le 2026-08-05 sur « dossier GStreamer : INTROUVABLE ». La cause est ailleurs, et elle accuse le mauvais coupable : Unable to find package 'pkgconfiglite'. Chocolatey installed 0/0 packages. `pkgconfiglite` a disparu du depot communautaire. Or chocolatey traite une commande comme UNE transaction : le paquet introuvable a annule les deux autres, et GStreamer -- parfaitement disponible, lui -- n'a jamais ete installe. Le message d'erreur pointait donc GStreamer alors que seul un utilitaire annexe manquait. Les deux workflows installent desormais les paquets separement, et le message dit ce qui manque vraiment. GStreamer sera bien installe ; il restera a trouver un pkg-config si le paquet ne revient pas. CE N'EST PAS UNE PANNE DE CETTE BRANCHE : elle se reproduit sur main, et le job est en continue-on-error, donc la PR n'est pas bloquee. Mais elle merite d'etre signalee : ce job produit `toolbox-node-windows-x64-gstreamer`, c'est-a-dire le SEUL binaire publie qui lit des videos. Tant que pkg-config manque au runner, le pack video ne peut pas etre reconstruit -- point a trancher avec Pym (paquet de remplacement, ou pkg-config telecharge directement). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
|
Point à trancher : le pack vidéo Windows ne peut plus être construit — cause externe, pas cette branche. Le job
Ce n'est pas une régression de cette PR : la même commande est sur Ce que j'ai fait : séparer les installations dans Ce que je n'ai pas fait, et pourquoi : trouver un remplaçant à Pourquoi ça compte : ce job produit Generated by Claude Code |
…rriger
Troisieme et dernier lot de la relecture adversariale de mes correctifs.
CE QUI NE MARCHAIT PAS, DANS MES PROPRES CORRECTIFS :
1. LE SELECTEUR DE MEDIA DES CUES RESTAIT VIDE. J'avais corrige la valeur
qu'il ecrit (m.path au lieu de m.name) sans voir qu'il n'etait rempli
qu'a l'evaluation du script -- donc AVANT l'arrivee de la mediatheque.
Et « Charger un media » est le type d'action par DEFAUT : en ouvrant
l'onglet Sequences on trouvait un menu vide, et il fallait changer de
type puis revenir pour le peupler. Le correctif etait juste et
inoperant. `loadMedia` rappelle desormais `majChampsAction`.
2. LE FILTRE DU PARC N'APPLIQUAIT QU'UNE DES DEUX REGLES du serveur.
`validate_upload_name` exige un nom plat ET un jeu de caracteres
restreint : « ete.mp4 » avec un accent restait propose, pour etre
refuse ensuite par un message technique. Le filtre est maintenant le
miroir de la regle, et le cas « aucun media eligible » est dit au lieu
d'un menu vide sans explication.
3. LE SONDAGE DES ECRANS RECONSTRUISAIT LE MENU TOUTES LES 5 SECONDES.
En ajoutant le rafraichissement (pour voir un videoprojecteur branche
a chaud) j'ai rendu la carte « Sortie » penible a la souris : le menu
se refermait sous le doigt. Il n'est reconstruit que si la liste a
VRAIMENT change, et la selection n'est pas ecrasee quand l'operateur a
le champ sous le curseur.
4. « RESOLUTION IGNOREE » REGARDAIT LE MODE, PAS LA FEATURE.
`config.resolution` n'est lu que par la sortie KMS, elle-meme sous
`#[cfg(feature = "gstreamer")]`. Un binaire sans GStreamer configure en
mode « kms » annoncait donc la resolution comme appliquee alors que
rien ne la lit. Le terme manquant est ajoute.
LA DOC REDISAIT CE QU'ON VENAIT DE CORRIGER -- le pire resultat possible
pour une branche dont le sujet est justement la derive doc/realite :
5. La ligne /dmx/master du manuel affirmait encore, EN GRAS, la regle
« c'est le type qui decide » -- celle que le commit precedent avait
supprimee comme un defaut. Je n'avais reecrit que la ligne /dmx/fader.
6. La carte « Ubuntu / Debian » avait perdu les paquets d'EXECUTION en
gagnant ceux de compilation : quelqu'un qui recompile avec
--features gstreamer se serait retrouve sans decodeurs, donc toujours
sans video. Les deux temps sont retablis.
7. L'arret d'un chaser par chaine vide -- implemente la veille -- n'etait
documente nulle part, ni dans le manuel ni dans OSCQuery.
8. La liste des cibles de fader du manuel oubliait `dmx_master` : la faute
EXACTE que le commit precedent pretendait avoir eradiquee, sur une
troisieme copie manuscrite que je n'avais pas vue.
9. Trois documents annoncaient trois nombres de tests differents, dont
aucun n'etait le bon. Ils annoncent le chiffre mesure : 267.
10. Le doc-comment de ScaleTarget::Rate decrivait encore l'echelle
lineaire remplacee par le meme commit.
Deux tests ajoutes la ou RIEN ne retenait le correctif : `fps` null quand
la mesure n'existe pas (contre-epreuve faite : le test echoue bien si on
remet le zero dur), et le refus d'un node inconnu du parc par le relais
d'identification.
267 tests, clippy -D warnings, fmt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
Fin de la relecture adversariale. Trois defauts restaient ouverts ; l'un d'eux etait dans le correctif que je venais d'ecrire. 1. LE SELECTEUR DE CUE AURAIT EFFACE LA SELECTION DE L'OPERATEUR. Mon correctif precedent faisait rappeler `majChampsAction` par `loadMedia`, pour que le menu « Charger un media » ne soit plus vide. Mais loadMedia est SONDE toutes les 5 s tant que la page Medias est visible : le menu aurait ete reconstruit sous le doigt, et le media deja choisi dans l'onglet Sequences remplace en silence par le premier de la liste -- l'operateur aurait enregistre une cue qui ne charge pas ce qu'il croit. La selection est desormais preservee (meme precaution que renderParcMedia), pour les medias comme pour les presets, et le rappel est limite au type d'action concerne (sinon un GET /api/dmx toutes les 5 s pour rien). 2. UN TEST QUI ASSURAIT UNE PROPRIETE JETEE A L'EXECUTION. `publication_plein_ecran` construisait un OutputSettings dont la production ne gardait plus que `.is_none()` : l'assertion centrale du test -- « l'ecran cible ne doit pas bouger » -- ne protegeait donc plus rien. Une refonte aurait pu ramener le defaut d'origine avec une suite verte. La fonction rend maintenant la valeur que la production RETIENT vraiment, et prend les deux entrees qui comptent : le canal decide s'il y a quelque chose a dire, ce que la fenetre a applique sert de base. Un test couvre le cas qui compte -- un reglage de l'UI encore en attente ne doit pas etre absorbe par un F11. 3. UNE CUE INCOMPRISE DISPARAISSAIT DEFINITIVEMENT. En rendant la lecture tolerante, j'avais supprime sans le voir le filet qui existait : avant, un fichier illisible partait en `.corrompu` et restait recuperable EN ENTIER. Maintenant qu'on le lit avec succes, la premiere reecriture de la conduite l'amputait pour de bon. Le cas vise est precisement le retour arriere apres une mise a jour, ou les cues perdues sont parfaitement valides pour la version qui les a ecrites. `sequences.json.incomplet` conserve l'original quand au moins une cue a ete ecartee, avec un WARN qui le dit. Deux tests : la copie porte bien la conduite entiere, et une conduite saine n'en laisse aucune. 269 tests, clippy -D warnings, fmt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2sFzNwRT8Juwd8Ai1LDZ
|
Correction du commentaire précédent : le pack vidéo se reconstruit bien. J'y écrivais que « le pack reste non constructible » tant que Run 30982052964 : Ce qui s'est passé : Chocolatey traite une commande comme une transaction, donc Il n'y a donc rien à trancher de ce côté : la release peut produire son pack vidéo. Le seul point qui demandait une décision dans ce fil est résolu ; je laisse le commentaire d'origine pour la trace du diagnostic. Generated by Claude Code |
La feuille de route restante étant bloquée sur le matériel (Pi, capture,
multi-machine) et le code ne contenant plus aucun TODO, cette passe porte sur
les zones qu'aucun des audits précédents n'avait couvertes : la cohérence entre
ce que Lanterne dit et ce qu'il fait.
Méthode : audit multi-agents sur 6 dimensions jamais explorées, chaque lot
repassé par une relecture adversariale — puis une seconde passe adversariale
sur mes propres correctifs, la démarche qui avait justifié la v3.4.1. Plus des
vérifications empiriques sur un vrai node à chaque fois que c'était possible.
Base de départ : 251 tests. Aujourd'hui : 269 tests, clippy
-D warnings,fmt, shellcheck, smoke — CI verte sur les dix jobs. La version passe en
3.5.0 (le manuel était resté en v3.3, le README en v3.0.0).
Les défauts les plus graves
L'archive Raspberry Pi officielle ne projette rien. Compilée
--no-default-features: ni fenêtre de sortie, ni MIDI, ni GStreamer. Le manuelinvitait pourtant, pour le Pi comme pour Ubuntu, à installer des paquets
gstreamer1.0-*et à brancher le vidéoprojecteur. Sur la cible principale duproduit, quelqu'un aurait suivi la procédure et vu du noir.
Aucune archive publique ne portait les mentions légales.
docs/TIERS.mdaffirme deux fois être « inclus dans chaque archive de release » ; le correctif
de la v3.4.1 n'avait été posé que sur
ci.yml, jamais surrelease.yml—partout sauf sur les fichiers que les gens téléchargent.
Mettre à jour retirait des fonctions. L'OTA choisissait l'archive sur la
seule plateforme : une machine Windows installée avec le pack vidéo se voyait
proposer le binaire léger. Le node déclare désormais ses capacités et refuse
la mise à jour quand aucune archive ne fait autant que lui.
Un Pi qui projette parfaitement était déclaré « sortie morte » (
fpsrestaitun zéro dur là où
rendudevenaitnull). La conduite pouvait enregistrerload "undefined", découvert le soir à l'heure de la cue. F11 puis unréglage depuis la tablette faisait perdre le plein écran. L'export
diagnostic ne sauvegardait rien : la liste des noms de presets, pas leur
contenu, et aucun fichier d'état.
Des fonctions qui existaient sans être joignables
/rate,/blending,/cue/go,/dmx/scene,/dmx/chasermarchaient en OSC sans être publiéesdans OSCQuery. L'arbre publie maintenant 55 adresses contre 45.
le manuel le promet depuis la v1 — et
scale = "pixelate"empêchait le nodede démarrer.
complète ajoutée (
dmx_master/dmx_fader, OSC, OSCQuery, cible MIDI).La relecture de mes propres correctifs
Elle a trouvé quatorze défauts dans les corrections ci-dessus, dont
plusieurs régressions que j'avais introduites :
0.999→ 255mais
1.0→ 1). Corrigé, puis re-corrigé : la deuxième version refusait90.0, que Chataigne envoie pour 90 — ce que ce fichier documente lui-mêmedix lignes plus haut.
s'arrêtait une ligne trop tôt, et le même geste depuis la tablette survivait.
inopérant. Puis mon correctif du correctif aurait effacé la sélection de
l'opérateur toutes les 5 s.
abîmé — celui qu'on exporte pour le récupérer — était le seul absent.
tombait entre deux crans). Échelle géométrique désormais.
tolérante j'avais supprimé le filet
.corrompusans le voir.pour une branche dont le sujet est la dérive doc/réalité.
Deux tests instables sur Windows aussi : ceux du séquenceur pariaient sur le
temps au lieu de l'attendre. Vérifié par 25 exécutions consécutives puis
4 suites en parallèle.
Ce qui demande une décision
deny.tomljustifie sa politique par « Lanterne est destiné à être VENDU »et parle de « distribution propriétaire », alors que
LICENSEetCargo.tomlsont MIT. La politique (refuser le GPL dans le binaire) reste juste ; la
formulation touche à la décision sur
gst-plugins-uglyqueCLAUDE.mddemandede ne pas rouvrir — non touchée.
docs/TIERS.md, lui, disait « logicielpropriétaire » en citant le
LICENSEqui dit MIT : corrigé.(Le pack vidéo Windows a cessé d'être constructible en cours de route —
pkgconfiglitedisparu de Chocolatey, qui annulait toute la transaction. Régléen séparant les installations : le pack se reconstruit, voir
la correction.)
Limites assumées (écrites dans le CHANGELOG)
dans l'unité, avec le bloc à décommenter — l'activer par défaut casserait les
installations sans bureau, et cela demande un Pi réel.
faire semblant, le journal et l'UI le disent.
DISPLAY/graphical.targetsur Pi OS Desktop, et décider si l'on publie unearchive Linux/Pi avec GStreamer.