<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>storagebits · #agents</title>
    <subtitle>Firmware, TV apps, a homelab, built at night with an organization of AI agents. And blok, the workshop notebook.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://blok.storagebits.workers.dev/tags/agents/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/tags/agents/atom.xml</id>
    <entry xml:lang="en">
        <title>Testing the NUCLEO-F412ZG with Hubris and 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/blog/hubris-f412/"/>
        <id>https://blok.storagebits.workers.dev/blog/hubris-f412/</id>
        <summary type="html">Porting Hubris, Oxide Computer&#39;s embedded OS, to a NUCLEO-F412ZG at 100 MHz: what had to change, from the untouched kernel to the two Humility fixes, explained.</summary>
        <content type="html" xml:base="https://blok.storagebits.workers.dev/blog/hubris-f412/">&lt;p&gt;The setup: a NUCLEO-F412ZG, an ST development board with a Cortex-M4, and &lt;a class=&quot;external&quot; rel=&quot;external&quot; href=&quot;https://github.com/oxidecomputer/hubris&quot;&gt;Hubris&lt;/a&gt;, Oxide Computer’s embedded OS, which doesn’t support it out of the box. The kernel didn’t change by a single line. The board had to be added, three drivers adapted, the probe firmware updated, and Humility, the debugger, fixed twice.&lt;/p&gt;
