<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">
    <title>storagebits · #devlog</title>
    <subtitle>Firmware, applis TV, un homelab, construits la nuit avec une organisation d&#39;agents IA. Et blok, le carnet d&#39;atelier.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://blok.storagebits.workers.dev/fr/tags/devlog/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://blok.storagebits.workers.dev"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-10-18T18:05:17-04:00</updated>
    <id>https://blok.storagebits.workers.dev/fr/tags/devlog/atom.xml</id>
    <entry xml:lang="fr">
        <title>Test de la NUCLEO-F412ZG avec Hubris et Humility</title>
        <published>2026-10-18T18:05:17-04:00</published>
        <updated>2026-10-18T18:05:17-04:00</updated>
        <author><name>François</name></author>
        <link rel="alternate" type="text/html" href="https://blok.storagebits.workers.dev/fr/blog/hubris-f412/"/>
        <id>https://blok.storagebits.workers.dev/fr/blog/hubris-f412/</id>
        <summary type="html">Porter Hubris, l&#39;OS embarqué d&#39;Oxide Computer, sur une NUCLEO-F412ZG à 100 MHz : ce qui a été touché, du kernel intact aux deux correctifs de Humility, expliqués.</summary>
        <content type="html" xml:base="https://blok.storagebits.workers.dev/fr/blog/hubris-f412/">&lt;p&gt;Le sujet : une NUCLEO-F412ZG, une carte de développement ST à Cortex-M4, et &lt;a class=&quot;external&quot; rel=&quot;external&quot; href=&quot;https://github.com/oxidecomputer/hubris&quot;&gt;Hubris&lt;/a&gt;, l’OS embarqué d’Oxide Computer, qui ne la supporte pas de base. Le kernel n’a pas bougé d’une ligne. Il a fallu ajouter la carte, adapter trois drivers, mettre à jour le firmware de la sonde et corriger deux fois Humility, le debugger.&lt;/p&gt;
