Email me

Updates

What changed recently

Short progress summaries across all my projects, newest first. The detail lives in each project's articles.

Updates are bullet-point summaries generated with AI from my commits and build notes, then checked by me. Project articles and learning notes are written by me.

  1. · Automating Garage Dreams AI summary

    • Threw the first version away. The single-model scaffold became legacy/, and the rebuild starts from a working site with a two-tier content model: long buyer’s guides written by two frontier CLIs that review each other, and short nightly articles drafted by a local model and reviewed by both. Subscription auth throughout rather than per-token billing.
    • The GitHub repo I was about to push to already held the old site’s history, with no shared commit to merge against. Renamed it to garagedreams-legacy rather than force-pushing over it. The local .git had been lost once already and that is not a thing to do twice.
    • The logo was in the DOM and invisible. The SVG carries live <text> in a font the browser never loads, because an SVG in an <img> renders in an isolated context with no access to the page’s fonts. Inlined it and loaded the face; the real fix is re-exporting with the text outlined.
    • Inlining both logos then mixed their colours: white letters in the orange wordmark, an orange R in the white one. SVG <style> blocks are not scoped, and both files had been exported with the same generic .cls-1, .cls-2 class names. Namespaced each at inline time.
    Active
  2. · Self-balancing robot AI summary

    • The register diagnostic now prints each i2cRead return code next to the value. On the bit-banged bus, a zero-initialised buffer made 00 indistinguishable from a failed read.
    • Tracked a dead left motor to uninitialised TIM8 config structs. Channels 1 to 3 read the unset complementary-output fields and channel 4 does not, so only the right motor (channels 3 and 4) ran. Value-initialising the structs fixed it.
    • Swapping the motor connectors could not separate hardware from firmware. Flashing Yahboom’s vendor image onto the same board did: both wheels drove.
    • See Establish a known-good hardware baseline.
    Active
  3. · Where the Action Actually Happens at F1 GPs AI summary

    • First run of the SQL loader: four transcription errors that each failed loudly, and one that didn’t. The incident insert sat inside a loop that skips crash sheets, so is_crash was false for every row while the load reported success. Split the loader into two passes.
    Active
  4. · Self-balancing robot AI summary

    • Ported the MPU6050 DMP driver unchanged, but its FIFO stayed empty. The original called IIC_MPU6050_Init() from board code outside the driver, and without it SCL sat in its reset state.
    • Got printf telemetry working after fixing three silent gaps: the nosys.specs _write stub, newlib fully buffering stdout, and the ST-Link V2 having no UART. Telemetry comes out of the robot’s USB-C port through its CH340.
    • Traced an angle of -2147483648 in every sample to uninitialised floats in the IMU class. That value is what ARM produces when casting NaN to int. The constructor now zeroes the angles and sets the quaternion to identity.
    • Covered in more depth in Bring up UART, I2C and the MPU6050.
    Active
  5. · Where the Action Actually Happens at F1 GPs AI summary

    • Changed the per-corner average to divide by every race year, not only the years a corner had action. A quiet year is a result, not missing data. Singapore corner 5 went from 3.0 to 2.0.
    • avg_lap still averages only the years present, because a quiet year has no lap. The two rules are separate named functions with docstrings.
    • See A quiet year is zero.
    Active
  6. · Automating Garage Dreams AI summary

    • Wrote the remaining-implementation plan for a less capable model to execute: complete code for every file, exact commands, nothing inferred. It ran steps 1 to 3 and the code matched the plan, but it left the generation script uncommitted, so the work existed only on disk.
    • The generation script hardcoded Ollama’s port. Swapped both the URL and the model name to environment variables, because the development machine runs llama.cpp on 8899 and the Pi will run something else.
    • localhost in that variable resolves to the dev container, not the host serving the model. host.docker.internal is the fix, and it is the kind of thing that reads as “the model is down”.
    • .devcontainer/devcontainer.json was listed as created in the previous session’s notes and did not exist. Described, never written. The first instance of a note recording an intention as a fact.
    Active
  7. · Automating Garage Dreams AI summary

    • First conversion run over the 101 guides: 84 converted, 16 skipped, 1 error. The skips are folders holding more than one .docx and therefore colliding on slug, and the first file wins. The error is accord-euro-r.docx, whose zip structure is broken and which no reader can recover; that guide is simply absent.
    • Image extraction produced nothing, and it took a re-run to work out why: the photos sit beside the .docx in each folder rather than embedded inside it, so the converter’s image handler had nothing to find.
    • Decided the guides carry their real press photos while generated articles get a placeholder. The text is original either way; the split is honest about which pages had a human behind them.
    • astro dev without --host binds only to 127.0.0.1 inside the dev container, which the editor’s port forwarding cannot reliably reach. Minutes lost to a page that looked like it was building slowly.
    Active
  8. · Automating Garage Dreams AI summary

    • Surveyed the source material: 101 .docx buyer’s guides, each in its own folder with photos beside it. Section headings are bold paragraphs rather than Word heading styles, and the spec data is in real Word table elements, so it should survive conversion as markdown tables.
    • Decided the generator writes from one section rather than a whole guide: roughly 1,500 distinct chunks across the corpus, picked one a night. The guides themselves get published too, so the source is also the archive.
    • The brief’s cron line was node scripts/generate-article.js, which cannot run a TypeScript file. Corrected to tsx before it reached a Pi.
    Active