&lt;figure class=&quot;portrait&quot;&gt;&lt;img src=&quot;nucleo-f412zg.webp&quot; alt=&quot;The NUCLEO-F412ZG board on a black mat, plugged in over USB, with its green, blue and red user LEDs lit.&quot; width=&quot;1125&quot; height=&quot;1500&quot; loading=&quot;lazy&quot;&gt;&lt;figcaption&gt;The NUCLEO-F412ZG, powered through the USB of the ST-LINK, the probe built into the bottom of the board. Above it, the three user LEDs driven by the pong task.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&quot;hubris-humility-what-are-they-who-s-behind-them&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hubris-humility-what-are-they-who-s-behind-them&quot; aria-label=&quot;Anchor link for: hubris-humility-what-are-they-who-s-behind-them&quot;&gt;#&lt;/a&gt;
Hubris, Humility: what are they? Who’s behind them?&lt;/h2&gt;
&lt;p&gt;Hubris is a small OS written in Rust that Oxide uses in its servers for the service processor. Each task lives in its own memory regions, protected by the MPU. Tasks, their priorities and their access to peripherals are all declared at build time, in an &lt;code&gt;app.toml&lt;/code&gt;. Humility reads the board’s memory through the probe and decodes it using the debug info: task states, stacks, registers, dumps.&lt;/p&gt;
&lt;p&gt;The main author is Cliff Biffle. His talk at the Open Source Firmware Conference 2021 explains why Oxide wrote its own OS. This talk is truly amazing, what a beautiful piece of engineering!&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;Bryan Cantrill, co-founder and CTO of Oxide, and Adam Leventhal also deserve a mention among the big names behind Hubris. In his talk on debugging Docker in production, at DockerCon 2015, Cantrill makes a simple case: a crashed system doesn’t get rebooted before it has been debugged, either live or from an image of its memory, the core dump. Humility does both: &lt;code&gt;humility tasks&lt;/code&gt; reads the running board, and &lt;code&gt;humility dump&lt;/code&gt; saves its full state to a file you examine later, with the board unplugged.&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 took down the original video in 2018; this is the copy Cantrill reposted.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&quot;what-had-to-be-patched&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-had-to-be-patched&quot; aria-label=&quot;Anchor link for: what-had-to-be-patched&quot;&gt;#&lt;/a&gt;
What had to be patched&lt;/h2&gt;
&lt;p&gt;I paste a mission prompt into Claude: get Hubris running on this board, plugged into the Mac over USB. The closest target in the repository is a Discovery board with an STM32F407. That same evening, Hubris runs at 100 MHz on the NUCLEO.&lt;/p&gt;
&lt;p&gt;What was patched, layer by layer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The kernel&lt;/strong&gt;: nothing, not one line.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The board and the app&lt;/strong&gt;: pure additions. A 2-line board file (the chip name for probe-rs), a chip description (1 MiB of flash, 256 KiB of RAM, peripheral addresses), and the app itself, with the 100 MHz clock configuration. The branch adds 621 lines, documentation included.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Three shared drivers&lt;/strong&gt;, about 150 lines: &lt;code&gt;stm32fx-rcc&lt;/code&gt; selects the F412 variant of the register library, &lt;code&gt;user-leds&lt;/code&gt; drives the Nucleo’s LEDs on PB0, PB7 and PB14, and &lt;code&gt;stm32fx-usart&lt;/code&gt; moves to USART3 (PD8/PD9), the ST-LINK’s virtual serial port, clocked at 50 MHz. The tricky part: two chip variants of the &lt;code&gt;stm32f4&lt;/code&gt; crate can’t coexist in the same task, and two of these drivers forced the F407 in their &lt;code&gt;Cargo.toml&lt;/code&gt;. That choice became a feature. The Discovery still builds identically, rechecked at every step.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The probe&lt;/strong&gt;: the ST-LINK firmware was too old and had to be updated.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Humility&lt;/strong&gt;, the debugger: two fixes, detailed below. The first one repairs Humility against the latest Hubris, whatever the board. The second fixes a wrong measurement that shows up on this board.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-mission-in-slices&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-mission-in-slices&quot; aria-label=&quot;Anchor link for: the-mission-in-slices&quot;&gt;#&lt;/a&gt;
The mission, in slices&lt;/h2&gt;
&lt;p&gt;The prompt’s north star fits in one sentence: “&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.” [&lt;code&gt;humility tasks&lt;/code&gt; shows every task &lt;code&gt;Healthy&lt;/code&gt; on the NUCLEO-F412ZG, the user LEDs blink under &lt;code&gt;pong&lt;/code&gt;’s control, and &lt;code&gt;ping&lt;/code&gt; writes to the ST-LINK’s virtual COM port.] Five slices, each one has to end on hardware that works: preflight, reference build, first boot on the 16 MHz internal oscillator, UART, then 100 MHz.&lt;/p&gt;
&lt;p&gt;First stop in slice 0. The board’s ST-LINK firmware is V2J25; probe-rs, which Humility builds on, requires J26. The prompt said to stop and ask. Claude offers to patch it automatically, I give it the zip downloaded from ST’s site, and that’s it.&lt;/p&gt;
&lt;p&gt;Every task is &lt;code&gt;Healthy&lt;/code&gt;. Claude can’t see the LEDs: it reads GPIOB’s output register several times in a row and sees it cycle between PB14, PB0 and nothing, in time with &lt;code&gt;pong&lt;/code&gt;. The board runs at 100 MHz. Over 10 seconds, the Hubris clock counts 10,235 ms against 10.32 s of wall time, the gap being Humility’s connection latency.&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;Clock tree of the NUCLEO-F412ZG at 100 MHz, scrollable horizontally&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;Clock tree of the NUCLEO-F412ZG at 100 MHz&lt;/title&gt;&lt;desc id=&quot;horloge-d&quot;&gt;The ST-LINK MCO supplies 8 MHz to the HSE bypass input. PLLM 4 gives 2 MHz, PLLN 100 gives a 200 MHz VCO, PLLP 2 gives a 100 MHz SYSCLK. AHB at 100 MHz, APB1 at 50 MHz for USART3, APB2 at 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;square wave, not a crystal&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 before 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;VCO input 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;core, 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 baud&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 at 2.7–3.6 V) · regulator in 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;Read back on the board: RCC_PLLCFGR = 0x28401904 · RCC_CFGR = 0x100a · FLASH_ACR = 0x703&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;Slice 4: from 16 MHz on the HSI to 100 MHz. Every constant is justified by a section of RM0402 in the code, and read back from the registers once flashed.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&quot;where-it-broke-savedstate&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#where-it-broke-savedstate&quot; aria-label=&quot;Anchor link for: where-it-broke-savedstate&quot;&gt;#&lt;/a&gt;
Where it broke: SavedState&lt;/h2&gt;
&lt;p&gt;Right before the first boot, &lt;code&gt;humility tasks&lt;/code&gt; fails: “expected structure SavedState not found”.&lt;/p&gt;
&lt;p&gt;To understand why, you need to know how Humility reads a task. When the kernel interrupts a task, it stores its registers (r4 to r11, the stack pointer, and so on) in a structure, the &lt;code&gt;save&lt;/code&gt; field of that task’s &lt;code&gt;Task&lt;/code&gt; entry. Humility reads the task table straight from the board’s RAM. To know at which byte each register sits, it needs the layout of that structure, which it finds in the debug info (DWARF) of the kernel binary. And it looked the structure up by name: &lt;code&gt;SavedState&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;On October 9, four days earlier, Hubris PR #2704 introduced an &lt;code&gt;Arch&lt;/code&gt; trait so the kernel can target several architectures. The ARMv7-M structure was renamed &lt;code&gt;ArmMSavedState&lt;/code&gt;. Humility can no longer find it, and neither &lt;code&gt;humility tasks&lt;/code&gt; nor reading a task’s registers works, whatever the board.&lt;/p&gt;
&lt;p&gt;Two ways out. Claude first pins the branch right before that commit and keeps going, which is enough for this board. It’s in DECISIONS.md, and it unblocks things. Then the fix, on the &lt;code&gt;savedstate-by-member&lt;/code&gt; branch of my Humility fork: instead of looking the type up by name, Humility takes the type of the &lt;code&gt;save&lt;/code&gt; field of &lt;code&gt;Task&lt;/code&gt;, which DWARF provides directly. The name can change; the field stays. Eleven lines added, four removed, in the two places that read this structure: &lt;code&gt;humility tasks&lt;/code&gt; and reading the registers. The fix works with both the old kernel and the new one, tested on the board in both cases.&lt;/p&gt;
&lt;h2 id=&quot;the-second-fix-stackmargin-showed-zeros&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-second-fix-stackmargin-showed-zeros&quot; aria-label=&quot;Anchor link for: the-second-fix-stackmargin-showed-zeros&quot;&gt;#&lt;/a&gt;
The second fix: stackmargin showed zeros&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;humility stackmargin&lt;/code&gt; tells you, for each task, how much stack it has never used. The idea: when a task starts, the kernel fills its whole stack with a pattern, &lt;code&gt;0xbaddcafe&lt;/code&gt;. Humility reads the stack from the bottom and counts the words still intact: that’s the margin.&lt;/p&gt;
&lt;p&gt;On the board, it shows a margin of 0 for &lt;code&gt;jefe&lt;/code&gt;, the supervisor, with a 152-byte stack, while its &lt;code&gt;app.toml&lt;/code&gt; declares 2200.&lt;/p&gt;
&lt;p&gt;A Cortex-M4’s MPU only knows regions whose size is a power of 2. Since April (PR #2458), Hubris splits a task’s RAM into several contiguous regions to waste less: &lt;code&gt;jefe&lt;/code&gt;’s 2560 bytes become 1 KiB + 1 KiB + 512 bytes instead of a single 4 KiB region. A stack can therefore span several regions. The kernel was fixed in May (PR #2492) to paint all of it. Humility, though, only read the region holding the top of the stack: here the last one, which holds only 152 bytes of stack, all of them used. Hence the margin of 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;The jefe stack as seen by humility stackmargin, before and after the fix, scrollable horizontally&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;The jefe stack as seen by humility stackmargin, before and after the fix&lt;/title&gt;&lt;desc id=&quot;pile-d&quot;&gt;jefe&#39;s RAM is 2560 bytes split into three contiguous MPU regions. Its 2200-byte stack starts at 0x20001400 and its initial top is 0x20001C98, in the third region. Before the fix, Humility only read that third region: 152 bytes, all used, hence a margin of 0. After the fix, it reads all 2200 bytes, finds 1928 bytes still at 0xbaddcafe and a margin of 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 B never touched&lt;/text&gt;&lt;text x=&quot;665&quot; y=&quot;81&quot; class=&quot;sys-dans&quot; text-anchor=&quot;middle&quot;&gt;data&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;region 1 · 1 KiB&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;region 2 · 1 KiB&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;region 3 · 512 B&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;initial top 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 B used&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;before&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;reads 152 B, stops at the 1st word → MARGIN 0&lt;/text&gt;&lt;text x=&quot;10&quot; y=&quot;219&quot; class=&quot;sys-serie sys-sain-txt&quot;&gt;after&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;reads 2200 B from the base, counts 1928 B of 0xbaddcafe → MARGIN 1928&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;Read on the board (humility map, readmem). Humility only looked at the region holding the top of the stack. The kernel, for its part, paints the whole stack with 0xbaddcafe across all three regions.&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;before jefe 0x20001c00       152       152       0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;after  jefe 0x20001400      2200       272    1928&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The fix, on the &lt;code&gt;stackmargin-multi-region&lt;/code&gt; branch, walks down the contiguous regions that belong to the same task, as the kernel does, before measuring. Humility’s regression test confirmed it too: on a dump from a Sidecar, a real Oxide switch, the expected snapshot contained three bogus “MARGIN 0”. After the fix, every stack size matches, to the byte, the one declared in that Sidecar’s &lt;code&gt;app.toml&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;where-i-m-at&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#where-i-m-at&quot; aria-label=&quot;Anchor link for: where-i-m-at&quot;&gt;#&lt;/a&gt;
Where I’m at&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;nucleo-f412zg&lt;/code&gt; branch has five commits, with a DECISIONS.md and a README. It’s pinned before PR #2704; with the first fix, Humility also keeps up with the recent kernel. Both fixes are on my Humility fork. The kernel test suite on the F412, the optional slice 5, isn’t done. And “all &lt;code&gt;Healthy&lt;/code&gt;” is only half true: &lt;code&gt;ping&lt;/code&gt; deliberately faults every 100 replies from &lt;code&gt;pong&lt;/code&gt;, as in Oxide’s demos.&lt;/p&gt;
&lt;p&gt;A lead for later, not done yet: an I2C or SPI sensor behind a driver task. In Hubris, a driver is a task like any other: it alone has access to the peripheral’s registers, declared in the &lt;code&gt;app.toml&lt;/code&gt;, and the other tasks talk to it through messages. The repository’s I2C driver targets the STM32H7 and G0 (plus a separate driver for the LPC55), SPI only the STM32H7 and the LPC55: nothing for the F4 family, it would have to be written for this board.&lt;/p&gt;
&lt;p&gt;To be continued…&lt;/p&gt;
&lt;aside class=&quot;transparence&quot; aria-label=&quot;How this post was written&quot;&gt;
  &lt;p class=&quot;transparence-titre&quot;&gt;How this post was written&lt;/p&gt;
  &lt;p&gt;Drafted by Claude Opus 5.5 from: a mission prompt, about ten messages and an interview written by François, his Claude Code session, the &lt;code&gt;hubris&lt;/code&gt; repository (5 commits on the &lt;code&gt;nucleo-f412zg&lt;/code&gt; branch), two Humility branches (3 commits), the &lt;code&gt;humility&lt;/code&gt; outputs and the registers read back on the board.&lt;/p&gt;
  &lt;p&gt;Proofread and corrected by François in 20 min, 6 % of the text rewritten.&lt;/p&gt;
  &lt;p&gt;Translated from French by the AI, from the proofread version. The translation was not reviewed line by line.&lt;/p&gt;
&lt;/aside&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>A 2020 watch, a complete firmware, three days</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/blog/ressusciter-kronograf/"/>
        <id>https://blok.storagebits.workers.dev/blog/ressusciter-kronograf/</id>
        <summary type="html">A 2020 T-Watch was sitting in a corner: developing for it took too much effort. In three days, with an agent, it got a firmware written from scratch and released under the MIT license.</summary>
        <content type="html" xml:base="https://blok.storagebits.workers.dev/blog/ressusciter-kronograf/">&lt;p&gt;I bought a LilyGO T-Watch 2020 six years ago, because it was cheap and ideal for testing and tinkering. Developing for it, on the other hand, took a real investment: C/C++, a framework to learn, some embedded know-how. It ended up in a corner, with other unfinished side projects.&lt;/p&gt;
&lt;p&gt;With AI, all those brakes come off: within a few minutes, I had a complete firmware, written from scratch. Without AI, I would not have taken it out of the drawer: building anything on it took too much effort.&lt;/p&gt;
&lt;p&gt;What devices are sleeping at your place? Imagine what you could make of them, in record time.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://blok.storagebits.workers.dev/blog/quatre-jours/&quot;&gt;post about the four days&lt;/a&gt; already told the story of the first flash. Here, I stay with the watch.&lt;/p&gt;
&lt;h2 id=&quot;what-i-wanted&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-i-wanted&quot; aria-label=&quot;Anchor link for: what-i-wanted&quot;&gt;#&lt;/a&gt;
What I wanted&lt;/h2&gt;
&lt;p&gt;An aviation chronograph dial and 7-segment displays in the style of “time circuits”. Then the weather, the buses and the iPhone notifications. The full list of apps is in the repository’s README.&lt;/p&gt;
&lt;p&gt;My method comes down to two moves. I simply lay out the features I want. Then I immediately ask for a first end-to-end version, to get a short feedback loop. The result becomes convincing very quickly.&lt;/p&gt;
&lt;p&gt;I steered the development in natural language, by stating intentions. Reading the generated code, I also learned a lot, about the language as much as about the frameworks and embedded concepts. The teaching value is huge!&lt;/p&gt;
&lt;h2 id=&quot;the-beginning&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-beginning&quot; aria-label=&quot;Anchor link for: the-beginning&quot;&gt;#&lt;/a&gt;
The beginning&lt;/h2&gt;
&lt;p&gt;First commit at 3:22 am. The mission prompt set two rules: back up the original 16 MB of flash before any write, and never touch the eFuses or the core voltage.&lt;/p&gt;
&lt;p&gt;The backup nearly failed. At 921,600 baud, the USB port vanished in the middle of a read. The agent started over at 230,400 baud, in 1 MB blocks checked one by one, because above that the cable introduced errors. At 3:46 am, the full backup was verified and its SHA-256 hash recorded. At 3:48 am, the first verified flash on the watch.&lt;/p&gt;
&lt;p&gt;I was a bit afraid of bricking the watch. I didn’t know where the fault came from; probably the cable. The agent took care of everything, down to trying a slow, chunked flash to work around that faulty cable (!). Its questions and its methods were relevant and precise, and that is what gave me confidence.&lt;/p&gt;
&lt;p&gt;At 4:09 am, the aviator dial was on screen, with a second hand ticking at 8 Hz.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/blog/ressusciter-kronograf/kronograf-t1-aviator.webp&quot; alt=&quot;The aviation chronograph dial, captured from the real watch over the serial port: graduated bezel, three sub-dials for battery, minutes and steps, orange second hand&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The aviator dial, captured on the real watch over the serial port. Capture already published in the previous post.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-moment-it-broke&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-moment-it-broke&quot; aria-label=&quot;Anchor link for: the-moment-it-broke&quot;&gt;#&lt;/a&gt;
The moment it broke&lt;/h2&gt;
&lt;p&gt;Plugged into USB, the watch held up. On battery, it crashed every single time after a few seconds. During the morning, I feared it was bricked, and I was pushing the agent to hurry before the next crash. I had to power it off and on again three times.&lt;/p&gt;
&lt;p&gt;There were two causes, found one after the other. First, the processor’s watchdog stayed armed during light sleep. It is now suspended before going to sleep and re-armed on wake-up. Second, a press on the button froze the watch. The power chip’s interrupt line stays low until it is read, and the interrupt kept firing endlessly. GPIO interrupts are now disabled during sleep, and I saw that the watch held up very well.&lt;/p&gt;
&lt;h2 id=&quot;a-decision-stop-depending-on-the-cable&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-decision-stop-depending-on-the-cable&quot; aria-label=&quot;Anchor link for: a-decision-stop-depending-on-the-cable&quot;&gt;#&lt;/a&gt;
A decision: stop depending on the cable&lt;/h2&gt;
&lt;p&gt;The USB drop-outs kept coming back during flashes. There were two options: wait for the port to come back, or make the cable optional. The agent proposed a “workshop mode”: when the watch is powered over USB and Wi-Fi is configured, it stays connected, accepts over-the-air updates (OTA) and exposes a TCP console. Wi-Fi left on all the time is normally an anti-pattern. On mains power, the battery is not at stake, and the option can be turned off.&lt;/p&gt;
&lt;p&gt;Since the cable kept failing, the agent switched to Wi-Fi updates (OTA) on its own. Magic! Later, the drop-outs also showed up while the processor was in its ROM bootloader: so the problem is hardware, not software. A safe mode followed: after three crashes in a row within the first minute, Bluetooth does not start, so the watch can always be repaired remotely.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/blog/ressusciter-kronograf/kronograf-photo.webp&quot; alt=&quot;The real T-Watch 2020 lying on the table: Kronograf diagnostic screen, memory, sensors detected in green, battery at 100%&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The real watch on the diagnostic screen, the first screen flashed. Photo already published in the previous post.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;where-i-stand&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#where-i-stand&quot; aria-label=&quot;Anchor link for: where-i-stand&quot;&gt;#&lt;/a&gt;
Where I stand&lt;/h2&gt;
&lt;p&gt;The cable problem (B2 in &lt;code&gt;BLOCKERS.md&lt;/code&gt;) is still open: the micro-USB connection remains fragile. The double tap that wakes the screen is still a bit hit-and-miss, but it works. The wrist gesture to wake the watch still needs tuning. Battery life over a full day has not been measured yet.&lt;/p&gt;
&lt;p&gt;What impressed me most is how quickly the agent flashed the watch on its own and showed me concrete results. I can’t wait to try it on other devices. In fact, I’ll soon be taking my micro:bits out again: I have an idea for a project!&lt;/p&gt;
&lt;aside class=&quot;transparence&quot; aria-label=&quot;How this post was written&quot;&gt;
  &lt;p class=&quot;transparence-titre&quot;&gt;How this post was written&lt;/p&gt;
  &lt;p&gt;Drafted by Claude Opus 5.5 from: a brief and an interview written by François, his session messages, the &lt;code&gt;kronograf&lt;/code&gt; repository, &lt;code&gt;DECISIONS.md&lt;/code&gt; and &lt;code&gt;BLOCKERS.md&lt;/code&gt;.&lt;/p&gt;
  &lt;p&gt;Proofread and corrected by François in 30 min, 17 % of the text rewritten.&lt;/p&gt;
  &lt;p&gt;Translated from French by the AI, from the proofread version. The translation was not reviewed line by line.&lt;/p&gt;
&lt;/aside&gt;
</content>
    </entry>
    <entry xml:lang="en">
        <title>Twelve minutes from idea to TV</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/blog/quatre-jours/"/>
        <id>https://blok.storagebits.workers.dev/blog/quatre-jours/</id>
        <summary type="html">Four days, six projects, 159 commits. The gap between “I&#39;d like that” and “it runs on the device” has become ridiculous. What still slows things down is me, my cables and my quotas.</summary>
        <content type="html" xml:base="https://blok.storagebits.workers.dev/blog/quatre-jours/">&lt;p&gt;Thursday, October 8, 8:34 pm. I send a mission prompt to an agent. At 8:46 pm the time shows up, huge, on my TV, in a typeface the agent picked.&lt;/p&gt;
&lt;p&gt;Four days later: six projects, 159 commits, some 240 messages from me, a good share of them under five words. “go T8”. “vas-y présente la tranche 3” [go ahead, show me slice 3]. “c’est fait, reteste” [done, test again].&lt;/p&gt;
&lt;p&gt;The gap between intent and something real has become ridiculous. Twelve minutes for the Almanak app. Sixteen for Lukarne. The bottleneck now is me: my sleep, my cables, my 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;Timeline, scrolls horizontally&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;Four days of commits, project by project&lt;/title&gt;&lt;desc id=&quot;frise-desc&quot;&gt;One row per project, from Thursday 8 October 7 pm to Monday 12 October 7 pm. Each tick is a commit. The hollow circle marks the first message to the agent; the filled circle, raised above the row, the first concrete result. Nights are shaded.&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;Fri 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;Sat 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;Sun 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;Mon 12&lt;/text&gt;&lt;text class=&quot;axe&quot; x=&quot;118.0&quot; y=&quot;28&quot;&gt;Thu 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;Thu 8:34 pm · mission prompt&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;8:46 pm · the clock shows up on the TV&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;Thu 9:19 pm · “let&#39;s go!”&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;10:00 pm · two-factor auth live on the gateway&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;Fri 5:58 pm · mission prompt&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;6:14 pm · first live cam fullscreen&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;Sun 3:18 pm · “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;4:02 pm · pairing works on the TV&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;Sun 8:13 pm · “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;11:01 pm · photo → list → store links&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;Mon 3:19 am · “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:48 am · first verified flash on the watch&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;first message to the 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;first concrete result&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;a 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;night&lt;/text&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;One row per project, Thursday 7 pm to Monday 7 pm. The number on the right counts commits. Hover the circles for exact times.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Each row is a project, each tick a commit. The hollow circle is my first message to the agent. The filled one, raised above the row, is the first concrete thing: the app running on the TV, the firmware on the watch, two-factor auth live on the homelab gateway. The grey bands are nights. There are commits in them. Not always because I was awake.&lt;/p&gt;
&lt;h2 id=&quot;thursday-night-three-agents-in-45-minutes&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#thursday-night-three-agents-in-45-minutes&quot; aria-label=&quot;Anchor link for: thursday-night-three-agents-in-45-minutes&quot;&gt;#&lt;/a&gt;
Thursday night: three agents in 45 minutes&lt;/h2&gt;
&lt;p&gt;On the evening of the 8th, within 45 minutes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;8:34 pm: the Almanak mission goes out.&lt;/li&gt;
&lt;li&gt;8:36 pm: I restart shot2cart, a project begun in September with another tool. “inspect project, it comes from an other AI conversation, inspect and we pursue”.&lt;/li&gt;
&lt;li&gt;9:07 pm: I download from claude.ai the &lt;code&gt;CLAUDE.md&lt;/code&gt; for my homelab, which I’m redesigning with an LLM. I had just written it with another instance while the first one was building Almanak.&lt;/li&gt;
&lt;li&gt;9:19 pm: “je viens de créer CLAUDE.md pour toi, c’est parti !” [just created CLAUDE.md for you, let’s go!]&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nobody waits for anybody. While one agent compiles, I write the next one’s mission with a third.&lt;/p&gt;
&lt;h2 id=&quot;almanak-the-spark&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#almanak-the-spark&quot; aria-label=&quot;Anchor link for: almanak-the-spark&quot;&gt;#&lt;/a&gt;
Almanak, the spark&lt;/h2&gt;
&lt;p&gt;The north star fit in one sentence:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A screen you want to leave on. When I walk past my TV, I know in 2 seconds: the time, the date, the weather now and later, and how many minutes until my next bus. It’s beautiful like a design object, not like an admin dashboard.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And one rule I kept for every project after it: no slice is done until the agent has seen it running on the Shield. It takes a capture over adb, reads it, critiques it. Legibility at 3 m, overscan, overflow. And for every visual slice, two variants; keep the better one.&lt;/p&gt;
&lt;div class=&quot;galerie&quot;&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/blog/quatre-jours/almanak.webp&quot; alt=&quot;Almanak on the TV, dark theme: the time in large type, the date, the day of the year, the next buses and a five-day forecast&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/blog/quatre-jours/almanak-papier.webp&quot; alt=&quot;Almanak, paper theme: the time, the name day, the buses and the temperature curve for the next hours&quot; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Almanak on the Shield on the first night: the dark theme at 10:17 pm, the paper theme at 9:38 pm.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Seven slices between 8:46 and 10:32 pm. For the clock, the agent compared an editorial serif with a Swiss sans-serif and kept Fraunces, because at 3 m, Inter at weight 200 “starts to look like a wire”. It’s right.&lt;/p&gt;
&lt;p&gt;Best bug: the bus schedule sync returned &lt;code&gt;HTTP 441&lt;/code&gt; on the Shield and worked fine from the Mac. The agent dug through &lt;code&gt;dumpsys connectivity&lt;/code&gt; and found the VPN running on the Shield: the STM site (Montreal’s transit agency) rejects VPN exit IPs. It switched to a mirror of the same feed, byte for byte, and documented split tunnelling in the README. The prompt never mentioned a VPN.&lt;/p&gt;
&lt;p&gt;To be honest: my definition of “done” asked for 24 hours of screensaver without a memory leak. The actual measurement covers about an hour. Done, then. In quotes.&lt;/p&gt;
&lt;h2 id=&quot;lukarne-my-favourite&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#lukarne-my-favourite&quot; aria-label=&quot;Anchor link for: lukarne-my-favourite&quot;&gt;#&lt;/a&gt;
Lukarne, my favourite&lt;/h2&gt;
&lt;p&gt;Next day, 5:58 pm, second mission. A window on the world: spin a globe with the remote, dive into a region, watch a wall of live webcams, zap from one camera to the next. The sentence that matters in the prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If an interaction isn’t smooth with the remote, it has failed, even if it is technically correct.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And a quieter one that explains why it all went so fast: “I’ve already built another Google TV app with you on this same Shield. Before anything else, find in my environment […] how we connect to it […]. Reuse what works.” Every project leaves behind an adb address, a deploy script, a &lt;code&gt;DECISIONS.md&lt;/code&gt; format. The next one starts from there.&lt;/p&gt;
&lt;p&gt;6:14 pm: first live stream fullscreen on the TV, HLS and YouTube, left/right to switch. 16 minutes. By 8:54 pm, V1 was there: an OpenGL day/night globe at 60 frames per second (3 ms per frame), 1,640 cameras in 34 regions (19 more since Monday), a 3×3 wall whose focused tile goes live within two seconds, and a “surprise me” button that sends you somewhere it’s daytime. Two 22-minute soak tests, zero crashes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/blog/quatre-jours/lukarne-globe.webp&quot; alt=&quot;The Lukarne globe centred on Europe at night, Venice selected, a webcam of St Mark’s Basin inset&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Lukarne: Europe at night, Venice selected, 16 live webcams, St Mark’s Basin in the corner.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The easiest way is to watch it run:&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 seconds on the Shield, no sound: the globe over to California, the dive into Pollock Pines, the wall of live highway cameras, fullscreen, next camera, climbing back out, then “surprise me”.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;The agent shot this video on Monday evening, for this post. It drove the Shield with a virtual remote (&lt;code&gt;adb shell input keyevent&lt;/code&gt;) and recorded the screen with &lt;code&gt;screenrecord&lt;/code&gt;. The first take was in French, over Europe, where it was 1 am: everything was black. It reshot in English, over California, in daylight.&lt;/p&gt;
&lt;div class=&quot;galerie&quot;&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/blog/quatre-jours/lukarne-mosaique.webp&quot; alt=&quot;The Pollock Pines wall: nine live highway cameras on US 50, the focused tile outlined&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/blog/quatre-jours/lukarne-plein-ecran.webp&quot; alt=&quot;Fullscreen on a California highway camera, a red car at the intersection, place name and local time at the bottom&quot; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;A place’s wall, then fullscreen. Left/right jumps to the neighbouring camera without going back to the wall.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/blog/quatre-jours/lukarne-aurore.webp&quot; alt=&quot;A green aurora over Fairbanks, Alaska, with the camera name and local time&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;“surprise me” landed on this, mid-shoot. Explore.org’s aurora camera in Fairbanks, 3:09 pm local time: probably a replay of the night between two live shots.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Then I used it, and complained. Saturday, 11:20 pm: “je suis assez surpris du peu de flux video , bcp d’images fixes qui apportent peu” [I’m pretty surprised how few video feeds there are, lots of stills that don’t add much]. 11:34 pm, commit: live first, stills optional. Fourteen minutes.&lt;/p&gt;
&lt;p&gt;Overnight from Sunday to Monday, at 12:08 am: “parfait , je vais dormir là” [perfect, I’m off to bed]. And two requests: “les vignettes pixelisée : on peut trouver un moyen de charger ne serait ce qu 1 frame propre plutôt que la vignette degeueu ?” [the pixelated thumbnails: can we find a way to load even 1 clean frame instead of the gross thumbnail?], and for the last zoom level, an “effet plongeant genre zoom google earth” [diving effect, like a Google Earth zoom]. At 12:26 am, the thumbnails are real frames grabbed on the device during playback. At 12:48 am, you dive into a place with a ×30 zoom, the lights come on, and you climb back out. At 12:59 am, a 10-minute soak test on top of all that. I was asleep.&lt;/p&gt;
&lt;p&gt;Monday afternoon, I asked for a demo mode, so the TV travels on its own. 41 minutes later:&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;Demo mode: random cameras where it&#39;s daytime, never the same region twice, with a fade through a title card in between.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/blog/quatre-jours/lukarne-reglages.webp&quot; alt=&quot;The Lukarne settings panel over the globe: language, temperature, still images, corner player, demo time per camera, source credits&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Settings, one remote away: language (English, French, Spanish), units, stills or live only, corner player, demo time per camera, and credits for every source.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Still missing: live coverage outside North America is thin. On day one, the agent burned the whole YouTube search quota while two bugs hid the results. Now a public repository republishes the live catalogue every day, and the app downloads it behind its logo. And there’s a bug I can’t always reproduce: a YouTube webcam that freezes when you chain two of them. I reported it at 3:46 pm, a fix landed at 4:31 pm. Still to be checked on the TV.&lt;/p&gt;
&lt;h2 id=&quot;dropbokstv-one-sunday-afternoon&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#dropbokstv-one-sunday-afternoon&quot; aria-label=&quot;Anchor link for: dropbokstv-one-sunday-afternoon&quot;&gt;#&lt;/a&gt;
DropboksTV, one Sunday afternoon&lt;/h2&gt;
&lt;p&gt;A Dropbox client for the TV, built for the couch: photos and videos first, read-only, no backend. Sunday, 3:18 pm: “read prompt-dropbokstv-claude-code.md and go”. 4:02 pm: phone pairing works on the TV. 6:28 pm: eighth and final slice, with search, offline mode, the screensaver and 128 translated strings per language.&lt;/p&gt;
&lt;p&gt;The trickiest part is pairing. Signing in to Dropbox from a TV, without a keyboard, should be a code you type on your phone. Except Dropbox’s API doesn’t offer that flow. The agent checked the SDK and wrote it down in its decision log. So it built something else:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the TV shows a QR code pointing to a page served by the TV itself, on the home Wi-Fi;&lt;/li&gt;
&lt;li&gt;the phone opens that page, which sends it to authorise DropboksTV on Dropbox;&lt;/li&gt;
&lt;li&gt;Dropbox shows a code, you paste it on the phone, and the TV exchanges it for a token (PKCE: the secret never leaves the TV, no backend);&lt;/li&gt;
&lt;li&gt;the session expires after 10 minutes and the TV renews the QR on its own. A typo doesn’t burn the session. And if the phone can’t see the TV, a button switches to the direct Dropbox link.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The TV’s little HTTP server (150 lines) was lifted from Almanak.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/blog/quatre-jours/dropbokstv-appairage.webp&quot; alt=&quot;DropboksTV pairing screen: three numbered steps and a QR code, blurred here&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Pairing&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And then there’s the viewer. In three slices, between 4:25 and 6:07 pm, the agent wrote a viewer that opens pretty much everything:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;photos, with ×2 and ×3 zoom you pan with the arrows, and a slideshow with fades or Ken Burns;&lt;/li&gt;
&lt;li&gt;videos: H.264, HEVC up to 4K, resume where you left off, &lt;code&gt;.srt&lt;/code&gt; subtitles;&lt;/li&gt;
&lt;li&gt;PDFs, page by page, with zoom;&lt;/li&gt;
&lt;li&gt;Word and PowerPoint, which Dropbox converts to PDF, and spreadsheets, scaled to 170% so they’re readable from the couch;&lt;/li&gt;
&lt;li&gt;formatted Markdown, code with line numbers and syntax colours;&lt;/li&gt;
&lt;li&gt;music, MP3 and FLAC, with cover art;&lt;/li&gt;
&lt;li&gt;and for everything else, a QR code that sends the file to your phone.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The video player took 16 minutes. Documents, music and send-to-phone, 27. Insane.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/blog/quatre-jours/dropbokstv-formats.webp&quot; alt=&quot;Eight DropboksTV screens: a zoomed photo, a video, a PDF, a spreadsheet, Markdown notes, Kotlin code, a song with its cover art and the send-to-phone screen&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;In order: zoomed photo, video, PDF, spreadsheet, Markdown, code, music, send to phone (QR blurred). The agent made the test files itself and dropped them into a dedicated Dropbox folder. Screenshots taken during development, in French.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;kronograf-or-the-return-of-hardware&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#kronograf-or-the-return-of-hardware&quot; aria-label=&quot;Anchor link for: kronograf-or-the-return-of-hardware&quot;&gt;#&lt;/a&gt;
Kronograf, or the return of hardware&lt;/h2&gt;
&lt;p&gt;Monday, 3:19 am, a 2020 LilyGO T-Watch plugged in over USB, and a mission: an aviation chronograph face, plus 7-segment “time circuit” displays. At 3:48 am, first verified flash on the watch, after a full backup of the original flash. That was rule no. 2 in the prompt: nothing gets written to the watch without a safety net. The cable dropped out during the first backup, and the agent refused to flash until it had a complete one.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blok.storagebits.workers.dev/blog/quatre-jours/kronograf-photo.webp&quot; alt=&quot;The real 2020 T-Watch on the table: Kronograf diagnostics screen, memory, sensors detected in green, battery at 100%&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The real watch, Monday at 6:11 pm: the diagnostics screen, the first one flashed. Green: chips detected; grey: what the V1 doesn’t have.&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;duo&quot;&gt;&lt;img src=&quot;kronograf-t1-aviator.webp&quot; alt=&quot;Aviation chronograph face: graduated bezel, three sub-dials for battery, minutes and steps, orange seconds hand&quot;&gt;&lt;img class=&quot;carre&quot; src=&quot;kronograf-t2-face1.webp&quot; alt=&quot;7-segment display face: destination in red, present in green, last sync in orange&quot;&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Left, a capture of the real watch over the serial port. Right, the “time circuits” face in the simulator, which compiles exactly the same UI.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is where the delay goes back up, and it’s not the agent’s fault. It’s no longer code against an emulator, it’s a tired micro-USB connector. My messages that morning: “elle plante systematiquement au bout de quelque secondes” [it crashes every time after a few seconds], “va vite sinon elle plante” [hurry or it crashes], “j’espère qu’elle n’est pas brikée” [I hope it isn’t bricked]. The problem was light sleep, with an interrupt storm when the button woke it up. At 11:17 am: “ce semble tres bien tenir maintenant” [seems to be holding up fine now]. At 11:18 am: “go pour la météo” [go for the weather]. At 11:31 am, the watch shows the weather and the next bus in real time.&lt;/p&gt;
&lt;p&gt;Tonight it has 33 commits, alarms, a stopwatch, a step counter, Wi-Fi updates and iPhone notifications over Bluetooth, which still don’t hold the connection well. The double tap is “un peu alétoire , comme le mouvement du poignet … mais ca passe” [a bit random, like the wrist gesture… but it’ll do].&lt;/p&gt;
&lt;h2 id=&quot;the-homelab-and-the-shot2cart-app&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-homelab-and-the-shot2cart-app&quot; aria-label=&quot;Anchor link for: the-homelab-and-the-shot2cart-app&quot;&gt;#&lt;/a&gt;
The homelab, and the shot2cart app&lt;/h2&gt;
&lt;p&gt;The longest job doesn’t show. Sixty commits and 276 messages to rebuild the home server from the ground up: an inventory, backups with real restore tests, remote access with passkeys for the family, alerts, a home page with an animated map of the infrastructure and another of the local network.&lt;/p&gt;
&lt;p&gt;My favourite message of the whole run, on the first night: “ignore cet audit ; je ne compte que sur toi maintenant” [ignore that audit; I’m counting only on you now]. The most honest one, on Sunday: “Nest dans Home Assistant c’est payant ??!!” [Nest in Home Assistant costs money??!!]. Yep, $5, thanks Google.&lt;/p&gt;
&lt;p&gt;shot2cart is the counter-example. The app’s pitch: a photo of a shopping list, and the cart opens at the store in under 15 seconds, without typing a thing. First attempt in September, with another tool and a spec-driven workflow: three days, two commits. On Thursday night, I restarted it on that base. Sunday, 8:13 pm, I archived everything: “forget about previous session, I archived the previous project and to start a new one with better prompt”. 11:01 pm: photo, analysis by a vision model, store links item by item. 12:04 am: “I’m going to sleep […] please go as far as you can in full autonomy”. By morning there was streaming detection, review cards and automated test flows.&lt;/p&gt;
&lt;p&gt;Same idea, same developer. The difference between September and this Sunday is the quality of the models, Opus 5.5 in particular. A beast!!!&lt;/p&gt;
&lt;h2 id=&quot;the-delay-measured&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-delay-measured&quot; aria-label=&quot;Anchor link for: the-delay-measured&quot;&gt;#&lt;/a&gt;
The delay, measured&lt;/h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Type&lt;/th&gt;&lt;th&gt;What I asked for&lt;/th&gt;&lt;th&gt;Time to 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” [add Spanish to the app] (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;Neighbours 2,500 km away: “je suis sur Londres mais la Patagonie est listée” [I’m on London but Patagonia is listed]&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;The logo that only shows for half a second at launch&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;More live, fewer stills&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;The “Google Earth style” diving zoom, requested before going to bed&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;A demo mode that chains random webcams&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;The recent video that always opens a video from 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” [go for the weather] on the watch&lt;/td&gt;&lt;td&gt;13 min&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;What doesn’t go faster: a micro-USB cable that comes loose, a YouTube quota that resets at 3 am, an API key the provider’s portal rejects, my subscription. Sunday, 4:46 pm: “il me reste que 10% pour notre session en cours” [I’ve only got 10% left for our current session]. And my sleep, which I ended up building into the plan: “je vais dormir la , avance au maximum de ton autonomie” [I’m going to sleep, go as far as you can on your own].&lt;/p&gt;
&lt;p&gt;What I don’t know yet: how many of these six projects I’ll still be using in a month. Time to see it through now, ship what can be shipped, feed other agents.&lt;/p&gt;
&lt;p&gt;This post was made the same way. One message from me on Monday late afternoon, in a jumble, typos included: “génère moi un article complet […] mettre en avant le délai ridicule que c’est devenu entre l’intention et le concret” [write me a complete article […] highlight how ridiculous the gap between intent and reality has become]. Lay it all out, transparently and honestly. The blog itself was online 45 minutes after my first message, Monday at 4 am.&lt;/p&gt;
&lt;p&gt;Anyway, have a good weekend!&lt;/p&gt;
&lt;aside class=&quot;transparence&quot; aria-label=&quot;How this post was written&quot;&gt;
  &lt;p class=&quot;transparence-titre&quot;&gt;How this post was written&lt;/p&gt;
  &lt;p&gt;Drafted by Claude Opus 5.5 from: a one-message brief from François, his Claude Code sessions (about 240 messages), six Git repositories (159 commits), their &lt;code&gt;DECISIONS.md&lt;/code&gt; files and screenshots, videos the agent shot on the Shield, a photo of the watch.&lt;/p&gt;
  &lt;p&gt;Proofread and corrected by François, 19 % of the text rewritten.&lt;/p&gt;
  &lt;p&gt;Translated from French by the AI, from the proofread version. The translation was not reviewed line by line.&lt;/p&gt;
&lt;/aside&gt;
</content>
    </entry>
</feed>