&lt;figure class=&quot;portrait&quot;&gt;&lt;img src=&quot;nucleo-f412zg.webp&quot; alt=&quot;La carte NUCLEO-F412ZG posée sur un tapis noir, branchée en USB, avec ses LEDs utilisateur verte, bleue et rouge allumées.&quot; width=&quot;1125&quot; height=&quot;1500&quot; loading=&quot;lazy&quot;&gt;&lt;figcaption&gt;La NUCLEO-F412ZG, branchée par l&#39;USB du ST-LINK, la sonde intégrée en bas de la carte. Au-dessus, les trois LEDs utilisateur que pilote la tâche pong.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&quot;hubris-humility-c-est-quoi-c-est-qui&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hubris-humility-c-est-quoi-c-est-qui&quot; aria-label=&quot;Anchor link for: hubris-humility-c-est-quoi-c-est-qui&quot;&gt;#&lt;/a&gt;
Hubris, Humility, c’est quoi ? C’est qui ?&lt;/h2&gt;
&lt;p&gt;Hubris est un petit OS en Rust qu’Oxide utilise dans ses serveurs pour le contrôleur de service. Chaque tâche vit dans ses propres régions mémoire, protégées par la MPU. Les tâches, leurs priorités et leurs droits sur les périphériques sont tous déclarés à la compilation, dans un &lt;code&gt;app.toml&lt;/code&gt;. Humility lit la mémoire de la carte à travers la sonde et la traduit grâce aux infos de debug : état des tâches, piles, registres, dumps.&lt;/p&gt;
&lt;p&gt;L’auteur principal, c’est Cliff Biffle. Sa présentation à l’Open Source Firmware Conference 2021 explique pourquoi Oxide a écrit son propre OS. Ce talk est vraiment incroyable, quelle belle pièce d’ingénierie !&lt;/p&gt;
&lt;figure class=&quot;video video-externe video-large&quot;&gt;&lt;iframe src=&quot;https://player.vimeo.com/video/650913348?dnt=1&quot; title=&quot;On Hubris and Humility, Cliff L. Biffle, OSFC 2021&quot; loading=&quot;lazy&quot; allow=&quot;fullscreen; picture-in-picture&quot; allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption&gt;« On Hubris and Humility: developing an OS for robustness in Rust », Cliff L. Biffle, Open Source Firmware Conference 2021, 43 min.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;On peut aussi citer Bryan Cantrill, cofondateur et CTO d’Oxide, et Adam Leventhal, parmi les grands manitous derrière Hubris. Dans sa présentation sur le debug de Docker en production, à la DockerCon 2015, Cantrill défend une idée simple : un système planté ne se redémarre pas avant d’avoir été débogué, en direct ou d’après une image de sa mémoire, le &lt;em&gt;core dump&lt;/em&gt;. Humility fait les deux : &lt;code&gt;humility tasks&lt;/code&gt; lit la carte en marche, &lt;code&gt;humility dump&lt;/code&gt; sauve son état complet dans un fichier qu’on examine ensuite carte débranchée.&lt;/p&gt;
&lt;figure class=&quot;video video-externe video-large&quot;&gt;&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/AdMqCUhvRz8&quot; title=&quot;Running Aground: Debugging Docker in Production, Bryan Cantrill, DockerCon 2015&quot; loading=&quot;lazy&quot; allow=&quot;fullscreen; picture-in-picture&quot; allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption&gt;« Running Aground: Debugging Docker in Production », Bryan Cantrill, DockerCon 2015. Docker a retiré la vidéo d&#39;origine en 2018 ; c&#39;est la copie republiée par Cantrill.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&quot;ce-qu-il-a-fallu-patcher&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#ce-qu-il-a-fallu-patcher&quot; aria-label=&quot;Anchor link for: ce-qu-il-a-fallu-patcher&quot;&gt;#&lt;/a&gt;
Ce qu’il a fallu patcher&lt;/h2&gt;
&lt;p&gt;Je colle un prompt de mission à Claude : faire tourner Hubris sur cette carte, branchée en USB sur le Mac. Sa cible la plus proche dans le dépôt est une carte Discovery à STM32F407. Le soir même, Hubris tourne à 100 MHz sur la NUCLEO.&lt;/p&gt;
&lt;p&gt;Ce qui a été patché, couche par couche :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Le kernel&lt;/strong&gt; : rien, pas une ligne.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La carte et l’app&lt;/strong&gt; : des ajouts purs. Un fichier de carte de 2 lignes (le nom de la puce pour probe-rs), une description de la puce (1 Mio de flash, 256 Kio de RAM, adresses des périphériques), et l’app elle-même, avec la configuration d’horloge à 100 MHz. La branche ajoute 621 lignes, documentation comprise.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trois drivers partagés&lt;/strong&gt;, environ 150 lignes : &lt;code&gt;stm32fx-rcc&lt;/code&gt; choisit la variante F412 de la bibliothèque de registres, &lt;code&gt;user-leds&lt;/code&gt; pilote les LEDs de la Nucleo sur PB0, PB7 et PB14, et &lt;code&gt;stm32fx-usart&lt;/code&gt; passe sur USART3 (PD8/PD9), le port série virtuel du ST-LINK, cadencé à 50 MHz. Le point délicat : deux variantes de puce du crate &lt;code&gt;stm32f4&lt;/code&gt; ne peuvent pas cohabiter dans une même tâche, et deux de ces drivers forçaient la F407 dans leur &lt;code&gt;Cargo.toml&lt;/code&gt;. Ce choix est passé dans une feature. La Discovery compile à l’identique, revérifié à chaque étape.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La sonde&lt;/strong&gt; : le firmware du ST-LINK, trop ancien, a dû être mis à jour.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Humility&lt;/strong&gt;, le debugger : deux correctifs, détaillés plus bas. Le premier répare Humility face au Hubris le plus récent, quelle que soit la carte. Le second corrige une mesure fausse qu’on voit sur cette carte.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;la-mission-en-tranches&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#la-mission-en-tranches&quot; aria-label=&quot;Anchor link for: la-mission-en-tranches&quot;&gt;#&lt;/a&gt;
La mission, en tranches&lt;/h2&gt;
&lt;p&gt;Le north star du prompt tient en une phrase : « &lt;code&gt;humility tasks&lt;/code&gt; montre toutes les tâches &lt;code&gt;Healthy&lt;/code&gt; sur la NUCLEO-F412ZG, les LEDs utilisateur clignotent sous le contrôle de &lt;code&gt;pong&lt;/code&gt;, et &lt;code&gt;ping&lt;/code&gt; écrit sur le port COM virtuel du ST-LINK. » Cinq tranches, chacune doit finir sur du matériel qui marche : préflight, build de référence, premier allumage sur l’oscillateur interne à 16 MHz, UART, puis 100 MHz.&lt;/p&gt;
&lt;p&gt;Premier arrêt dès la tranche 0. Le firmware du ST-LINK de la carte est en V2J25 ; probe-rs, sur lequel s’appuie Humility, exige J26. Le prompt disait de s’arrêter et de demander. Claude propose de patcher automatiquement, je lui donne le zip téléchargé du site de ST et c’est fait.&lt;/p&gt;
&lt;p&gt;Toutes les tâches sont &lt;code&gt;Healthy&lt;/code&gt;. Claude ne voit pas les LEDs : il lit le registre de sortie de GPIOB plusieurs fois de suite et le voit alterner entre PB14, PB0 et rien, au rythme de &lt;code&gt;pong&lt;/code&gt;. La carte tourne à 100 MHz. Sur 10 secondes, l’horloge de Hubris compte 10 235 ms pour 10,32 s au mur, l’écart étant la latence de connexion de Humility.&lt;/p&gt;
&lt;figure class=&quot;sys sys-horloge&quot;&gt;&lt;div class=&quot;defile&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Arbre d&amp;#x27;horloge de la NUCLEO-F412ZG à 100 MHz, défilable horizontalement&quot;&gt;&lt;svg viewBox=&quot;0 0 760 300&quot; role=&quot;img&quot; aria-labelledby=&quot;horloge-t horloge-d&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;title id=&quot;horloge-t&quot;&gt;Arbre d&amp;#x27;horloge de la NUCLEO-F412ZG à 100 MHz&lt;/title&gt;&lt;desc id=&quot;horloge-d&quot;&gt;Le MCO du ST-LINK fournit 8 MHz en entrée HSE bypass. PLLM 4 donne 2 MHz, PLLN 100 donne un VCO à 200 MHz, PLLP 2 donne SYSCLK à 100 MHz. AHB à 100 MHz, APB1 à 50 MHz pour l&amp;#x27;USART3, APB2 à 100 MHz.&lt;/desc&gt;&lt;defs&gt;&lt;marker id=&quot;hl-fl&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;7&quot; markerHeight=&quot;7&quot; orient=&quot;auto-start-reverse&quot;&gt;&lt;path class=&quot;sys-pointe&quot; d=&quot;M0,0 L10,5 L0,10 z&quot;/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;rect class=&quot;sys-boite&quot; x=&quot;10&quot; y=&quot;40&quot; width=&quot;118&quot; height=&quot;74&quot; rx=&quot;6&quot;/&gt;&lt;text x=&quot;69&quot; y=&quot;60&quot; class=&quot;sys-titre&quot; text-anchor=&quot;middle&quot;&gt;ST-LINK MCO&lt;/text&gt;&lt;text x=&quot;69&quot; y=&quot;84&quot; class=&quot;sys-valeur&quot; text-anchor=&quot;middle&quot;&gt;8 MHz&lt;/text&gt;&lt;text x=&quot;69&quot; y=&quot;103&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;carré, pas un quartz&lt;/text&gt;&lt;rect class=&quot;sys-boite&quot; x=&quot;158&quot; y=&quot;40&quot; width=&quot;118&quot; height=&quot;74&quot; rx=&quot;6&quot;/&gt;&lt;text x=&quot;217&quot; y=&quot;60&quot; class=&quot;sys-titre&quot; text-anchor=&quot;middle&quot;&gt;HSE bypass&lt;/text&gt;&lt;text x=&quot;217&quot; y=&quot;84&quot; class=&quot;sys-valeur&quot; text-anchor=&quot;middle&quot;&gt;8 MHz&lt;/text&gt;&lt;text x=&quot;217&quot; y=&quot;103&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;HSEBYP avant HSEON&lt;/text&gt;&lt;rect class=&quot;sys-boite&quot; x=&quot;306&quot; y=&quot;40&quot; width=&quot;118&quot; height=&quot;74&quot; rx=&quot;6&quot;/&gt;&lt;text x=&quot;365&quot; y=&quot;60&quot; class=&quot;sys-titre&quot; text-anchor=&quot;middle&quot;&gt;PLLM = 4&lt;/text&gt;&lt;text x=&quot;365&quot; y=&quot;84&quot; class=&quot;sys-valeur&quot; text-anchor=&quot;middle&quot;&gt;2 MHz&lt;/text&gt;&lt;text x=&quot;365&quot; y=&quot;103&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;entrée VCO 1–2 MHz&lt;/text&gt;&lt;rect class=&quot;sys-boite&quot; x=&quot;454&quot; y=&quot;40&quot; width=&quot;118&quot; height=&quot;74&quot; rx=&quot;6&quot;/&gt;&lt;text x=&quot;513&quot; y=&quot;60&quot; class=&quot;sys-titre&quot; text-anchor=&quot;middle&quot;&gt;PLLN = 100&lt;/text&gt;&lt;text x=&quot;513&quot; y=&quot;84&quot; class=&quot;sys-valeur&quot; text-anchor=&quot;middle&quot;&gt;200 MHz&lt;/text&gt;&lt;text x=&quot;513&quot; y=&quot;103&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;VCO 100–432 MHz&lt;/text&gt;&lt;rect class=&quot;sys-boite&quot; x=&quot;602&quot; y=&quot;40&quot; width=&quot;148&quot; height=&quot;74&quot; rx=&quot;6&quot;/&gt;&lt;text x=&quot;676&quot; y=&quot;60&quot; class=&quot;sys-titre&quot; text-anchor=&quot;middle&quot;&gt;PLLP = 2&lt;/text&gt;&lt;text x=&quot;676&quot; y=&quot;84&quot; class=&quot;sys-valeur&quot; text-anchor=&quot;middle&quot;&gt;100 MHz&lt;/text&gt;&lt;text x=&quot;676&quot; y=&quot;103&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;SYSCLK, max F412&lt;/text&gt;&lt;line class=&quot;sys-fil sys-courant&quot; x1=&quot;128&quot; y1=&quot;77&quot; x2=&quot;156&quot; y2=&quot;77&quot; marker-end=&quot;url(#hl-fl)&quot;/&gt;&lt;line class=&quot;sys-fil sys-courant&quot; x1=&quot;276&quot; y1=&quot;77&quot; x2=&quot;304&quot; y2=&quot;77&quot; marker-end=&quot;url(#hl-fl)&quot;/&gt;&lt;line class=&quot;sys-fil sys-courant&quot; x1=&quot;424&quot; y1=&quot;77&quot; x2=&quot;452&quot; y2=&quot;77&quot; marker-end=&quot;url(#hl-fl)&quot;/&gt;&lt;line class=&quot;sys-fil sys-courant&quot; x1=&quot;572&quot; y1=&quot;77&quot; x2=&quot;600&quot; y2=&quot;77&quot; marker-end=&quot;url(#hl-fl)&quot;/&gt;&lt;path class=&quot;sys-fil sys-courant&quot; d=&quot;M676,114 V150 H160 V196&quot; marker-end=&quot;url(#hl-fl)&quot;/&gt;&lt;path class=&quot;sys-fil sys-courant&quot; d=&quot;M390,150 V196&quot; marker-end=&quot;url(#hl-fl)&quot;/&gt;&lt;path class=&quot;sys-fil sys-courant&quot; d=&quot;M620,150 V196&quot; marker-end=&quot;url(#hl-fl)&quot;/&gt;&lt;rect class=&quot;sys-boite&quot; x=&quot;60&quot; y=&quot;200&quot; width=&quot;200&quot; height=&quot;74&quot; rx=&quot;6&quot;/&gt;&lt;text x=&quot;160&quot; y=&quot;220&quot; class=&quot;sys-titre&quot; text-anchor=&quot;middle&quot;&gt;AHB ÷1&lt;/text&gt;&lt;text x=&quot;160&quot; y=&quot;244&quot; class=&quot;sys-valeur&quot; text-anchor=&quot;middle&quot;&gt;100 MHz&lt;/text&gt;&lt;text x=&quot;160&quot; y=&quot;263&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;cœur, SysTick : 100 000 cycles/ms&lt;/text&gt;&lt;rect class=&quot;sys-boite&quot; x=&quot;290&quot; y=&quot;200&quot; width=&quot;200&quot; height=&quot;74&quot; rx=&quot;6&quot;/&gt;&lt;text x=&quot;390&quot; y=&quot;220&quot; class=&quot;sys-titre&quot; text-anchor=&quot;middle&quot;&gt;APB1 ÷2&lt;/text&gt;&lt;text x=&quot;390&quot; y=&quot;244&quot; class=&quot;sys-valeur&quot; text-anchor=&quot;middle&quot;&gt;50 MHz&lt;/text&gt;&lt;text x=&quot;390&quot; y=&quot;263&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;USART3 : BRR 434 → 115 207 bauds&lt;/text&gt;&lt;rect class=&quot;sys-boite&quot; x=&quot;520&quot; y=&quot;200&quot; width=&quot;200&quot; height=&quot;74&quot; rx=&quot;6&quot;/&gt;&lt;text x=&quot;620&quot; y=&quot;220&quot; class=&quot;sys-titre&quot; text-anchor=&quot;middle&quot;&gt;APB2 ÷1&lt;/text&gt;&lt;text x=&quot;620&quot; y=&quot;244&quot; class=&quot;sys-valeur&quot; text-anchor=&quot;middle&quot;&gt;100 MHz&lt;/text&gt;&lt;text x=&quot;620&quot; y=&quot;263&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;max 100 MHz&lt;/text&gt;&lt;text x=&quot;380&quot; y=&quot;24&quot; class=&quot;sys-note&quot; text-anchor=&quot;middle&quot;&gt;Flash : 3 wait states (90 &amp;lt; HCLK ≤ 100 MHz à 2,7–3,6 V) · régulateur en scale 1&lt;/text&gt;&lt;text x=&quot;380&quot; y=&quot;296&quot; class=&quot;sys-code&quot; text-anchor=&quot;middle&quot;&gt;Relu sur la carte : RCC_PLLCFGR = 0x28401904 · RCC_CFGR = 0x100a · FLASH_ACR = 0x703&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;La tranche 4 : de 16 MHz sur la HSI à 100 MHz. Chaque constante est justifiée par une section du RM0402 dans le code, et relue dans les registres une fois flashée.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&quot;le-moment-ou-ca-a-casse-savedstate&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#le-moment-ou-ca-a-casse-savedstate&quot; aria-label=&quot;Anchor link for: le-moment-ou-ca-a-casse-savedstate&quot;&gt;#&lt;/a&gt;
Le moment où ça a cassé : SavedState&lt;/h2&gt;
&lt;p&gt;Juste avant le premier allumage, &lt;code&gt;humility tasks&lt;/code&gt; échoue : « expected structure SavedState not found ».&lt;/p&gt;
&lt;p&gt;Pour comprendre, il faut savoir comment Humility lit une tâche. Quand le kernel interrompt une tâche, il range ses registres (r4 à r11, le pointeur de pile, etc.) dans une structure, le champ &lt;code&gt;save&lt;/code&gt; de l’entrée &lt;code&gt;Task&lt;/code&gt; de cette tâche. Humility lit la table des tâches directement dans la RAM de la carte. Pour savoir à quel octet se trouve chaque registre, il a besoin du plan de cette structure, qu’il trouve dans les infos de debug (DWARF) du binaire du kernel. Et il la cherchait par son nom : &lt;code&gt;SavedState&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Le 9 octobre, quatre jours plus tôt, la PR #2704 de Hubris a introduit un trait &lt;code&gt;Arch&lt;/code&gt; pour que le kernel puisse viser plusieurs architectures. La structure de l’ARMv7-M y a pris le nom &lt;code&gt;ArmMSavedState&lt;/code&gt;. Humility ne la trouve plus, et ni &lt;code&gt;humility tasks&lt;/code&gt; ni la lecture des registres d’une tâche ne marchent, quelle que soit la carte.&lt;/p&gt;
&lt;p&gt;Deux façons de s’en sortir. Claude fige d’abord la branche juste avant ce commit et continue, ce qui suffit pour cette carte. C’est dans DECISIONS.md, et ça débloque. Ensuite, le correctif, sur la branche &lt;code&gt;savedstate-by-member&lt;/code&gt; de mon fork de Humility : au lieu de chercher le type par son nom, Humility prend le type du champ &lt;code&gt;save&lt;/code&gt; de &lt;code&gt;Task&lt;/code&gt;, que le DWARF donne directement. Le nom peut changer, le champ reste. Onze lignes ajoutées, quatre retirées, aux deux endroits qui lisaient cette structure : &lt;code&gt;humility tasks&lt;/code&gt; et la lecture des registres. Le correctif marche avec l’ancien kernel et avec le nouveau, testé sur la carte dans les deux cas.&lt;/p&gt;
&lt;h2 id=&quot;le-second-correctif-stackmargin-affichait-des-zeros&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#le-second-correctif-stackmargin-affichait-des-zeros&quot; aria-label=&quot;Anchor link for: le-second-correctif-stackmargin-affichait-des-zeros&quot;&gt;#&lt;/a&gt;
Le second correctif : stackmargin affichait des zéros&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;humility stackmargin&lt;/code&gt; dit, pour chaque tâche, combien de pile elle n’a jamais utilisée. Le principe : au démarrage d’une tâche, le kernel remplit toute sa pile avec un motif, &lt;code&gt;0xbaddcafe&lt;/code&gt;. Humility lit la pile depuis le bas et compte les mots encore intacts : c’est la marge.&lt;/p&gt;
&lt;p&gt;Sur la carte, il affiche une marge de 0 pour &lt;code&gt;jefe&lt;/code&gt;, le superviseur, avec une pile de 152 octets alors que son &lt;code&gt;app.toml&lt;/code&gt; en déclare 2200.&lt;/p&gt;
&lt;p&gt;La MPU d’un Cortex-M4 ne connaît que des régions dont la taille est une puissance de 2. Depuis avril (PR #2458), Hubris découpe la RAM d’une tâche en plusieurs régions contiguës pour moins gaspiller : les 2560 octets de &lt;code&gt;jefe&lt;/code&gt; font 1 Kio + 1 Kio + 512 octets au lieu d’une région de 4 Kio. Une pile peut donc s’étaler sur plusieurs régions. Le kernel a été corrigé en mai (PR #2492) pour la peindre en entier. Humility, lui, ne lisait que la région qui contient le sommet de pile : ici la dernière, où ne tiennent que 152 octets de pile, tous utilisés. D’où la marge de 0.&lt;/p&gt;
&lt;figure class=&quot;sys sys-pile&quot;&gt;&lt;div class=&quot;defile&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;La pile de jefe vue par humility stackmargin, avant et après correction, défilable horizontalement&quot;&gt;&lt;svg viewBox=&quot;0 0 800 300&quot; role=&quot;img&quot; aria-labelledby=&quot;pile-t pile-d&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;title id=&quot;pile-t&quot;&gt;La pile de jefe vue par humility stackmargin, avant et après correction&lt;/title&gt;&lt;desc id=&quot;pile-d&quot;&gt;La RAM de jefe fait 2560 octets répartis en trois régions MPU contiguës. Sa pile de 2200 octets commence à 0x20001400 et son sommet initial est 0x20001C98, dans la troisième région. Avant correction, Humility ne lisait que cette troisième région : 152 octets, tous utilisés, donc une marge de 0. Après correction, il lit les 2200 octets, trouve 1928 octets encore à 0xbaddcafe et une marge de 1928.&lt;/desc&gt;&lt;defs&gt;&lt;pattern id=&quot;pl-peint&quot; width=&quot;6&quot; height=&quot;6&quot; patternUnits=&quot;userSpaceOnUse&quot; patternTransform=&quot;rotate(45)&quot;&gt;&lt;line class=&quot;sys-hachure&quot; x1=&quot;0&quot; y1=&quot;0&quot; x2=&quot;0&quot; y2=&quot;6&quot;/&gt;&lt;/pattern&gt;&lt;/defs&gt;&lt;rect class=&quot;sys-peint-fond&quot; x=&quot;70.0&quot; y=&quot;54&quot; width=&quot;482.0&quot; height=&quot;44&quot;/&gt;&lt;rect fill=&quot;url(#pl-peint)&quot; x=&quot;70.0&quot; y=&quot;54&quot; width=&quot;482.0&quot; height=&quot;44&quot;/&gt;&lt;rect class=&quot;sys-utilise&quot; x=&quot;552.0&quot; y=&quot;54&quot; width=&quot;68.0&quot; height=&quot;44&quot;/&gt;&lt;rect class=&quot;sys-donnees&quot; x=&quot;620.0&quot; y=&quot;54&quot; width=&quot;90.0&quot; height=&quot;44&quot;/&gt;&lt;text x=&quot;198&quot; y=&quot;81&quot; class=&quot;sys-dans&quot; text-anchor=&quot;middle&quot;&gt;0xbaddcafe : 1928 o jamais touchés&lt;/text&gt;&lt;text x=&quot;665&quot; y=&quot;81&quot; class=&quot;sys-dans&quot; text-anchor=&quot;middle&quot;&gt;données&lt;/text&gt;&lt;rect class=&quot;sys-region&quot; x=&quot;70.0&quot; y=&quot;54&quot; width=&quot;256.0&quot; height=&quot;44&quot;/&gt;&lt;text x=&quot;198&quot; y=&quot;44&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;région 1 · 1 Kio&lt;/text&gt;&lt;rect class=&quot;sys-region&quot; x=&quot;326.0&quot; y=&quot;54&quot; width=&quot;256.0&quot; height=&quot;44&quot;/&gt;&lt;text x=&quot;454&quot; y=&quot;44&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;région 2 · 1 Kio&lt;/text&gt;&lt;rect class=&quot;sys-region&quot; x=&quot;582.0&quot; y=&quot;54&quot; width=&quot;128.0&quot; height=&quot;44&quot;/&gt;&lt;text x=&quot;646&quot; y=&quot;44&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;région 3 · 512 o&lt;/text&gt;&lt;line class=&quot;sys-repere&quot; x1=&quot;620.0&quot; y1=&quot;50&quot; x2=&quot;620.0&quot; y2=&quot;168&quot;/&gt;&lt;text x=&quot;624&quot; y=&quot;120&quot; class=&quot;sys-code&quot;&gt;sommet initial 0x20001C98&lt;/text&gt;&lt;line class=&quot;sys-repere&quot; x1=&quot;552.0&quot; y1=&quot;98&quot; x2=&quot;552.0&quot; y2=&quot;108&quot;/&gt;&lt;text x=&quot;548&quot; y=&quot;120&quot; class=&quot;sys-detail&quot; text-anchor=&quot;end&quot;&gt;272 o utilisés&lt;/text&gt;&lt;text x=&quot;70&quot; y=&quot;290&quot; class=&quot;sys-axe&quot; text-anchor=&quot;middle&quot;&gt;0x20001400&lt;/text&gt;&lt;text x=&quot;326&quot; y=&quot;290&quot; class=&quot;sys-axe&quot; text-anchor=&quot;middle&quot;&gt;0x20001800&lt;/text&gt;&lt;text x=&quot;582&quot; y=&quot;290&quot; class=&quot;sys-axe&quot; text-anchor=&quot;middle&quot;&gt;0x20001C00&lt;/text&gt;&lt;text x=&quot;710&quot; y=&quot;290&quot; class=&quot;sys-axe&quot; text-anchor=&quot;middle&quot;&gt;0x20001E00&lt;/text&gt;&lt;text x=&quot;10&quot; y=&quot;174&quot; class=&quot;sys-serie sys-faute-txt&quot;&gt;avant&lt;/text&gt;&lt;line class=&quot;sys-mesure sys-faute&quot; x1=&quot;582.0&quot; y1=&quot;170&quot; x2=&quot;620.0&quot; y2=&quot;170&quot;/&gt;&lt;circle class=&quot;sys-scan sys-scan-avant&quot; cx=&quot;582.0&quot; cy=&quot;170&quot; r=&quot;5&quot;/&gt;&lt;text x=&quot;574&quot; y=&quot;174&quot; class=&quot;sys-detail&quot; text-anchor=&quot;end&quot;&gt;lit 152 o, bute au 1er mot → MARGIN 0&lt;/text&gt;&lt;text x=&quot;10&quot; y=&quot;219&quot; class=&quot;sys-serie sys-sain-txt&quot;&gt;après&lt;/text&gt;&lt;line class=&quot;sys-mesure sys-sain&quot; x1=&quot;70.0&quot; y1=&quot;215&quot; x2=&quot;620.0&quot; y2=&quot;215&quot;/&gt;&lt;g class=&quot;sys-scan-apres&quot;&gt;&lt;circle class=&quot;sys-scan&quot; cx=&quot;70.0&quot; cy=&quot;215&quot; r=&quot;5&quot;/&gt;&lt;/g&gt;&lt;text x=&quot;70&quot; y=&quot;237&quot; class=&quot;sys-detail&quot;&gt;lit 2200 o depuis la base, compte 1928 o de 0xbaddcafe → MARGIN 1928&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;Relevé sur la carte (humility map, readmem). Humility ne regardait que la région qui contient le sommet de pile. Le kernel, lui, peint toute la pile en 0xbaddcafe sur les trois régions.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;           STACKBASE  STACKSIZE  MAXDEPTH  MARGIN&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;avant  jefe 0x20001c00       152       152       0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;après  jefe 0x20001400      2200       272    1928&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le correctif, sur la branche &lt;code&gt;stackmargin-multi-region&lt;/code&gt;, descend les régions contiguës qui appartiennent à la même tâche, comme le fait le kernel, avant de mesurer. Le test de non-régression de Humility l’a aussi validé : sur un dump de Sidecar, un vrai switch d’Oxide, l’instantané attendu contenait trois faux « MARGIN 0 ». Après correction, chaque taille de pile égale à l’octet près celle déclarée dans l’&lt;code&gt;app.toml&lt;/code&gt; de ce Sidecar.&lt;/p&gt;
&lt;h2 id=&quot;ou-j-en-suis&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#ou-j-en-suis&quot; aria-label=&quot;Anchor link for: ou-j-en-suis&quot;&gt;#&lt;/a&gt;
Où j’en suis&lt;/h2&gt;
&lt;p&gt;La branche &lt;code&gt;nucleo-f412zg&lt;/code&gt; compte cinq commits, avec un DECISIONS.md et un README. Elle est figée avant la PR #2704 ; avec le premier correctif, Humility suit aussi le kernel récent. Les deux correctifs sont sur mon fork de Humility. La suite de tests du kernel sur la F412, la tranche 5 optionnelle, n’est pas faite. Et « tout &lt;code&gt;Healthy&lt;/code&gt; » n’est vrai qu’à moitié : &lt;code&gt;ping&lt;/code&gt; fait exprès une faute toutes les 100 réponses de &lt;code&gt;pong&lt;/code&gt;, comme dans les démos d’Oxide.&lt;/p&gt;
&lt;p&gt;Une piste pour la suite, pas encore faite : un capteur I2C ou SPI derrière une tâche driver. Dans Hubris, un driver est une tâche comme une autre : elle seule a accès aux registres du périphérique, déclarés dans l’&lt;code&gt;app.toml&lt;/code&gt;, et les autres tâches lui parlent par messages. Le driver I2C du dépôt vise les STM32H7 et G0 (plus un driver à part pour le LPC55), le SPI seulement les STM32H7 et le LPC55 : rien pour la famille F4, il faudrait l’écrire pour cette carte.&lt;/p&gt;
&lt;p&gt;À suivre…&lt;/p&gt;
&lt;aside class=&quot;transparence&quot; aria-label=&quot;Comment ce billet a été écrit&quot;&gt;
  &lt;p class=&quot;transparence-titre&quot;&gt;Comment ce billet a été écrit&lt;/p&gt;
  &lt;p&gt;Rédigé par Claude Opus 5.5 à partir de : un prompt de mission, une dizaine de messages et un entretien écrits par François, sa session Claude Code, le dépôt &lt;code&gt;hubris&lt;/code&gt; (5 commits sur la branche &lt;code&gt;nucleo-f412zg&lt;/code&gt;), deux branches Humility (3 commits), les sorties &lt;code&gt;humility&lt;/code&gt; et les registres relus sur la carte.&lt;/p&gt;
  &lt;p&gt;Relu et corrigé par François en 20 min, 6 % du texte réécrit.&lt;/p&gt;
&lt;/aside&gt;
</content>
    </entry>
    <entry xml:lang="fr">
        <title>Une montre de 2020, un firmware complet, trois jours</title>
        <published>2026-10-16T00:00:00+00:00</published>
        <updated>2026-10-16T00:00:00+00:00</updated>
        <author><name>François</name></author>
        <link rel="alternate" type="text/html" href="https://blok.storagebits.workers.dev/fr/blog/ressusciter-kronograf/"/>
        <id>https://blok.storagebits.workers.dev/fr/blog/ressusciter-kronograf/</id>
        <summary type="html">Une T-Watch de 2020 dormait dans un coin : développer dessus demandait trop d&#39;efforts. En trois jours, avec un agent, elle a reçu un firmware écrit de zéro, publié sous licence MIT.</summary>
        <content type="html" xml:base="https://blok.storagebits.workers.dev/fr/blog/ressusciter-kronograf/">&lt;p&gt;J’ai acheté une LilyGO T-Watch 2020 il y a six ans, parce qu’elle était bon marché et idéale pour tester et bricoler. Développer dessus demandait en revanche un vrai investissement : du C/C++, un framework à apprendre, des notions d’embarqué. Elle a fini dans un coin, avec d’autres side projects inachevés.&lt;/p&gt;
&lt;p&gt;Avec l’IA, tous ces freins sautent : en quelques minutes, j’avais un firmware complet, écrit de zéro. Sans l’IA, je ne l’aurais pas ressortie du tiroir : y créer quelque chose demandait trop d’efforts.&lt;/p&gt;
&lt;p&gt;Quels appareils dorment chez vous ? Imaginez ce que vous pourriez en faire, en un temps record.&lt;/p&gt;
&lt;p&gt;Le &lt;a href=&quot;https://blok.storagebits.workers.dev/fr/blog/quatre-jours/&quot;&gt;billet sur les quatre jours&lt;/a&gt; a déjà raconté le premier flash. Ici, je reste sur la montre.&lt;/p&gt;
&lt;h2 id=&quot;ce-que-je-voulais&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#ce-que-je-voulais&quot; aria-label=&quot;Anchor link for: ce-que-je-voulais&quot;&gt;#&lt;/a&gt;
Ce que je voulais&lt;/h2&gt;
&lt;p&gt;Un cadran de chronographe d’aviation et des afficheurs 7 segments façon « circuits temporels ». Puis la météo, les bus et les notifications de l’iPhone. La liste complète des applications est dans le README du dépôt.&lt;/p&gt;
&lt;p&gt;Ma méthode tient en deux gestes. J’expose simplement les fonctions que je veux. Puis je demande tout de suite de concevoir une première version de bout en bout, pour avoir une boucle de retour courte. Le résultat devient convaincant très vite.&lt;/p&gt;
&lt;p&gt;J’ai piloté le développement en langage naturel, en exprimant des intentions. En lisant le code généré, j’ai aussi beaucoup appris, tant sur le langage que sur les frameworks et les concepts d’embarqué. La portée pédagogique est immense !&lt;/p&gt;
&lt;h2 id=&quot;le-commencement&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#le-commencement&quot; aria-label=&quot;Anchor link for: le-commencement&quot;&gt;#&lt;/a&gt;
Le commencement&lt;/h2&gt;
&lt;p&gt;Premier commit à 3 h 22. Le prompt de mission imposait deux règles : sauvegarder les 16 Mo de flash d’origine avant toute écriture, et ne jamais toucher aux eFuses ni à la tension du cœur.&lt;/p&gt;
&lt;p&gt;La sauvegarde a failli échouer. À 921 600 bauds, le port USB a disparu en pleine lecture. L’agent a recommencé à 230 400 bauds, par blocs de 1 Mo vérifiés un par un, parce qu’au-delà le câble introduisait des erreurs. À 3 h 46, la sauvegarde complète était vérifiée et son empreinte SHA-256 consignée. À 3 h 48, premier flash vérifié sur la montre.&lt;/p&gt;
&lt;p&gt;J’ai eu un peu peur de bricker la montre. Je ne savais pas d’où venait la panne ; probablement du câble. L’agent a tout pris en charge, jusqu’à essayer un flash lent et fractionné pour composer avec ce câble défectueux (!). Ses questions et ses méthodes étaient pertinentes et précises, et c’est ce qui me donnait confiance.&lt;/p&gt;
&lt;p&gt;À 4 h 09, le cadran d’aviateur s’affichait, avec une trotteuse à 8 Hz.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/fr/blog/ressusciter-kronograf/kronograf-t1-aviator.webp&quot; alt=&quot;Le cadran chronographe d’aviation, capture de la vraie montre par le port série : lunette graduée, trois sous-cadrans batterie, minutes et pas, trotteuse orange&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Le cadran d’aviateur, capturé sur la vraie montre par le port série. Capture déjà publiée dans le billet précédent.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;le-moment-ou-ca-a-casse&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#le-moment-ou-ca-a-casse&quot; aria-label=&quot;Anchor link for: le-moment-ou-ca-a-casse&quot;&gt;#&lt;/a&gt;
Le moment où ça a cassé&lt;/h2&gt;
&lt;p&gt;Branchée en USB, la montre tenait. Sur batterie, elle plantait systématiquement au bout de quelques secondes. Dans la matinée, je craignais qu’elle soit brickée, et je pressais l’agent d’aller vite avant le plantage suivant. J’ai dû l’éteindre et la rallumer trois fois.&lt;/p&gt;
&lt;p&gt;Il y avait deux causes, trouvées l’une après l’autre. D’abord, le chien de garde du processeur restait armé pendant le sommeil léger. Il est maintenant suspendu avant la mise en veille et réarmé au réveil. Ensuite, un appui sur le bouton figeait la montre. La ligne d’interruption de la puce d’alimentation reste à l’état bas tant qu’on ne la lit pas, et l’interruption se redéclenchait sans fin. Les interruptions GPIO sont désormais désactivées pendant le sommeil, et j’ai constaté que la montre tenait très bien.&lt;/p&gt;
&lt;h2 id=&quot;une-decision-ne-plus-dependre-du-cable&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#une-decision-ne-plus-dependre-du-cable&quot; aria-label=&quot;Anchor link for: une-decision-ne-plus-dependre-du-cable&quot;&gt;#&lt;/a&gt;
Une décision : ne plus dépendre du câble&lt;/h2&gt;
&lt;p&gt;Les coupures USB revenaient pendant les flashs. Il y avait deux options : attendre que le port revienne, ou rendre le câble accessoire. L’agent a proposé le « mode atelier » : quand la montre est alimentée par USB et que le Wi-Fi est configuré, elle reste connectée, accepte les mises à jour par Wi-Fi (OTA) et expose une console TCP. Un Wi-Fi allumé en permanence est normalement un anti-pattern. Sur secteur, la batterie n’est pas en jeu, et l’option se désactive.&lt;/p&gt;
&lt;p&gt;Comme le câble lâchait, l’agent est passé de lui-même aux mises à jour par Wi-Fi (OTA). Magique ! Plus tard, les coupures sont aussi apparues pendant que le processeur était dans son chargeur de démarrage en ROM : le problème est donc matériel, pas logiciel. Un mode sans échec a suivi : après trois plantages d’affilée dans la première minute, le Bluetooth ne démarre pas, pour qu’on puisse toujours réparer la montre à distance.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/fr/blog/ressusciter-kronograf/kronograf-photo.webp&quot; alt=&quot;La vraie T-Watch 2020 posée sur la table : écran de diagnostic Kronograf, mémoire, capteurs détectés en vert, batterie à 100 %&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;La vraie montre sur l’écran de diagnostic, le premier écran flashé. Photo déjà publiée dans le billet précédent.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;ou-j-en-suis&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#ou-j-en-suis&quot; aria-label=&quot;Anchor link for: ou-j-en-suis&quot;&gt;#&lt;/a&gt;
Où j’en suis&lt;/h2&gt;
&lt;p&gt;Le problème de câble (B2 dans &lt;code&gt;BLOCKERS.md&lt;/code&gt;) reste ouvert : la connexion micro-USB est toujours fragile. Le double tapotement qui réveille l’écran reste un peu aléatoire, mais il fonctionne. Le geste du poignet pour réveiller la montre est encore à régler. L’autonomie sur une journée n’est pas encore mesurée.&lt;/p&gt;
&lt;p&gt;Ce qui m’a le plus impressionné, c’est la rapidité avec laquelle l’agent a flashé la montre en autonomie et m’a montré des résultats concrets. J’ai hâte de l’essayer sur d’autres appareils. Je vais d’ailleurs bientôt ressortir mes micro:bits : j’ai une idée de projet !&lt;/p&gt;
&lt;aside class=&quot;transparence&quot; aria-label=&quot;Comment ce billet a été écrit&quot;&gt;
  &lt;p class=&quot;transparence-titre&quot;&gt;Comment ce billet a été écrit&lt;/p&gt;
  &lt;p&gt;Rédigé par Claude Opus 5.5 à partir de : un brief et un entretien écrits par François, ses messages de session, le dépôt &lt;code&gt;kronograf&lt;/code&gt;, &lt;code&gt;DECISIONS.md&lt;/code&gt; et &lt;code&gt;BLOCKERS.md&lt;/code&gt;.&lt;/p&gt;
  &lt;p&gt;Relu et corrigé par François en 30 min, 17 % du texte réécrit.&lt;/p&gt;
&lt;/aside&gt;
</content>
    </entry>
    <entry xml:lang="fr">
        <title>Douze minutes entre l&#39;idée et la télé</title>
        <published>2026-10-12T00:00:00+00:00</published>
        <updated>2026-10-12T00:00:00+00:00</updated>
        <author><name>François</name></author>
        <link rel="alternate" type="text/html" href="https://blok.storagebits.workers.dev/fr/blog/quatre-jours/"/>
        <id>https://blok.storagebits.workers.dev/fr/blog/quatre-jours/</id>
        <summary type="html">Quatre jours, six projets, 159 commits. Le délai entre « j&#39;aimerais bien » et « ça tourne sur l&#39;appareil » est devenu ridicule. Ce qui ralentit encore, c&#39;est moi, mes câbles et mes quotas.</summary>
        <content type="html" xml:base="https://blok.storagebits.workers.dev/fr/blog/quatre-jours/">&lt;p&gt;Jeudi 8 octobre, 20 h 34, j’envoie un prompt de mission à un agent. 20 h 46, l’heure s’affiche en grand sur ma télé, dans une typo choisie par l’agent.&lt;/p&gt;
&lt;p&gt;Quatre jours plus tard : six projets, 159 commits, quelque 240 messages de ma part, dont une bonne partie font moins de cinq mots. « go T8 ». « vas-y présente la tranche 3 ». « c’est fait, reteste ».&lt;/p&gt;
&lt;p&gt;Le délai entre l’intention et le concret est devenu ridicule. Douze minutes pour l’application Almanak. Seize pour Lukarne. Le goulot d’étranglement, maintenant, c’est moi : mon sommeil, mes câbles, mes quotas.&lt;/p&gt;
&lt;figure class=&quot;frise&quot;&gt;&lt;div class=&quot;defile&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Frise chronologique, défilable horizontalement&quot;&gt;&lt;svg viewBox=&quot;0 0 900 412&quot; role=&quot;img&quot; aria-labelledby=&quot;frise-titre frise-desc&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;title id=&quot;frise-titre&quot;&gt;Quatre jours de commits, projet par projet&lt;/title&gt;&lt;desc id=&quot;frise-desc&quot;&gt;Une ligne par projet, du jeudi 8 octobre 19 h au lundi 12 octobre 19 h. Chaque trait est un commit. Le cercle vide marque le premier message à l&#39;agent ; le cercle plein, relevé au-dessus de la ligne, le premier résultat concret. Les nuits sont ombrées.&lt;/desc&gt;&lt;rect class=&quot;nuit&quot; x=&quot;157.3&quot; y=&quot;34&quot; width=&quot;55.0&quot; height=&quot;338&quot;/&gt;&lt;line class=&quot;grille&quot; x1=&quot;157.3&quot; x2=&quot;157.3&quot; y1=&quot;34&quot; y2=&quot;372&quot;/&gt;&lt;text class=&quot;axe&quot; x=&quot;161.3&quot; y=&quot;28&quot;&gt;ven. 9&lt;/text&gt;&lt;rect class=&quot;nuit&quot; x=&quot;345.8&quot; y=&quot;34&quot; width=&quot;55.0&quot; height=&quot;338&quot;/&gt;&lt;line class=&quot;grille&quot; x1=&quot;345.8&quot; x2=&quot;345.8&quot; y1=&quot;34&quot; y2=&quot;372&quot;/&gt;&lt;text class=&quot;axe&quot; x=&quot;349.8&quot; y=&quot;28&quot;&gt;sam. 10&lt;/text&gt;&lt;rect class=&quot;nuit&quot; x=&quot;534.3&quot; y=&quot;34&quot; width=&quot;55.0&quot; height=&quot;338&quot;/&gt;&lt;line class=&quot;grille&quot; x1=&quot;534.3&quot; x2=&quot;534.3&quot; y1=&quot;34&quot; y2=&quot;372&quot;/&gt;&lt;text class=&quot;axe&quot; x=&quot;538.3&quot; y=&quot;28&quot;&gt;dim. 11&lt;/text&gt;&lt;rect class=&quot;nuit&quot; x=&quot;722.8&quot; y=&quot;34&quot; width=&quot;55.0&quot; height=&quot;338&quot;/&gt;&lt;line class=&quot;grille&quot; x1=&quot;722.8&quot; x2=&quot;722.8&quot; y1=&quot;34&quot; y2=&quot;372&quot;/&gt;&lt;text class=&quot;axe&quot; x=&quot;726.8&quot; y=&quot;28&quot;&gt;lun. 12&lt;/text&gt;&lt;text class=&quot;axe&quot; x=&quot;118.0&quot; y=&quot;28&quot;&gt;jeu. 8&lt;/text&gt;&lt;text class=&quot;nom&quot; x=&quot;104&quot; y=&quot;85.0&quot; text-anchor=&quot;end&quot;&gt;Almanak&lt;/text&gt;&lt;line class=&quot;rail&quot; x1=&quot;118&quot; x2=&quot;872&quot; y1=&quot;81.0&quot; y2=&quot;81.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;669.9&quot; x2=&quot;669.9&quot; y1=&quot;74.0&quot; y2=&quot;88.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;669.6&quot; x2=&quot;669.6&quot; y1=&quot;74.0&quot; y2=&quot;88.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;611.3&quot; x2=&quot;611.3&quot; y1=&quot;74.0&quot; y2=&quot;88.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;145.9&quot; x2=&quot;145.9&quot; y1=&quot;74.0&quot; y2=&quot;88.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;145.3&quot; x2=&quot;145.3&quot; y1=&quot;74.0&quot; y2=&quot;88.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;140.8&quot; x2=&quot;140.8&quot; y1=&quot;74.0&quot; y2=&quot;88.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;139.4&quot; x2=&quot;139.4&quot; y1=&quot;74.0&quot; y2=&quot;88.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;137.5&quot; x2=&quot;137.5&quot; y1=&quot;74.0&quot; y2=&quot;88.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;135.9&quot; x2=&quot;135.9&quot; y1=&quot;74.0&quot; y2=&quot;88.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;133.6&quot; x2=&quot;133.6&quot; y1=&quot;74.0&quot; y2=&quot;88.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;131.9&quot; x2=&quot;131.9&quot; y1=&quot;74.0&quot; y2=&quot;88.0&quot;/&gt;&lt;path class=&quot;delai&quot; d=&quot;M130.3,81.0 L131.9,81.0 L131.9,63.0&quot;/&gt;&lt;circle class=&quot;intention&quot; cx=&quot;130.3&quot; cy=&quot;81.0&quot; r=&quot;5&quot;&gt;&lt;title&gt;jeu. 20 h 34 · prompt de mission&lt;/title&gt;&lt;/circle&gt;&lt;circle class=&quot;concret&quot; cx=&quot;131.9&quot; cy=&quot;63.0&quot; r=&quot;5.5&quot;&gt;&lt;title&gt;20 h 46 · l&#39;heure s&#39;affiche sur la télé&lt;/title&gt;&lt;/circle&gt;&lt;text class=&quot;etiquette&quot; x=&quot;140.9&quot; y=&quot;67.0&quot;&gt;12 min&lt;/text&gt;&lt;text class=&quot;compte&quot; x=&quot;880&quot; y=&quot;85.0&quot;&gt;11&lt;/text&gt;&lt;text class=&quot;nom&quot; x=&quot;104&quot; y=&quot;139.0&quot; text-anchor=&quot;end&quot;&gt;Homelab&lt;/text&gt;&lt;line class=&quot;rail&quot; x1=&quot;118&quot; x2=&quot;872&quot; y1=&quot;135.0&quot; y2=&quot;135.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;869.9&quot; x2=&quot;869.9&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;863.8&quot; x2=&quot;863.8&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;863.3&quot; x2=&quot;863.3&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;859.6&quot; x2=&quot;859.6&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;847.3&quot; x2=&quot;847.3&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;843.9&quot; x2=&quot;843.9&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;838.3&quot; x2=&quot;838.3&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;820.5&quot; x2=&quot;820.5&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;819.9&quot; x2=&quot;819.9&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;819.9&quot; x2=&quot;819.9&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;811.7&quot; x2=&quot;811.7&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;810.2&quot; x2=&quot;810.2&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;807.8&quot; x2=&quot;807.8&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;777.1&quot; x2=&quot;777.1&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;762.2&quot; x2=&quot;762.2&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;757.2&quot; x2=&quot;757.2&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;755.4&quot; x2=&quot;755.4&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;752.5&quot; x2=&quot;752.5&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;747.7&quot; x2=&quot;747.7&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;729.7&quot; x2=&quot;729.7&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;729.1&quot; x2=&quot;729.1&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;715.3&quot; x2=&quot;715.3&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;707.6&quot; x2=&quot;707.6&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;706.1&quot; x2=&quot;706.1&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;704.2&quot; x2=&quot;704.2&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;703.4&quot; x2=&quot;703.4&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;697.9&quot; x2=&quot;697.9&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;693.0&quot; x2=&quot;693.0&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;692.3&quot; x2=&quot;692.3&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;690.6&quot; x2=&quot;690.6&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;688.3&quot; x2=&quot;688.3&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;675.3&quot; x2=&quot;675.3&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;672.7&quot; x2=&quot;672.7&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;670.4&quot; x2=&quot;670.4&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;666.2&quot; x2=&quot;666.2&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;665.0&quot; x2=&quot;665.0&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;663.7&quot; x2=&quot;663.7&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;663.4&quot; x2=&quot;663.4&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;661.3&quot; x2=&quot;661.3&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;660.2&quot; x2=&quot;660.2&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;656.5&quot; x2=&quot;656.5&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;651.3&quot; x2=&quot;651.3&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;640.9&quot; x2=&quot;640.9&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;639.3&quot; x2=&quot;639.3&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;627.0&quot; x2=&quot;627.0&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;624.1&quot; x2=&quot;624.1&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;617.1&quot; x2=&quot;617.1&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;615.1&quot; x2=&quot;615.1&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;579.8&quot; x2=&quot;579.8&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;532.9&quot; x2=&quot;532.9&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;532.8&quot; x2=&quot;532.8&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;527.1&quot; x2=&quot;527.1&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;526.5&quot; x2=&quot;526.5&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;526.0&quot; x2=&quot;526.0&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;496.1&quot; x2=&quot;496.1&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;495.7&quot; x2=&quot;495.7&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;494.8&quot; x2=&quot;494.8&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;488.8&quot; x2=&quot;488.8&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;488.2&quot; x2=&quot;488.2&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;487.9&quot; x2=&quot;487.9&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;484.2&quot; x2=&quot;484.2&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;484.2&quot; x2=&quot;484.2&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;478.5&quot; x2=&quot;478.5&quot; y1=&quot;128.0&quot; y2=&quot;142.0&quot;/&gt;&lt;path class=&quot;delai&quot; d=&quot;M136.2,135.0 L141.6,135.0 L141.6,117.0&quot;/&gt;&lt;circle class=&quot;intention&quot; cx=&quot;136.2&quot; cy=&quot;135.0&quot; r=&quot;5&quot;&gt;&lt;title&gt;jeu. 21 h 19 · « c&#39;est parti ! »&lt;/title&gt;&lt;/circle&gt;&lt;circle class=&quot;concret&quot; cx=&quot;141.6&quot; cy=&quot;117.0&quot; r=&quot;5.5&quot;&gt;&lt;title&gt;22 h 00 · double authentification active sur la passerelle&lt;/title&gt;&lt;/circle&gt;&lt;text class=&quot;etiquette&quot; x=&quot;150.6&quot; y=&quot;121.0&quot;&gt;41 min&lt;/text&gt;&lt;text class=&quot;compte&quot; x=&quot;880&quot; y=&quot;139.0&quot;&gt;63&lt;/text&gt;&lt;text class=&quot;nom&quot; x=&quot;104&quot; y=&quot;193.0&quot; text-anchor=&quot;end&quot;&gt;Lukarne&lt;/text&gt;&lt;line class=&quot;rail&quot; x1=&quot;118&quot; x2=&quot;872&quot; y1=&quot;189.0&quot; y2=&quot;189.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;852.5&quot; x2=&quot;852.5&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;842.4&quot; x2=&quot;842.4&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;812.1&quot; x2=&quot;812.1&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;743.0&quot; x2=&quot;743.0&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;730.6&quot; x2=&quot;730.6&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;729.1&quot; x2=&quot;729.1&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;726.2&quot; x2=&quot;726.2&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;724.3&quot; x2=&quot;724.3&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;708.1&quot; x2=&quot;708.1&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;705.9&quot; x2=&quot;705.9&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;705.3&quot; x2=&quot;705.3&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;703.9&quot; x2=&quot;703.9&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;656.5&quot; x2=&quot;656.5&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;655.3&quot; x2=&quot;655.3&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;652.0&quot; x2=&quot;652.0&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;650.7&quot; x2=&quot;650.7&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;643.4&quot; x2=&quot;643.4&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;642.9&quot; x2=&quot;642.9&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;610.2&quot; x2=&quot;610.2&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;531.0&quot; x2=&quot;531.0&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;429.2&quot; x2=&quot;429.2&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;321.5&quot; x2=&quot;321.5&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;317.3&quot; x2=&quot;317.3&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;312.5&quot; x2=&quot;312.5&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;307.7&quot; x2=&quot;307.7&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;306.0&quot; x2=&quot;306.0&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;300.5&quot; x2=&quot;300.5&quot; y1=&quot;182.0&quot; y2=&quot;196.0&quot;/&gt;&lt;path class=&quot;delai&quot; d=&quot;M298.4,189.0 L300.5,189.0 L300.5,171.0&quot;/&gt;&lt;circle class=&quot;intention&quot; cx=&quot;298.4&quot; cy=&quot;189.0&quot; r=&quot;5&quot;&gt;&lt;title&gt;ven. 17 h 58 · prompt de mission&lt;/title&gt;&lt;/circle&gt;&lt;circle class=&quot;concret&quot; cx=&quot;300.5&quot; cy=&quot;171.0&quot; r=&quot;5.5&quot;&gt;&lt;title&gt;18 h 14 · premier direct en plein écran&lt;/title&gt;&lt;/circle&gt;&lt;text class=&quot;etiquette&quot; x=&quot;309.5&quot; y=&quot;175.0&quot;&gt;16 min&lt;/text&gt;&lt;text class=&quot;compte&quot; x=&quot;880&quot; y=&quot;193.0&quot;&gt;27&lt;/text&gt;&lt;text class=&quot;nom&quot; x=&quot;104&quot; y=&quot;247.0&quot; text-anchor=&quot;end&quot;&gt;DropboksTV&lt;/text&gt;&lt;line class=&quot;rail&quot; x1=&quot;118&quot; x2=&quot;872&quot; y1=&quot;243.0&quot; y2=&quot;243.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;687.6&quot; x2=&quot;687.6&quot; y1=&quot;236.0&quot; y2=&quot;250.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;683.4&quot; x2=&quot;683.4&quot; y1=&quot;236.0&quot; y2=&quot;250.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;682.4&quot; x2=&quot;682.4&quot; y1=&quot;236.0&quot; y2=&quot;250.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;679.4&quot; x2=&quot;679.4&quot; y1=&quot;236.0&quot; y2=&quot;250.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;679.4&quot; x2=&quot;679.4&quot; y1=&quot;236.0&quot; y2=&quot;250.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;677.5&quot; x2=&quot;677.5&quot; y1=&quot;236.0&quot; y2=&quot;250.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;676.7&quot; x2=&quot;676.7&quot; y1=&quot;236.0&quot; y2=&quot;250.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;673.1&quot; x2=&quot;673.1&quot; y1=&quot;236.0&quot; y2=&quot;250.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;668.8&quot; x2=&quot;668.8&quot; y1=&quot;236.0&quot; y2=&quot;250.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;666.6&quot; x2=&quot;666.6&quot; y1=&quot;236.0&quot; y2=&quot;250.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;663.3&quot; x2=&quot;663.3&quot; y1=&quot;236.0&quot; y2=&quot;250.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;660.2&quot; x2=&quot;660.2&quot; y1=&quot;236.0&quot; y2=&quot;250.0&quot;/&gt;&lt;path class=&quot;delai&quot; d=&quot;M654.4,243.0 L660.2,243.0 L660.2,225.0&quot;/&gt;&lt;circle class=&quot;intention&quot; cx=&quot;654.4&quot; cy=&quot;243.0&quot; r=&quot;5&quot;&gt;&lt;title&gt;dim. 15 h 18 · « read the prompt and go »&lt;/title&gt;&lt;/circle&gt;&lt;circle class=&quot;concret&quot; cx=&quot;660.2&quot; cy=&quot;225.0&quot; r=&quot;5.5&quot;&gt;&lt;title&gt;16 h 02 · appairage réussi sur la télé&lt;/title&gt;&lt;/circle&gt;&lt;text class=&quot;etiquette&quot; x=&quot;669.2&quot; y=&quot;229.0&quot;&gt;44 min&lt;/text&gt;&lt;text class=&quot;compte&quot; x=&quot;880&quot; y=&quot;247.0&quot;&gt;12&lt;/text&gt;&lt;text class=&quot;nom&quot; x=&quot;104&quot; y=&quot;301.0&quot; text-anchor=&quot;end&quot;&gt;shot2cart&lt;/text&gt;&lt;line class=&quot;rail&quot; x1=&quot;118&quot; x2=&quot;872&quot; y1=&quot;297.0&quot; y2=&quot;297.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;775.5&quot; x2=&quot;775.5&quot; y1=&quot;290.0&quot; y2=&quot;304.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;770.9&quot; x2=&quot;770.9&quot; y1=&quot;290.0&quot; y2=&quot;304.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;768.6&quot; x2=&quot;768.6&quot; y1=&quot;290.0&quot; y2=&quot;304.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;767.6&quot; x2=&quot;767.6&quot; y1=&quot;290.0&quot; y2=&quot;304.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;760.8&quot; x2=&quot;760.8&quot; y1=&quot;290.0&quot; y2=&quot;304.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;754.5&quot; x2=&quot;754.5&quot; y1=&quot;290.0&quot; y2=&quot;304.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;751.4&quot; x2=&quot;751.4&quot; y1=&quot;290.0&quot; y2=&quot;304.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;745.4&quot; x2=&quot;745.4&quot; y1=&quot;290.0&quot; y2=&quot;304.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;731.1&quot; x2=&quot;731.1&quot; y1=&quot;290.0&quot; y2=&quot;304.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;718.0&quot; x2=&quot;718.0&quot; y1=&quot;290.0&quot; y2=&quot;304.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;716.9&quot; x2=&quot;716.9&quot; y1=&quot;290.0&quot; y2=&quot;304.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;715.1&quot; x2=&quot;715.1&quot; y1=&quot;290.0&quot; y2=&quot;304.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;694.1&quot; x2=&quot;694.1&quot; y1=&quot;290.0&quot; y2=&quot;304.0&quot;/&gt;&lt;path class=&quot;delai&quot; d=&quot;M693.1,297.0 L715.0,297.0 L715.0,279.0&quot;/&gt;&lt;circle class=&quot;intention&quot; cx=&quot;693.1&quot; cy=&quot;297.0&quot; r=&quot;5&quot;&gt;&lt;title&gt;dim. 20 h 13 · « forget about previous session »&lt;/title&gt;&lt;/circle&gt;&lt;circle class=&quot;concret&quot; cx=&quot;715.0&quot; cy=&quot;279.0&quot; r=&quot;5.5&quot;&gt;&lt;title&gt;23 h 01 · photo → liste → liens marchands&lt;/title&gt;&lt;/circle&gt;&lt;text class=&quot;etiquette&quot; x=&quot;724.0&quot; y=&quot;283.0&quot;&gt;2 h 48&lt;/text&gt;&lt;text class=&quot;compte&quot; x=&quot;880&quot; y=&quot;301.0&quot;&gt;13&lt;/text&gt;&lt;text class=&quot;nom&quot; x=&quot;104&quot; y=&quot;355.0&quot; text-anchor=&quot;end&quot;&gt;Kronograf&lt;/text&gt;&lt;line class=&quot;rail&quot; x1=&quot;118&quot; x2=&quot;872&quot; y1=&quot;351.0&quot; y2=&quot;351.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;861.6&quot; x2=&quot;861.6&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;845.9&quot; x2=&quot;845.9&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;844.8&quot; x2=&quot;844.8&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;843.7&quot; x2=&quot;843.7&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;839.5&quot; x2=&quot;839.5&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;823.0&quot; x2=&quot;823.0&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;820.9&quot; x2=&quot;820.9&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;817.6&quot; x2=&quot;817.6&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;813.3&quot; x2=&quot;813.3&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;811.5&quot; x2=&quot;811.5&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;810.9&quot; x2=&quot;810.9&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;809.4&quot; x2=&quot;809.4&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;802.1&quot; x2=&quot;802.1&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;776.0&quot; x2=&quot;776.0&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;775.4&quot; x2=&quot;775.4&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;767.6&quot; x2=&quot;767.6&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;761.5&quot; x2=&quot;761.5&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;760.7&quot; x2=&quot;760.7&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;759.9&quot; x2=&quot;759.9&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;759.1&quot; x2=&quot;759.1&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;756.9&quot; x2=&quot;756.9&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;756.8&quot; x2=&quot;756.8&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;756.2&quot; x2=&quot;756.2&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;756.0&quot; x2=&quot;756.0&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;755.5&quot; x2=&quot;755.5&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;754.2&quot; x2=&quot;754.2&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;754.1&quot; x2=&quot;754.1&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;752.7&quot; x2=&quot;752.7&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;752.4&quot; x2=&quot;752.4&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;752.1&quot; x2=&quot;752.1&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;751.5&quot; x2=&quot;751.5&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;749.9&quot; x2=&quot;749.9&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;line class=&quot;commit&quot; x1=&quot;749.3&quot; x2=&quot;749.3&quot; y1=&quot;344.0&quot; y2=&quot;358.0&quot;/&gt;&lt;path class=&quot;delai&quot; d=&quot;M748.8,351.0 L752.6,351.0 L752.6,333.0&quot;/&gt;&lt;circle class=&quot;intention&quot; cx=&quot;748.8&quot; cy=&quot;351.0&quot; r=&quot;5&quot;&gt;&lt;title&gt;lun. 3 h 19 · « read it and get started »&lt;/title&gt;&lt;/circle&gt;&lt;circle class=&quot;concret&quot; cx=&quot;752.6&quot; cy=&quot;333.0&quot; r=&quot;5.5&quot;&gt;&lt;title&gt;3 h 48 · premier flash vérifié sur la montre&lt;/title&gt;&lt;/circle&gt;&lt;text class=&quot;etiquette&quot; x=&quot;761.6&quot; y=&quot;337.0&quot;&gt;29 min&lt;/text&gt;&lt;text class=&quot;compte&quot; x=&quot;880&quot; y=&quot;355.0&quot;&gt;33&lt;/text&gt;&lt;g class=&quot;legende&quot; transform=&quot;translate(118,394)&quot;&gt;&lt;circle class=&quot;intention&quot; cx=&quot;6&quot; cy=&quot;0&quot; r=&quot;5&quot;/&gt;&lt;text x=&quot;16&quot; y=&quot;4&quot;&gt;premier message à l&#39;agent&lt;/text&gt;&lt;circle class=&quot;concret&quot; cx=&quot;236&quot; cy=&quot;0&quot; r=&quot;5.5&quot;/&gt;&lt;text x=&quot;246&quot; y=&quot;4&quot;&gt;premier résultat concret&lt;/text&gt;&lt;line class=&quot;commit&quot; x1=&quot;452&quot; x2=&quot;452&quot; y1=&quot;-7&quot; y2=&quot;7&quot;/&gt;&lt;text x=&quot;462&quot; y=&quot;4&quot;&gt;un commit&lt;/text&gt;&lt;rect class=&quot;nuit&quot; x=&quot;560&quot; y=&quot;-7&quot; width=&quot;14&quot; height=&quot;14&quot;/&gt;&lt;text x=&quot;580&quot; y=&quot;4&quot;&gt;nuit&lt;/text&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;Une ligne par projet, du jeudi 19 h au lundi 19 h. Le chiffre à droite compte les commits. Survole les cercles pour les heures exactes.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Chaque ligne est un projet, chaque trait un commit. Le cercle vide, c’est mon premier message à l’agent. Le plein, relevé au-dessus de la ligne, c’est le premier truc concret : l’app qui tourne sur la télé, le firmware sur la montre, la double authentification active sur la passerelle du homelab. Les bandes grises, ce sont les nuits. Il y a des commits dedans. Pas toujours parce que j’étais debout.&lt;/p&gt;
&lt;h2 id=&quot;jeudi-soir-trois-agents-en-45-minutes&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#jeudi-soir-trois-agents-en-45-minutes&quot; aria-label=&quot;Anchor link for: jeudi-soir-trois-agents-en-45-minutes&quot;&gt;#&lt;/a&gt;
Jeudi soir : trois agents en 45 minutes&lt;/h2&gt;
&lt;p&gt;Le 8 au soir, en 45 minutes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;20 h 34 : la mission Almanak part.&lt;/li&gt;
&lt;li&gt;20 h 36 : je relance shot2cart, un projet commencé en septembre avec un autre outil. « inspect project, it comes from an other AI conversation, inspect and we pursue ».&lt;/li&gt;
&lt;li&gt;21 h 07 : je télécharge depuis claude.ai le &lt;code&gt;CLAUDE.md&lt;/code&gt; de mon homelab, que je redesign par LLM, et que je venais d’écrire avec une autre instance pendant que la première construisait Almanak.&lt;/li&gt;
&lt;li&gt;21 h 19 : « je viens de créer CLAUDE.md pour toi, c’est parti ! »&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Personne n’attend personne. Pendant qu’un agent compile, j’écris la mission du suivant avec un troisième.&lt;/p&gt;
&lt;h2 id=&quot;almanak-l-etincelle&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#almanak-l-etincelle&quot; aria-label=&quot;Anchor link for: almanak-l-etincelle&quot;&gt;#&lt;/a&gt;
Almanak, l’étincelle&lt;/h2&gt;
&lt;p&gt;Le north star tenait en une phrase :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Un écran qu’on a envie de laisser allumé. Quand je passe devant ma télé, je sais en 2 secondes : l’heure, la date, le temps qu’il fait et va faire, et dans combien de minutes passe mon prochain bus. C’est beau comme un objet de design, pas comme un dashboard d’admin.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Et une règle que j’ai gardée pour tous les projets suivants : aucune tranche n’est finie tant que l’agent ne l’a pas vue tourner sur la Shield. Il fait une capture par adb, il la lit, il la critique. Lisibilité à 3 m, overscan, débordements. Et pour chaque tranche visuelle, deux variantes, on garde la meilleure.&lt;/p&gt;
&lt;div class=&quot;galerie&quot;&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/fr/blog/quatre-jours/almanak.webp&quot; alt=&quot;Almanak sur la télé, thème sombre : l’heure en grand, la date, le jour de l’année, les prochains bus et la météo sur cinq jours&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/fr/blog/quatre-jours/almanak-papier.webp&quot; alt=&quot;Almanak, thème papier : l’heure, la fête du jour, les bus et la courbe de température des prochaines heures&quot; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Almanak sur la Shield le premier soir : le thème sombre à 22 h 17, le thème papier à 21 h 38.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Sept tranches entre 20 h 46 et 22 h 32. Pour l’horloge, l’agent a comparé une police serif éditoriale et une sans-serif suisse, et il a gardé Fraunces parce qu’à 3 m, Inter en graisse 200 « commence à ressembler à un fil de fer ». Il a raison.&lt;/p&gt;
&lt;p&gt;Le meilleur bug : la synchro des horaires de bus renvoyait &lt;code&gt;HTTP 441&lt;/code&gt; sur la Shield et marchait depuis le Mac. L’agent a fouillé &lt;code&gt;dumpsys connectivity&lt;/code&gt; et trouvé le VPN qui tourne sur la Shield : le site de la STM (le réseau de transport de Montréal) refuse les IP de sortie des VPN. Il a basculé sur un miroir du même flux, octet pour octet, et documenté le split tunneling dans le README. Le prompt ne parlait pas de VPN.&lt;/p&gt;
&lt;p&gt;Pour être honnête : ma définition de « fini » demandait 24 h d’écran de veille sans fuite mémoire. La mesure réelle couvre à peu près une heure. Fini, donc. Avec des guillemets.&lt;/p&gt;
&lt;h2 id=&quot;lukarne-mon-prefere&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#lukarne-mon-prefere&quot; aria-label=&quot;Anchor link for: lukarne-mon-prefere&quot;&gt;#&lt;/a&gt;
Lukarne, mon préféré&lt;/h2&gt;
&lt;p&gt;Le lendemain, 17 h 58, deuxième mission. Une fenêtre sur le monde : faire tourner un globe à la télécommande, plonger sur une région, voir une mosaïque de webcams en direct, zapper d’une caméra à l’autre. La phrase qui compte dans le prompt :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Si une interaction n’est pas fluide à la télécommande, elle est ratée, même si elle est techniquement correcte.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Et une autre, plus discrète, qui explique pourquoi tout est allé si vite : « J’ai déjà développé une autre app Google TV avec toi sur cette même Shield. Avant toute chose, retrouve dans mon environnement […] comment on s’y connecte […]. Réutilise ce qui marche. » Chaque projet laisse derrière lui une adresse adb, un script de déploiement, un format de &lt;code&gt;DECISIONS.md&lt;/code&gt;. Le suivant part de là.&lt;/p&gt;
&lt;p&gt;18 h 14 : premier direct en plein écran sur la télé, HLS et YouTube, gauche/droite pour passer de l’un à l’autre. 16 minutes. À 20 h 54, la V1 était là : un globe jour/nuit en OpenGL à 60 images par seconde (3 ms par image), 1 640 caméras dans 34 régions (19 de plus depuis lundi), une mosaïque 3×3 dont la tuile sélectionnée passe en direct en deux secondes, et un bouton « surprends-moi » qui t’envoie là où il fait jour. Deux tests d’endurance de 22 minutes, zéro crash.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/fr/blog/quatre-jours/lukarne-globe.webp&quot; alt=&quot;Le globe de Lukarne centré sur l’Europe de nuit, Venise sélectionnée, une webcam de la place Saint-Marc en incrustation&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Lukarne : l’Europe de nuit, Venise sélectionnée, 16 webcams en direct, le bassin de Saint-Marc en coin d’écran.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Le plus simple, c’est de la regarder tourner :&lt;/p&gt;
&lt;figure class=&quot;video&quot;&gt;&lt;video controls muted playsinline preload=&quot;metadata&quot; poster=&quot;lukarne-visite-poster.jpg&quot;&gt;&lt;source src=&quot;lukarne-visite.mp4&quot; type=&quot;video/mp4&quot;&gt;&lt;/video&gt;&lt;figcaption&gt;78 secondes sur la Shield, sans le son : le globe jusqu&#39;à la Californie, la plongée sur Pollock Pines, la mosaïque de caméras d&#39;autoroute en direct, le plein écran, la caméra suivante, on remonte, puis « surprise me ».&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Cette vidéo, c’est l’agent qui l’a tournée, lundi soir, pour ce billet. Il pilotait la Shield à la télécommande virtuelle (&lt;code&gt;adb shell input keyevent&lt;/code&gt;) et enregistrait l’écran avec &lt;code&gt;screenrecord&lt;/code&gt;. Il a fait une première prise en français, en Europe, où il était 1 h du matin : tout était noir. Il a refait la prise en anglais, en Californie, de jour.&lt;/p&gt;
&lt;div class=&quot;galerie&quot;&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/fr/blog/quatre-jours/lukarne-mosaique.webp&quot; alt=&quot;La mosaïque de Pollock Pines : neuf caméras d’autoroute de l’US 50 en direct, la tuile sélectionnée encadrée&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/fr/blog/quatre-jours/lukarne-plein-ecran.webp&quot; alt=&quot;Plein écran sur une caméra d’autoroute en Californie, une voiture rouge au carrefour, le nom du lieu et l’heure locale en bas&quot; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;La mosaïque d’un lieu, puis le plein écran. Gauche/droite passe à la caméra voisine sans repasser par la mosaïque.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/fr/blog/quatre-jours/lukarne-aurore.webp&quot; alt=&quot;Une aurore boréale verte au-dessus de Fairbanks, en Alaska, avec le nom de la caméra et l’heure locale&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;« surprise me » est tombé là-dessus, en plein tournage. La caméra aurore d’explore.org à Fairbanks, 15 h 09 heure locale : sans doute une rediffusion de la nuit entre deux plans en direct.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Puis je l’ai utilisée, et je me suis plaint. Samedi, 23 h 20 : « je suis assez surpris du peu de flux video , bcp d’images fixes qui apportent peu ». 23 h 34, commit : le direct d’abord, les images fixes en option. Quatorze minutes.&lt;/p&gt;
&lt;p&gt;Dans la nuit de dimanche à lundi, minuit huit : « parfait , je vais dormir là ». Et deux demandes : « les vignettes pixelisée : on peut trouver un moyen de charger ne serait ce qu 1 frame propre plutôt que la vignette degeueu ? », et pour le dernier niveau de zoom, un « effet plongeant genre zoom google earth ». À 0 h 26, les vignettes sont des vraies images capturées sur l’appareil pendant la lecture. À 0 h 48, on plonge sur un lieu avec un zoom ×30, les lumières s’allument, et on remonte. À 0 h 59, test d’endurance de 10 minutes après tout ça. Je dormais.&lt;/p&gt;
&lt;p&gt;Lundi après-midi, j’ai demandé un mode démo, pour que la télé voyage toute seule. 41 minutes plus tard :&lt;/p&gt;
&lt;figure class=&quot;video&quot;&gt;&lt;video autoplay loop muted playsinline preload=&quot;metadata&quot; poster=&quot;lukarne-demo-poster.jpg&quot;&gt;&lt;source src=&quot;lukarne-demo.mp4&quot; type=&quot;video/mp4&quot;&gt;&lt;/video&gt;&lt;figcaption&gt;Le mode démo : des caméras au hasard, là où il fait jour, jamais deux fois la même région, avec un fondu sur un carton-titre entre deux.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/fr/blog/quatre-jours/lukarne-reglages.webp&quot; alt=&quot;Le panneau de réglages de Lukarne ouvert sur le globe : langue, température, images fixes, lecteur en coin, durée par caméra du mode démo, crédits des sources&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Les réglages, au bout de la télécommande : langue (anglais, français, espagnol), unités, images fixes ou direct seulement, lecteur en coin, durée par caméra du mode démo, et les crédits de chaque source.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Ce qui manque encore : la couverture en direct hors Amérique du Nord reste mince. Le premier jour, l’agent a brûlé tout le quota de recherche YouTube pendant que deux bugs cachaient les résultats. Aujourd’hui, un dépôt public republie chaque jour le catalogue des directs, et l’app le télécharge derrière son logo. Et il y a un bug que je ne sais pas toujours reproduire : une webcam YouTube qui reste figée quand on en enchaîne deux. Je l’ai signalé à 15 h 46, il y a eu un correctif à 16 h 31. Reste à le vérifier sur la télé.&lt;/p&gt;
&lt;h2 id=&quot;dropbokstv-un-dimanche-apres-midi&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#dropbokstv-un-dimanche-apres-midi&quot; aria-label=&quot;Anchor link for: dropbokstv-un-dimanche-apres-midi&quot;&gt;#&lt;/a&gt;
DropboksTV, un dimanche après-midi&lt;/h2&gt;
&lt;p&gt;Un client Dropbox pour la télé, pensé pour le canapé : photos et vidéos d’abord, lecture seule, pas de backend. Dimanche, 15 h 18 : « read prompt-dropbokstv-claude-code.md and go ». 16 h 02 : l’appairage par téléphone marche sur la télé. 18 h 28 : huitième et dernière tranche, avec la recherche, le mode hors ligne, l’écran de veille et 128 textes traduits par langue.&lt;/p&gt;
&lt;p&gt;Le morceau le plus tordu, c’est l’appairage. Se connecter à Dropbox depuis une télé, sans clavier, ça devrait être un code à taper sur le téléphone. Sauf que l’API de Dropbox ne propose pas ce flux. L’agent l’a vérifié dans le SDK et l’a consigné dans son journal de décisions. Il a donc monté autre chose :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;la télé affiche un QR code qui pointe vers une page servie par la télé elle-même, sur le Wi-Fi de la maison ;&lt;/li&gt;
&lt;li&gt;le téléphone ouvre cette page, qui l’envoie autoriser DropboksTV chez Dropbox ;&lt;/li&gt;
&lt;li&gt;Dropbox affiche un code, on le colle sur le téléphone, et la télé l’échange contre un jeton (PKCE : le secret ne quitte jamais la télé, pas de backend) ;&lt;/li&gt;
&lt;li&gt;la session expire au bout de 10 minutes et la télé renouvelle le QR toute seule. Une faute de frappe ne grille pas la session. Et si le téléphone ne voit pas la télé, un bouton bascule sur le lien Dropbox direct.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le petit serveur HTTP de la télé (150 lignes), il l’a repris d’Almanak.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/fr/blog/quatre-jours/dropbokstv-appairage.webp&quot; alt=&quot;Écran d’appairage de DropboksTV : trois étapes numérotées et un QR code, flouté ici&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;L’appairage&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Et puis il y a le lecteur. En trois tranches, entre 16 h 25 et 18 h 07, l’agent a écrit une visionneuse qui ouvre à peu près tout :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;les photos, avec un zoom ×2 et ×3 qu’on déplace aux flèches, et un diaporama en fondu ou en Ken Burns ;&lt;/li&gt;
&lt;li&gt;les vidéos : H.264, HEVC jusqu’en 4K, reprise là où on s’était arrêté, sous-titres &lt;code&gt;.srt&lt;/code&gt; ;&lt;/li&gt;
&lt;li&gt;les PDF, page par page, avec zoom ;&lt;/li&gt;
&lt;li&gt;Word et PowerPoint, que Dropbox convertit en PDF, et les tableurs, mis à l’échelle à 170 % pour être lisibles depuis le canapé ;&lt;/li&gt;
&lt;li&gt;le Markdown mis en forme, le code avec numéros de ligne et coloration ;&lt;/li&gt;
&lt;li&gt;la musique, MP3 et FLAC, avec la pochette ;&lt;/li&gt;
&lt;li&gt;et pour tout le reste, un QR code qui envoie le fichier sur le téléphone.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le lecteur vidéo a pris 16 minutes. Les documents, la musique et l’envoi vers le téléphone, 27. La folie.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/fr/blog/quatre-jours/dropbokstv-formats.webp&quot; alt=&quot;Huit écrans de DropboksTV : une photo zoomée, une vidéo, un PDF, un tableur, des notes Markdown, du code Kotlin, un morceau de musique avec sa pochette et l’écran d’envoi vers le téléphone&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Dans l’ordre : photo zoomée, vidéo, PDF, tableur, Markdown, code, musique, envoi vers le téléphone (QR flouté). Les fichiers de test, c’est l’agent qui les a fabriqués et déposés dans un dossier Dropbox dédié. Captures prises pendant le développement.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;kronograf-ou-le-retour-du-materiel&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#kronograf-ou-le-retour-du-materiel&quot; aria-label=&quot;Anchor link for: kronograf-ou-le-retour-du-materiel&quot;&gt;#&lt;/a&gt;
Kronograf, ou le retour du matériel&lt;/h2&gt;
&lt;p&gt;Lundi, 3 h 19 du matin, une montre LilyGO T-Watch de 2020 branchée en USB, et une mission : un cadran de chronographe d’aviation, et des afficheurs 7 segments « circuits temporels ». À 3 h 48, premier flash vérifié sur la montre, après une sauvegarde complète de la flash d’origine. C’était la règle n° 2 du prompt : on n’écrit rien sur la montre sans filet. Le câble a lâché pendant la première sauvegarde, et l’agent a refusé de flasher tant qu’il n’en avait pas une complète.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/fr/blog/quatre-jours/kronograf-photo.webp&quot; alt=&quot;La vraie T-Watch 2020 posée sur la table : écran de diagnostic Kronograf, mémoire, capteurs détectés en vert, batterie à 100 %&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;La vraie montre, lundi à 18 h 11 : l’écran de diagnostic, le premier écran flashé. En vert, les puces détectées ; en gris, ce que la V1 n’a pas.&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;duo&quot;&gt;&lt;img src=&quot;kronograf-t1-aviator.webp&quot; alt=&quot;Cadran chronographe d&#39;aviation : lunette graduée, trois sous-cadrans batterie, minutes et pas, trotteuse orange&quot;&gt;&lt;img class=&quot;carre&quot; src=&quot;kronograf-t2-face1.webp&quot; alt=&quot;Cadran à afficheurs 7 segments : destination en rouge, présent en vert, dernière synchro en orange&quot;&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;À gauche, capture de la vraie montre par le port série. À droite, le cadran « circuits temporels » dans le simulateur, qui compile exactement la même interface.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Là, le délai remonte, et ce n’est pas la faute de l’agent. Ce n’est plus du code contre un émulateur, c’est un connecteur micro-USB fatigué. Mes messages de la matinée : « elle plante systematiquement au bout de quelque secondes », « va vite sinon elle plante », « j’espère qu’elle n’est pas brikée ». Le problème venait du sommeil léger, avec une tempête d’interruptions au réveil par le bouton. À 11 h 17 : « ce semble tres bien tenir maintenant ». À 11 h 18 : « go pour la météo ». À 11 h 31, la montre affiche la météo et le prochain bus en temps réel.&lt;/p&gt;
&lt;p&gt;Ce soir, elle a 33 commits, des alarmes, un chrono, un podomètre, des mises à jour par Wi-Fi et les notifications de l’iPhone en Bluetooth, qui accrochent encore mal. Le double tapotement est « un peu alétoire , comme le mouvement du poignet … mais ca passe ».&lt;/p&gt;
&lt;h2 id=&quot;le-homelab-et-l-app-shot2cart&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#le-homelab-et-l-app-shot2cart&quot; aria-label=&quot;Anchor link for: le-homelab-et-l-app-shot2cart&quot;&gt;#&lt;/a&gt;
Le homelab, et l’app shot2cart&lt;/h2&gt;
&lt;p&gt;Le chantier le plus long ne se voit pas. Soixante commits et 276 messages pour remettre à plat le serveur de la maison : état des lieux, sauvegardes avec de vrais tests de restauration, accès à distance par passkeys pour la famille, alertes, une page d’accueil avec un schéma animé de l’infra et un autre du réseau local.&lt;/p&gt;
&lt;p&gt;Mon message préféré de toute la série, le premier soir : « ignore cet audit ; je ne compte que sur toi maintenant ». Le plus honnête, dimanche : « Nest dans Home Assistant c’est payant ??!! ». Et ouais, 5 $, merci Google.&lt;/p&gt;
&lt;p&gt;shot2cart, c’est le contre-exemple. Le pitch de l’app, c’est une photo d’une liste de courses, et le panier s’ouvre chez le marchand en moins de 15 secondes, sans rien taper. Première tentative en septembre, avec un autre outil et un workflow de spécifications : trois jours, deux commits. Jeudi soir, je l’ai relancée sur cette base. Dimanche, 20 h 13, j’ai tout archivé : « forget about previous session, I archived the previous project and to start a new one with better prompt ». 23 h 01 : photo, analyse par un modèle de vision, liens marchands article par article. Minuit quatre : « I’m going to sleep […] please go as far as you can in full autonomy ». Le matin, il y avait la détection en streaming, des fiches de relecture et des parcours de test automatisés.&lt;/p&gt;
&lt;p&gt;Même idée, même développeur. La différence entre septembre et ce dimanche, c’est la qualité des modèles, et notamment Opus 5.5. Une bête !!!&lt;/p&gt;
&lt;h2 id=&quot;le-delai-mesure&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#le-delai-mesure&quot; aria-label=&quot;Anchor link for: le-delai-mesure&quot;&gt;#&lt;/a&gt;
Le délai, mesuré&lt;/h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Type&lt;/th&gt;&lt;th&gt;Ce que j’ai demandé&lt;/th&gt;&lt;th&gt;Délai jusqu’au commit&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;FEAT&lt;/td&gt;&lt;td&gt;« ajoute la langue espagnole à l’application » (Lukarne)&lt;/td&gt;&lt;td&gt;5 min&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;BUG&lt;/td&gt;&lt;td&gt;Les voisins à 2 500 km : « je suis sur Londres mais la Patagonie est listée »&lt;/td&gt;&lt;td&gt;5 min&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;BUG&lt;/td&gt;&lt;td&gt;Le logo qui n’apparaît qu’une demi-seconde au lancement&lt;/td&gt;&lt;td&gt;2 min&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;FEAT&lt;/td&gt;&lt;td&gt;Plus de direct, moins d’images fixes&lt;/td&gt;&lt;td&gt;14 min&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;FEAT&lt;/td&gt;&lt;td&gt;Le zoom plongeant « genre Google Earth », demandé avant d’aller dormir&lt;/td&gt;&lt;td&gt;40 min&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;FEAT&lt;/td&gt;&lt;td&gt;Un mode démo qui enchaîne les webcams au hasard&lt;/td&gt;&lt;td&gt;41 min&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;BUG&lt;/td&gt;&lt;td&gt;La vidéo récente qui ouvre toujours une vidéo de 2012 (DropboksTV)&lt;/td&gt;&lt;td&gt;2 min&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;FEAT&lt;/td&gt;&lt;td&gt;« go pour la météo » sur la montre&lt;/td&gt;&lt;td&gt;13 min&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Ce qui ne va pas plus vite : un câble micro-USB qui se débranche, un quota YouTube qui se remet à zéro à 3 h du matin, une clé d’API que le portail du fournisseur refuse, mon abonnement. Dimanche, 16 h 46 : « il me reste que 10% pour notre session en cours ». Et mon sommeil, que j’ai fini par intégrer au plan : « je vais dormir la , avance au maximum de ton autonomie ».&lt;/p&gt;
&lt;p&gt;Ce que je ne sais pas encore : combien de ces six projets j’utiliserai encore dans un mois. Faut aller au bout maintenant, publier ce qui est publiable, alimenter d’autres agents.&lt;/p&gt;
&lt;p&gt;Ce billet a été fait pareil. Un message de ma part lundi en fin d’après-midi, en vrac, avec mes fautes de frappe : « génère moi un article complet […] mettre en avant le délai ridicule que c’est devenu entre l’intention et le concret ». Reprends tout en transparence et honnêteté. Le blog lui-même était en ligne 45 minutes après mon premier message, lundi à 4 h du matin.&lt;/p&gt;
&lt;p&gt;En bref, une bonne fin de semaine !&lt;/p&gt;
&lt;aside class=&quot;transparence&quot; aria-label=&quot;Comment ce billet a été écrit&quot;&gt;
  &lt;p class=&quot;transparence-titre&quot;&gt;Comment ce billet a été écrit&lt;/p&gt;
  &lt;p&gt;Rédigé par Claude Opus 5.5 à partir de : une consigne de François en un message, ses sessions Claude Code (environ 240 messages), six dépôts Git (159 commits), leurs &lt;code&gt;DECISIONS.md&lt;/code&gt; et captures, des vidéos tournées par l’agent sur la Shield, une photo de la montre.&lt;/p&gt;
  &lt;p&gt;Relu et corrigé par François, 19 % du texte réécrit.&lt;/p&gt;
&lt;/aside&gt;
</content>
    </entry>
</feed>
