<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/feeds.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://chameth.com/">
    <title>Chameth.com</title>
    <subtitle>Personal homepage of Chris Smith</subtitle>
    <link href="https://chameth.com/index.xml" rel="self"/>
    <link href="https://chameth.com/"/>
    <icon>https://chameth.com/favicon.png</icon>
    <updated>2026-10-01T00:00:00Z</updated>
    <id>https://chameth.com/</id>
    <author>
        <name>Chris Smith</name>
    </author>
    <entry>
        <title>Monthly Meanderings: September 2026</title>
        <link href="https://chameth.com/monthly-meanderings-2026-09/"/>
        <updated>2026-10-01T00:00:00Z</updated>
        <id>https://chameth.com/monthly-meanderings-2026-09/</id>
        <content xml:lang="en" type="html">&lt;p&gt;Where has the year gone? How is it October already? Although I’m actually writing this in September, so it isn’t. But the point still stands, I think.
Man, I’m getting good at these intros. They just flow right off the page, right? … Right?&lt;/p&gt;
&lt;h3 id=&#34;website-updates&#34;&gt;Website updates&lt;/h3&gt;
&lt;p&gt;A couple of new posts appeared in September. &lt;a href=&#34;https://chameth.com/lego-acme-dns-01-surprises/&#34;&gt;Lego, ACME DNS-01, and negatively-cached surprises&lt;/a&gt; is
a rather snappily titled post about debugging an issue a friend had with &lt;a href=&#34;https://github.com/csmith/centauri&#34;&gt;Centauri&lt;/a&gt;, my reverse proxy.
Turns out a combination of default settings in the Lego library and the perfectly reasonable behaviour of systemd-resolved makes for a bad
time. Maybe that should have had a spoiler warning.&lt;/p&gt;
&lt;p&gt;Then in &lt;a href=&#34;https://chameth.com/the-expanding-scope-of-coding-agents/&#34;&gt;the expanding scope of coding agents&lt;/a&gt; I talk a bit about how my use of
LLMs for coding has evolved, and how I “vibed” a perfect-for-me music player with a single, short prompt. It’s a spiritual successor to
&lt;a href=&#34;https://chameth.com/an-app-can-be-a-ready-meal/&#34;&gt;an app can be a ready meal&lt;/a&gt; from last year. Now the microwave is even easier to operate.
Or operates on bigger ready meals? It’s a good job I didn’t try to keep the same analogy in the post.&lt;/p&gt;
&lt;p&gt;Under the hood, I’ve been slowly working on changing how data on my website is handled. Previously, if I wanted to display something like
board game stats, then chameth.com had to have a complete copy of all my board game data so it could query whatever information was needed.
As I added more and more bits and pieces, this felt heavier and heavier, and in some cases was straight-up duplicating data and logic that
I had elsewhere. I’m instead moving to a model where the shortcodes pull data from a remote source and cache it for an appropriate amount
of time. It’s then refreshed in the background for as long as it’s “live”. So the monthly recap embeds you’ll see below refresh until a few
days into October to pick up any straggling data, and are then frozen. It doesn’t really make any difference to what you see on the page,
but the architecture pleases me, and I got to delete a whole bunch of code.&lt;/p&gt;
&lt;p&gt;Finally, I had to tweak the &lt;a href=&#34;https://chameth.com/feeds/posts/build/&#34;&gt;feed builder&lt;/a&gt; I mentioned last month. Some scrapers managed
to stumble into it, then ignored all the &lt;code&gt;nofollow&lt;/code&gt; and &lt;code&gt;noindex&lt;/code&gt; tags, and decided to try and scrape every possible combination of posts.
That is a lot of combinations. Some of the scrapers are lovely things that pretend to be Chrome and use a different residential proxy
per connection, so you can’t simply block IPs or user agents. I’ve changed the builder so that each mutation is a form submission, which
seems to have helped a bit, but there are still some hitting all the URLs they discovered before the change.&lt;/p&gt;
&lt;p&gt;I drafted this post a few days before publishing it, and originally wrote “the scrapers didn’t actually put any noticeable load on the
website, but they did manage to screw up my analytics!” Guess what! Literally as I went to publish this post my monitoring went off
because the site stopped responding after one scraper did 100 requests per second from a bunch of different IPs. Guess I’m writing rate
limiting and ASN blocking code tomorrow.&lt;/p&gt;
&lt;h3 id=&#34;entertainment&#34;&gt;Entertainment&lt;/h3&gt;
&lt;p&gt;Oh look, one of those new cached shortcode things I was talking about! When last month’s post went live, chameth.com had a database
of every track I’d listened to since I started logging them. Now instead there’s a dedicated service for that, and it just passes
the specific information over. In this case, my most played albums this month:&lt;/p&gt;
&lt;div class=&#34;played-albums raised-box&#34; data-title=&#34;Top albums · 1 Sep – 30 Sep 2026&#34;&gt;
    &lt;table class=&#34;plain-table&#34;&gt;
        &lt;thead&gt;
        &lt;tr&gt;
            &lt;th scope=&#34;col&#34;&gt;&lt;/th&gt;
            &lt;th scope=&#34;col&#34;&gt;&lt;/th&gt;
            &lt;th scope=&#34;col&#34;&gt;&lt;/th&gt;
            &lt;th scope=&#34;col&#34;&gt;Album&lt;/th&gt;
            &lt;th scope=&#34;col&#34;&gt;Artist&lt;/th&gt;
            &lt;th scope=&#34;col&#34; class=&#34;numeric&#34;&gt;Unique tracks played&lt;/th&gt;
            &lt;th scope=&#34;col&#34; class=&#34;numeric&#34;&gt;Total tracks played&lt;/th&gt;
        &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;1&lt;/td&gt;
            &lt;td class=&#34;movement movement-up&#34; title=&#34;Up from #4&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-up&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 11l-6 -6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 11l6 -6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/6gjRjALcL9fd1Kvbao3cGT/cover.jpg&#34; alt=&#34;Cover art for The Strange Case of…&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;The Strange Case of…&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;Halestorm&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;7&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;23&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;2&lt;/td&gt;
            &lt;td class=&#34;movement movement-up&#34; title=&#34;Up from #8&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-up&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 11l-6 -6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 11l6 -6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/1Dvu79hzihfhAKvx3zIO5G/cover.jpg&#34; alt=&#34;Cover art for From Zero&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;From Zero&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;Linkin Park&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;7&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;22&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;3&lt;/td&gt;
            &lt;td class=&#34;movement movement-up&#34; title=&#34;Up from #20&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-up&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 11l-6 -6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 11l6 -6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/6SUzAWsriE3QwZiW218rhH/cover.jpg&#34; alt=&#34;Cover art for Good Girl Gone Bad: Reloaded&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;Good Girl Gone Bad: Reloaded&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;Rihanna&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;6&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;22&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;4&lt;/td&gt;
            &lt;td class=&#34;movement movement-up&#34; title=&#34;Up from #9&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-up&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 11l-6 -6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 11l6 -6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/1osLFMjRSYHgnTTrEewJuU/cover.jpg&#34; alt=&#34;Cover art for KPop Demon Hunters (Soundtrack from the Netflix Film)&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;KPop Demon Hunters (Soundtrack from the Netflix Film)&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;KPop Demon Hunters Cast&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;5&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;21&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;5&lt;/td&gt;
            &lt;td class=&#34;movement movement-down&#34; title=&#34;Down from #2&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-down&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 13l-6 6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 13l6 6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/6EwJNS4EVKbNX7fmO7SDUM/cover.jpg&#34; alt=&#34;Cover art for Love, Drugs &amp;amp; Misery&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;Love, Drugs &amp;amp; Misery&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;Eva Under Fire&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;10&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;21&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;6&lt;/td&gt;
            &lt;td class=&#34;movement movement-up&#34; title=&#34;Up from #31&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-up&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 11l-6 -6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 11l6 -6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/54jv4KMFTCrYADDkUIHfSM/cover.jpg&#34; alt=&#34;Cover art for Hands&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;Hands&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;Little Boots&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;7&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;20&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;7&lt;/td&gt;
            &lt;td class=&#34;movement movement-up&#34; title=&#34;Up from #52&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-up&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 11l-6 -6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 11l6 -6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/3DBjxRaO6cHncBI4gGKDdE/cover.jpg&#34; alt=&#34;Cover art for THE WAY&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;THE WAY&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;Dorothy&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;6&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;20&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;8&lt;/td&gt;
            &lt;td class=&#34;movement movement-down&#34; title=&#34;Down from #1&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-down&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 13l-6 6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 13l6 6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/2Eyx1KIPPCkpSjNouXEX9n/cover.jpg&#34; alt=&#34;Cover art for Fallen&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;Fallen&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;Evanescence&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;7&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;19&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;9&lt;/td&gt;
            &lt;td class=&#34;movement movement-up&#34; title=&#34;Up from #11&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-up&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 11l-6 -6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 11l6 -6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/00d6zr0QokyzPwOtyAQJ64/cover.jpg&#34; alt=&#34;Cover art for Villainous&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;Villainous&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;Eva Under Fire&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;7&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;19&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;10&lt;/td&gt;
            &lt;td class=&#34;movement movement-up&#34; title=&#34;Up from #17&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-up&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 11l-6 -6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 11l6 -6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/4ZkaRA2Fg1P4f1h8cic9XM/cover.jpg&#34; alt=&#34;Cover art for American Dream&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;American Dream&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;DIAMANTE&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;7&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;18&lt;/td&gt;
        &lt;/tr&gt;
        &lt;/tbody&gt;
    &lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;As I said last month, this is probably going to be pretty same-y. There are some small movements, but it’s still mostly just
“albums I love” combined with “albums I like that have songs with a suitable BPM for running” in some order.&lt;/p&gt;
&lt;p&gt;I watched a bit of TV this month, catching up on the most recent seasons of &lt;a href=&#34;https://www.themoviedb.org/tv/60625-rick-and-morty&#34;&gt;Rick and Morty&lt;/a&gt;.
It’s nice having something relatively short to watch when you’re at a bit of a loose end, or don’t want to have to remember
what contrived plot arc is going on in a longer-form series. I was also pleasantly surprised to discover that
&lt;a href=&#34;https://www.themoviedb.org/tv/288670-saturday-night-live-uk&#34;&gt;SNL UK&lt;/a&gt; is back. It’s — as you’d expect — more of the
same standard SNL formula, with some hits and some misses, but Weekend Update remains consistently hilarious.&lt;/p&gt;
&lt;p&gt;Most of the rest of my entertainment time has been spent in World of Warcraft, in one form or another. I picked up beta
access to &lt;a href=&#34;https://worldofwarcraft.blizzard.com/en-gb/forever&#34;&gt;WoW: Forever&lt;/a&gt;, which is basically a semi-modernised version
of classic WoW but without following the same expansions as the original. Just… classic… forever. I initially didn’t
think I’d like it, given how opposed classic players seem to be to everything nice, but so far it seems like they’ve done
a good job balancing it. It feels a lot more like an RPG than modern WoW, where everything feels like a thin veneer over
getting gear or various currencies. It’s still early though, so I’m not sure how much “sticking power” it’ll have with me.&lt;/p&gt;
&lt;p&gt;In normal WoW, I’ve been playing both my Shaman and Monk healers. I’m leaning back towards Monk now. It’s still undertuned
despite several buffs, but I don’t think that actually matters at the level I’m playing at. After playing
around with some of the talents, I’m happier with it than I was, and I still find it a lot more fun to play than the Shaman,
even if it doesn’t quite perform as well.&lt;/p&gt;
&lt;div class=&#34;wow-char-grid&#34;&gt;
&lt;div class=&#34;wow-char raised-box&#34; data-title=&#34;World of Warcraft Character Data&#34;&gt;
&lt;div class=&#34;overview&#34;&gt;
&lt;img src=&#34;https://chameth.com/wow/characters/b903c78c6da49cd4924abbe7af21c50cee99c0ebf1c2739b7d918ab58fcb079a.png&#34; alt=&#34;Miraceth&#34; loading=&#34;lazy&#34;/&gt;
&lt;p class=&#34;detail&#34;&gt;Level 90&lt;/p&gt;
&lt;p class=&#34;detail&#34;&gt;Female Night Elf&lt;/p&gt;
&lt;p class=&#34;detail&#34;&gt;&lt;span class=&#34;wow-class-monk&#34;&gt;Mistweaver Monk&lt;/span&gt;&lt;/p&gt;
&lt;p class=&#34;detail&#34;&gt;323 average item level&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;info&#34;&gt;
&lt;h3 class=&#34;plain-header&#34;&gt;Miraceth&lt;span class=&#34;realm&#34;&gt;-Terenas&lt;/span&gt;&lt;/h3&gt;
&lt;h4 class=&#34;plain-header&#34;&gt;Professions&lt;/h4&gt;
&lt;table class=&#34;plain-table professions&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Midnight Leatherworking&lt;/td&gt;&lt;td&gt;&lt;progress class=&#34;tier-progress&#34; value=&#34;100&#34; max=&#34;100&#34;&gt;&lt;/progress&gt;&lt;/td&gt;&lt;td class=&#34;tier-count&#34;&gt;100/100&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Midnight Skinning&lt;/td&gt;&lt;td&gt;&lt;progress class=&#34;tier-progress&#34; value=&#34;100&#34; max=&#34;100&#34;&gt;&lt;/progress&gt;&lt;/td&gt;&lt;td class=&#34;tier-count&#34;&gt;100/100&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Midnight Cooking&lt;/td&gt;&lt;td&gt;&lt;progress class=&#34;tier-progress&#34; value=&#34;100&#34; max=&#34;100&#34;&gt;&lt;/progress&gt;&lt;/td&gt;&lt;td class=&#34;tier-count&#34;&gt;100/100&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Midnight Fishing&lt;/td&gt;&lt;td&gt;&lt;progress class=&#34;tier-progress&#34; value=&#34;110&#34; max=&#34;300&#34;&gt;&lt;/progress&gt;&lt;/td&gt;&lt;td class=&#34;tier-count&#34;&gt;110/300&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h4 class=&#34;plain-header&#34;&gt;Mythic+&lt;/h4&gt;
&lt;table class=&#34;plain-table mythic-plus&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Altar of Fangs&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+10&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;18:53&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;334&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Den of Nalorakk&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;27:40&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;370&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Kings&amp;#39; Rest&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;32:35&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;365&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Murder Row&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;24:02&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;376&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Ruby Life Pools&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;21:31&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;374&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Temple of Sethraliss&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+10&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;23:12&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;330&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;The Blinding Vale&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+10&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;23:56&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;328&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Voidscar Arena&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;29:38&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;365&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;total&#34;&gt;
&lt;th colspan=&#34;3&#34; class=&#34;plain-header&#34;&gt;Total M+ rating&lt;/th&gt;
&lt;td class=&#34;rating&#34;&gt;2842&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&#34;links&#34;&gt;
&lt;a href=&#34;https://worldofwarcraft.blizzard.com/en-gb/character/eu/terenas/miraceth&#34;&gt;View on blizzard.com&lt;/a&gt;
&lt;a href=&#34;https://raider.io/characters/eu/terenas/Miraceth&#34;&gt;View on raider.io&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&#34;wow-char raised-box&#34; data-title=&#34;World of Warcraft Character Data&#34;&gt;
&lt;div class=&#34;overview&#34;&gt;
&lt;img src=&#34;https://chameth.com/wow/characters/e7be7f23f0313b9fc0ce63ead04d576c17980e3027d62afb699ebad3d19abb28.png&#34; alt=&#34;Hirameth&#34; loading=&#34;lazy&#34;/&gt;
&lt;p class=&#34;detail&#34;&gt;Level 90&lt;/p&gt;
&lt;p class=&#34;detail&#34;&gt;Female Dwarf&lt;/p&gt;
&lt;p class=&#34;detail&#34;&gt;&lt;span class=&#34;wow-class-shaman&#34;&gt;Restoration Shaman&lt;/span&gt;&lt;/p&gt;
&lt;p class=&#34;detail&#34;&gt;320 average item level&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;info&#34;&gt;
&lt;h3 class=&#34;plain-header&#34;&gt;Hirameth&lt;span class=&#34;realm&#34;&gt;-Terenas&lt;/span&gt;&lt;/h3&gt;
&lt;h4 class=&#34;plain-header&#34;&gt;Professions&lt;/h4&gt;
&lt;table class=&#34;plain-table professions&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Midnight Blacksmithing&lt;/td&gt;&lt;td&gt;&lt;progress class=&#34;tier-progress&#34; value=&#34;100&#34; max=&#34;100&#34;&gt;&lt;/progress&gt;&lt;/td&gt;&lt;td class=&#34;tier-count&#34;&gt;100/100&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Midnight Inscription&lt;/td&gt;&lt;td&gt;&lt;progress class=&#34;tier-progress&#34; value=&#34;92&#34; max=&#34;100&#34;&gt;&lt;/progress&gt;&lt;/td&gt;&lt;td class=&#34;tier-count&#34;&gt;92/100&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Midnight Fishing&lt;/td&gt;&lt;td&gt;&lt;progress class=&#34;tier-progress&#34; value=&#34;35&#34; max=&#34;300&#34;&gt;&lt;/progress&gt;&lt;/td&gt;&lt;td class=&#34;tier-count&#34;&gt;35/300&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h4 class=&#34;plain-header&#34;&gt;Mythic+&lt;/h4&gt;
&lt;table class=&#34;plain-table mythic-plus&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Altar of Fangs&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;27:28&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;368&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Den of Nalorakk&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;28:10&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;369&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Kings&amp;#39; Rest&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+14&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;26:20&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;403&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Murder Row&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;25:04&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;375&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Ruby Life Pools&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;25:19&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;369&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Temple of Sethraliss&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;24:13&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;374&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;The Blinding Vale&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;20:23&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;377&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Voidscar Arena&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;23:27&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;373&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;total&#34;&gt;
&lt;th colspan=&#34;3&#34; class=&#34;plain-header&#34;&gt;Total M+ rating&lt;/th&gt;
&lt;td class=&#34;rating&#34;&gt;3008&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&#34;links&#34;&gt;
&lt;a href=&#34;https://worldofwarcraft.blizzard.com/en-gb/character/eu/terenas/hirameth&#34;&gt;View on blizzard.com&lt;/a&gt;
&lt;a href=&#34;https://raider.io/characters/eu/terenas/Hirameth&#34;&gt;View on raider.io&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;fitness&#34;&gt;Fitness&lt;/h3&gt;
&lt;figure class=&#34;image right&#34; data-width=&#34;152&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/monthly-meanderings-2026-09/5k-medal.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/monthly-meanderings-2026-09/5k-medal.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/monthly-meanderings-2026-09/5k-medal.png&#34; alt=&#34;Photo of a medal: a black star with a circle around it, with the word &amp;#39;Finisher&amp;#39; engraved. An attached lanyard says &amp;#39;5km finisher&amp;#39;&#34; loading=&#34;lazy&#34; width=&#34;152&#34; height=&#34;512&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;A medal for running my first 5k&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This has been a fun month for my running activities! I ran my first timed 5k at the start of the month, finishing in 41:22.
My aim was for a pace of around 8 minutes per km, and the actual course was 5.2km, so I was pretty much spot on. It’s not
a very fast pace, but given I only started running at the end of June I’m pretty happy with it. I’m now working my way
up towards 10k, then I’m going to spend a little while just building volume and hopefully some speed.&lt;/p&gt;
&lt;p&gt;Now that I can comfortably run 5km, albeit rather slowly, I’ve started going to my local parkrun each week. Naturally
I’ve &lt;a href=&#34;https://chameth.com/parkrun/&#34;&gt;created a webpage&lt;/a&gt; that tracks my stats and results. In hindsight I could have
started going much earlier, and done some of my walk-run training there, but I didn’t want to be around that many
people when I was still finding my feet.&lt;/p&gt;
&lt;p&gt;I feel like I’m getting a bit better at pacing myself, but I’m also conscious that I don’t really know what my “all-out”
speed is and how long I can maintain it for. I don’t think I want to do a time trial while I’m working towards a 10k,
so for now I’ll just stick with slowly but surely pushing my PBs down:&lt;/p&gt;
&lt;div class=&#34;workout-summary-sections&#34;&gt;
&lt;div class=&#34;workout-summary-section raised-box&#34; data-title=&#34;Running · 1 Sep – 30 Sep 2026&#34;&gt;
&lt;div class=&#34;stat-row&#34;&gt;
&lt;div class=&#34;stat&#34;&gt;
&lt;p class=&#34;stat-value&#34;&gt;14&lt;/p&gt;
&lt;p class=&#34;stat-label&#34;&gt;activities&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;stat&#34;&gt;
&lt;p class=&#34;stat-value&#34;&gt;8h 0m&lt;/p&gt;
&lt;p class=&#34;stat-label&#34;&gt;total time&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;stat&#34;&gt;
&lt;p class=&#34;stat-value&#34;&gt;59.8km&lt;/p&gt;
&lt;p class=&#34;stat-label&#34;&gt;total distance&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;stat&#34;&gt;
&lt;p class=&#34;stat-value&#34;&gt;6.5km&lt;/p&gt;
&lt;p class=&#34;stat-label&#34;&gt;longest distance&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul class=&#34;workout-summary-pbs&#34;&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;400m&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;2:36&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;2:26&lt;/span&gt;
&lt;/li&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;800m&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;5:54&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;5:15&lt;/span&gt;
&lt;/li&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;1km&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;7:27&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;6:40&lt;/span&gt;
&lt;/li&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;1 mile&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;12:11&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;11:00&lt;/span&gt;
&lt;/li&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;2km&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;15:13&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;13:35&lt;/span&gt;
&lt;/li&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;3km&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;23:05&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;20:37&lt;/span&gt;
&lt;/li&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;5km&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;38:45&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;37:04&lt;/span&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I managed to entirely avoid getting on my bike this month. A few planned rides fell through, and the rest of the time
the weather was miserable, or my legs were still raising complaints about previous runs, or I was doing this pesky
thing called “work”, or I just didn’t feel like it. Hopefully I can squeeze a decent ride in at some point before the
weather turns even more, but that list of excuses looks nigh insurmountable.&lt;/p&gt;
&lt;h3 id=&#34;around-the-web&#34;&gt;Around the web&lt;/h3&gt;
&lt;h4 id=&#34;prompts-arent-realhttpsevaluationclub&#34;&gt;&lt;a href=&#34;https://evaluation.club/&#34;&gt;Prompts aren’t real&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;An interesting presentation from &lt;a href=&#34;https://mcfunley.com/&#34;&gt;Dan McKinley&lt;/a&gt;, who I remember from &lt;a href=&#34;https://boringtechnology.club/&#34;&gt;the boring technology club&lt;/a&gt;.
He seems to be single-handedly propping up the entire &lt;code&gt;.club&lt;/code&gt; TLD. Anyway, it’s an interesting look at how some LLM tooling
works, and contrasts starkly with how half-assed a lot of support bots seem to be.&lt;/p&gt;
&lt;h4 id=&#34;the-contagion-of-fearhttpsbcantrilldtraceorg20260913the-contagion-of-fear&#34;&gt;&lt;a href=&#34;https://bcantrill.dtrace.org/2026/09/13/the-contagion-of-fear/&#34;&gt;The Contagion of Fear&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Bryan Cantrill talks about the recent hysteria concerning the risk of LLMs wiping out humanity. Bryan always seems to have very sensible takes, and
presents them very well, and this one is no exception.&lt;/p&gt;
&lt;h4 id=&#34;moving-from-wordpress-to-hugohttpswwwsimonmottcouk202609moving-from-wordpress-to-hugo&#34;&gt;&lt;a href=&#34;https://www.simonmott.co.uk/2026/09/moving-from-wordpress-to-hugo/&#34;&gt;Moving from WordPress to Hugo&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A post from my friend Simon about redoing his website. I particularly like the screenshots of the previous versions at the end. I’ve
been meaning to do a “History of this site” post for a while, and seeing this has moved it up my priority list.&lt;/p&gt;
&lt;h4 id=&#34;flip-fluid-on-flip-dotshttpsmitxelacomprojectsflipflip&#34;&gt;&lt;a href=&#34;https://mitxela.com/projects/flipflip&#34;&gt;FLIP Fluid on Flip Dots&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I find hardware projects like these more and more appealing as the software world gets more and more LLMy. They’re physical, and whimsical,
and you can’t just recreate it by typing some words into a prompt. Now if you’ll excuse me, I have a sudden urge to solder something…&lt;/p&gt;
</content>
    </entry>
    <entry>
        <title>The expanding scope of coding agents</title>
        <link href="https://chameth.com/the-expanding-scope-of-coding-agents/"/>
        <updated>2026-09-14T00:00:00Z</updated>
        <id>https://chameth.com/the-expanding-scope-of-coding-agents/</id>
        <content xml:lang="en" type="html">&lt;p&gt;It’s time for another post about LLMs! I know, I know, that’s basically all anyone talks about these days.
But that sort of makes sense: it’s what a significant number of software engineers spend their day using.
And they’re changing so rapidly. Back in May last year, &lt;a href=&#34;https://chameth.com/coming-around-on-llms/&#34;&gt;I said&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But how good is it at actually writing code? It’s like having a keen but not particularly thorough Junior Engineer at your beck and call. If you give it a clearly defined task and guidance on how to implement it (and maybe some feedback as it suggests changes), it’s more than capable of doing it. If you don’t give it enough guidance it tends to go more off the rails. I tried having it generate a simple application from scratch with minimal technical guidance and no review of what it was doing, and it made such a mess of it I decided it was quicker to throw it away and start again by hand.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What I was describing there basically amounted to “the coding agent can be responsible for a function,
or a group of functions if they’re described well enough”. That post was published at about the time
that Sonnet and Opus 4 were released, and there have been several big releases since then.&lt;/p&gt;
&lt;h3 id=&#34;graduating-from-functions-to-features&#34;&gt;Graduating from functions to features&lt;/h3&gt;
&lt;p&gt;Now, I didn’t stick with Claude through all those shiny new releases. Anthropic effectively banned using
third-party harnesses with their subscription plans, and did it in a way that rubbed me the wrong way.
As far as I’m concerned, I’m paying for inference-as-a-service: it shouldn’t matter if I do that via
Claude Code, or OpenCode, or something else. But they disagree. So I jumped ship to
&lt;a href=&#34;https://z.ai/subscribe&#34;&gt;Z.ai’s GLM coding plan&lt;/a&gt;. At the time I signed up, the cost for a year-long
subscription was about the same as a month of Claude, with higher limits.&lt;/p&gt;
&lt;p&gt;The GLM models definitely weren’t (and still aren’t) as good as the Claude series, but they were
mostly good enough. But as new models were released, the capabilities slowly ratcheted up.
I noticed the unit of work I gave them graduated from functions to small, self-contained features.
Instead of describing exact implementation steps, I was describing the functionality I wanted. I
didn’t stop providing guidance on &lt;em&gt;how&lt;/em&gt; to do it, but I was increasingly happy giving it larger
chunks of work and getting something reasonable back.&lt;/p&gt;
&lt;p&gt;Around the same time, the cost of some of the models dropped quite a bit. Between a new
&lt;code&gt;flash&lt;/code&gt; model, token discounts, resets, and so on, it got to the point where I &lt;em&gt;couldn’t&lt;/em&gt; hit the
plan limits even if I tried. So I was throwing more and more work at it: having two to three things
going at the same time, with me only dropping in at the end to review and ask for follow-ups.&lt;/p&gt;
&lt;h3 id=&#34;from-features-to-artefacts&#34;&gt;From features to artefacts&lt;/h3&gt;
&lt;p&gt;The GLM models weren’t quite good enough to tackle larger units of work. I tried going back to
Claude to see what it was like now but bounced off because its output was incredibly painful to read.
I wanted to try Fable, their next-tier model, but you can’t do that without one of the amazingly
expensive plans or paying API costs directly. Neither of those options is particularly appealing
to me. Meanwhile, OpenAI launched Astra, their Fable-equivalent. And you could use it in small
amounts on the less expensive plans.&lt;/p&gt;
&lt;p&gt;I played around with Astra on a few small things, but I was conscious of the low limits.
Plus, GLM-5.3-flash mostly did the bits and pieces that I wanted. I figured I’d give Astra a
proper try the next time I had a larger project. Fortunately, some packet loss gave me that
opportunity very soon.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://chameth.com/further-adventures-in-music-organisation/&#34;&gt;I use Navidrome&lt;/a&gt; running
on a remote server to host all my music, which is fantastic. Until your connection goes a bit
wibbly. Apparently, the music player I was using didn’t believe in buffering, so when I
developed 20% packet loss to the server the music kept pausing, playing a second, pausing,
playing another second, and so on. Pretty much the most infuriating way it could have handled
it. Writing my own music player had been at the back of my mind for a little while, and
suddenly I had both impetus AND a shiny new tool.&lt;/p&gt;
&lt;p&gt;I launched &lt;a href=&#34;https://pi.dev/&#34;&gt;pi&lt;/a&gt;, switched over to Astra on low thinking mode, and gave it this
prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is a new project. It’s going to be a native music player written in Go using the Gio library, backed by a Navidrome server. Think foobar2000, but Linux-native, Go, and Navidrome. It doesn’t have to do everything right away, but an MVP should be able to: connect to navidrome (saving credentials), show playlists, play a playlist in order. skip tracks, pause/unpause, adjust volume, etc. It should be tolerant to network issues – buffer a good few minutes of playback data, or an entire track or two. I have two other UI projects using Gio for reference, see ../gtodo and ../glauncher. For actual playback we may want to consider using an established backend like mpv, but that’s not a hard recommendation. For this project you are the overseer/orchestrator. You should use subagents using the zai/glm-5.3-flash model to do recon, implementation, debugging, etc. You can ask me questions if you have any.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I’d seen a few comments from people doing a similar kind of orchestration, and references
to tools like &lt;a href=&#34;https://github.com/PrimeIntellect-ai/prime-agent&#34;&gt;prime agent&lt;/a&gt; that have
first-class support for the workflow. It seemed like a natural fit for my case: Astra can
do the orchestration, architecture, and review, but the much cheaper GLM models can do
the actual heavy lifting. It asked me a few questions, and an hour later a fully functioning
music player popped out:&lt;/p&gt;
&lt;figure class=&#34;image full&#34; data-width=&#34;1156&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/the-expanding-scope-of-coding-agents/gmusic.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/the-expanding-scope-of-coding-agents/gmusic.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/the-expanding-scope-of-coding-agents/gmusic.png&#34; alt=&#34;A screenshot of gmusic, showing a sidebar with playlists, a large table showing tracks in the selected playlist, and a control bar at the bottom.&#34; loading=&#34;lazy&#34; width=&#34;1156&#34; height=&#34;784&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;gmusic, fresh off the press, playing away.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It was &lt;em&gt;exactly&lt;/em&gt; what I asked for. A one-paragraph prompt producing a fully functioning
piece of software kind of blows my mind. And that was with Astra on low thinking, on the
£20/month plan, orchestrating cheaper agents. There was a fair bit of judgement
encoded in the prompt, and “think foobar2000” carries a lot of information in just two
words, but it still felt very light on the details compared to what I’d usually write.
Just a description of what I want in, and a binary out.&lt;/p&gt;
&lt;p&gt;I’ve been using gmusic for a week now, and only had to make a few minor
additions to go from “MVP” to “comfortable daily driver”: scrobbling support, media keys,
and remembering playback/playlist position across restarts&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:1&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h3 id=&#34;cattle-pets-prompts-and-saying-no&#34;&gt;Cattle, pets, prompts, and saying no&lt;/h3&gt;
&lt;p&gt;The ability to generate a binary from a paragraph of text changes the way I look at
software. Sysadmins often speak of treating servers like “cattle, not pets”: if one
server develops a fault, you just tear it down and automatically rebuild or redeploy
it. It holds no special value, so you don’t need to slowly nurse it back to health,
or spend ages recreating it. I think that sort of attitude now applies to software.&lt;/p&gt;
&lt;p&gt;If it turns out gmusic has some horrible architectural flaw — and it might, because
I haven’t even glanced at the source — then I can just throw the whole thing away,
feed in the same prompt with an additional bit on how to avoid the same mistake, and
get something similar enough back.&lt;/p&gt;
&lt;p&gt;There are some limits here: the “paragraph-to-artefact” pipeline is stochastic,
so you might in fact &lt;em&gt;not&lt;/em&gt; get something similar enough back. And you have to be willing
to throw the whole thing away and wait for a replacement. No big deal for a music app,
but I wouldn’t want to take that attitude with a business-critical application. There’s
also an interesting wrinkle in the other direction: model capabilities are advancing
so much that you might &lt;em&gt;want&lt;/em&gt; to throw it out and regenerate it again. Could “regenerate
the app from the prompt” become the new “install updates”?&lt;/p&gt;
&lt;p&gt;One thing to fall out of that view is that &lt;em&gt;documented intent&lt;/em&gt; becomes king. Your prompt
and subsequent corrections and additions are more important than the generated code.
I like the idea of keeping some kind of decision log that future agents
can be pointed at, but I’ve not yet found a good way to do so. Getting the agents to
write it out reveals some obvious flaws: they’re very bad at distinguishing deliberate
decisions from incidental implementation details; they’re also bad at picking the right
level of detail, and seem chronically incapable of breaking with previously documented
behaviour.&lt;/p&gt;
&lt;p&gt;Agent-managed documentation, in fact, seems to end up resembling a cult. The first
pass is probably OK-ish. Then the next agent to touch it will be compelled to extend
any list, timeline, or history with whatever it’s working on. Over time, a superfluous
comment about how something was made will undergo a gradual epistemological transformation
until it’s treated as a commandment from on high by all future agents. It’s a mess.&lt;/p&gt;
&lt;p&gt;I think this is part of a bigger problem: models are still way too sycophantic. There’s
very little pushback against either the user or the documentation. If I asked an agent to
add a feature to gmusic that analyses a webcam image of my toaster and alerts me when
the toast is ready, it’d just do it. If you’re not careful, your software will gain 7
kitchen sinks and 3 Swiss Army knives, and at no point will the agent suggest splitting
them out, or refactoring. Before you know it, your cattle have mutated into grotesque
pets with far too many limbs.&lt;/p&gt;
&lt;p&gt;The current generation of models is almost certainly capable enough to push back
on bizarre features, stop scope creep, ask questions to determine how robustly to
build something or how to handle edge cases, and so on. You can probably even
prompt them in a way that they’ll do it, but in their normal state the
&lt;a href=&#34;https://en.wikipedia.org/wiki/Reinforcement_learning_from_human_feedback&#34;&gt;RLHF&lt;/a&gt;&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:2&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;
seems to have bulldozed over all that in favour of being a yes man.&lt;/p&gt;
&lt;h3 id=&#34;from-artefacts-to-systems&#34;&gt;From artefacts to systems?&lt;/h3&gt;
&lt;p&gt;I think the current state of agents is that they can own “artefacts”, having graduated
from functions and features. It’s a very deliberate choice of words: they can’t own
the full application or system because they won’t independently step back and make
the necessary judgement calls.&lt;/p&gt;
&lt;p&gt;It’s tempting to imagine a perfect little software factory, where you have an agent
in charge of product management, one in charge of architecture, one that manages
the documentation, and so on, all with their own points of view and priorities in
their prompts. If you’re not careful, you’ll soon be
&lt;a href=&#34;https://github.com/gastownhall/gastown&#34;&gt;talking about polecats and deacons&lt;/a&gt; and
writing very large cheques to frontier labs.&lt;/p&gt;
&lt;p&gt;I do think there’s some benefit in this kind of orchestration, but there’s also
a limit to how much adding agents wearing different hats will help. And there’s
also a limit to how much I’m willing to spend on inference! I &lt;em&gt;have&lt;/em&gt; got good
mileage out of a dedicated code review agent. It often catches one or two problems
that the other model overlooked, so I get an obvious and immediate
“return on investment”: a few pence spent running deepseek-4.1-flash with a code
review prompt earns me fewer bugs.&lt;/p&gt;
&lt;p&gt;I think that’s basically the cut-off for me right now. I almost want a
“documentation review” agent to try and manage some of the problems I mentioned
earlier, but I don’t think it’s worth it. Maybe it’ll make a fun experiment
as local models get more capable.&lt;/p&gt;
&lt;h3 id=&#34;determinism-instead-of-prompts&#34;&gt;Determinism instead of prompts&lt;/h3&gt;
&lt;p&gt;One thing that interests me is how much of this problematic behaviour we can
get rid of &lt;em&gt;without&lt;/em&gt; relying on the model to remember and follow instructions.
I had an issue with agents making a mess of the git history with non-fast-forward
merges from subagents. The “natural” fix is to prompt the agent not to do that,
but that relies on it following that one particular instruction. It doesn’t
always do that. Instead I made a simple pi extension that checks the git history
and tells the agent to fix it if it’s broken. That has had a 100% success rate
so far.&lt;/p&gt;
&lt;p&gt;Similarly, in the gmusic prompt I told the agent what model to use for
the subagents. After doing this for a while (and promoting the instructions
to a skill instead of something I type out every time), I noticed it would
occasionally just use a different model. So now I have another pi extension
that defines subagent roles using the models I want it to use, and the
agents can’t just do their own thing.&lt;/p&gt;
&lt;p&gt;This makes me wonder if the next step to expanding agent capabilities
isn’t necessarily more capable models, or more agents in different
hats, but more rigour in software engineering. If we had a way to accurately
assess the need for refactoring, or the quality of documentation, then we
could immediately put hard-and-fast rules in that the agents will follow.
That’s a load-bearing “if”, though, if you’ll forgive me the Claude-ism.&lt;/p&gt;
&lt;p&gt;However we reach it, the future where a prompt can become a fully formed,
maintainable, secure, documented piece of software probably isn’t that far
away. It’s going to be very interesting when regular users can just summon
good software to do whatever they want. I’m not sure what that means for
my career as a software engineer: there are still a lot of places in
the process that benefit from knowledge, experience and judgement, but maybe
that won’t be the case for much longer? There are
&lt;a href=&#34;https://en.wikipedia.org/wiki/May_you_live_in_interesting_times&#34;&gt;interesting times&lt;/a&gt;
ahead for us, either way.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr/&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Which actually fixed a long-standing minor annoyance for me: I have a “Daily Mix” playlist generated, well, daily. In my old player it would effectively queue the whole playlist, so when I opened it the next day I carried on listening to the &lt;em&gt;old&lt;/em&gt; daily playlist. So instead of just pressing the play button on my keyboard, I had to find the app, click the new playlist, and then press play. Not a problem worth fixing in itself, but still a minor daily papercut that I’m glad to be rid of. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:1&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;Or whatever other post-training is done these days. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:2&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content>
    </entry>
    <entry>
        <title>Lego, ACME DNS-01, and negatively-cached surprises</title>
        <link href="https://chameth.com/lego-acme-dns-01-surprises/"/>
        <updated>2026-09-09T00:00:00Z</updated>
        <id>https://chameth.com/lego-acme-dns-01-surprises/</id>
        <content xml:lang="en" type="html">&lt;p&gt;Recently &lt;a href=&#34;https://www.simonmott.co.uk&#34;&gt;a friend&lt;/a&gt; was deploying &lt;a href=&#34;https://github.com/csmith/centauri&#34;&gt;Centauri&lt;/a&gt;, my reverse proxy service that can obtain TLS certificates
using ACME DNS-01 challenges. He asked a seemingly innocent question: “When Centauri is ‘waiting for record propagation’ is there an option to increase the time?”
But that’s a question with a hidden, concerning XY problem. We both use the same DNS provider, and it definitely shouldn’t take more than a minute to see a DNS record
after it’s been created. Plus, I don’t have issues with it timing out!&lt;/p&gt;
&lt;p&gt;Centauri uses the excellent &lt;a href=&#34;https://github.com/go-acme/lego&#34;&gt;lego library&lt;/a&gt; to do the actual ACME dance, and I’ve dug into it before pretty extensively to debug
other DNS issues. When I last looked, it used the local recursive DNS resolver to check for CNAMEs and find the authoritative server, then checked for record
propagation there. So, naturally, I blamed Simon’s local resolver doing something weird. Turns out that was surprisingly close to correct, but not for the reasons
I thought.&lt;/p&gt;
&lt;p&gt;Unfortunately for my snap judgements, Simon had a perfect rejoinder: he’d deployed the exact same VM image before and Centauri worked fine. And also while we’d
been speaking it had managed to get a certificate after waiting 58 seconds for DNS propagation. Simon suggested that maybe it was systemd-resolved caching
something stale, but I dismissed that based on what I knew of how Lego worked.&lt;/p&gt;
&lt;h3 id=&#34;diving-back-into-lego&#34;&gt;Diving back into Lego&lt;/h3&gt;
&lt;p&gt;Of course, after I dismissed the idea, I immediately started doubting myself. Last time I was looking at DNS issues I traced the code all the way across the
Lego codebase. This time I just had a coding agent explore and summarise for me. Turns out it DOES use the recursive resolver to check for the TXT challenge
record.&lt;/p&gt;
&lt;p&gt;The DNS functionality looks like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CNAME query on the &lt;code&gt;_acme-challenge&lt;/code&gt; record using the recursive resolver.&lt;/li&gt;
&lt;li&gt;Maybe an SOA query on the same, depending on the DNS provider, using the recursive resolver.&lt;/li&gt;
&lt;li&gt;Actually create the challenge record using the DNS provider.&lt;/li&gt;
&lt;li&gt;Query records recursively to discover CNAMEs using the recursive resolver. It doesn’t matter if this fails.&lt;/li&gt;
&lt;li&gt;Query the TXT record on &lt;em&gt;every&lt;/em&gt; configured recursive resolver, blocking until they &lt;em&gt;all&lt;/em&gt; work.&lt;/li&gt;
&lt;li&gt;Query the TXT record on the authoritative nameserver.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So yes, a stale response from the recursive resolver can indeed ruin the whole thing. Simon’s domain had a minimum TTL of 60 seconds, so that links in
nicely with it working at 58 seconds and failing most of the time: the 1 minute timeout is racing the 1 minute TTL, somehow. But my
domains use a much longer TTL and I can see renewals happening in my logs basically immediately. What gives?&lt;/p&gt;
&lt;h3 id=&#34;nxdomain-caching-and-rfc-fun&#34;&gt;NXDOMAIN caching and RFC fun&lt;/h3&gt;
&lt;p&gt;So I had a theory as to what was happening, but it still had some loose ends that I didn’t understand. I did get a pretty big hint when I started doing
lookups from various locations, though. On my desktop if I do a CNAME query then a TXT query for a record that doesn’t exist, I get this:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;$ drill CNAME nonexistent.chameth.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, rcode: NXDOMAIN, id: 12630
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;chameth.com.	3600	IN	SOA	chameth.com. root.chameth.com. 2026339162 7200 3600 86400 3600
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;$ drill TXT nonexistent.chameth.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, rcode: NXDOMAIN, id: 56402
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;chameth.com.	3590	IN	SOA	chameth.com. root.chameth.com. 2026339162 7200 3600 86400 3600
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And from my server:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;$ drill CNAME nonexistent.chameth.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, rcode: NXDOMAIN, id: 7807
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;chameth.com.	3600	IN	SOA	chameth.com. root.chameth.com. 2026339162 7200 3600 86400 3600
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;$ drill TXT nonexistent.chameth.com 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, rcode: NXDOMAIN, id: 5069
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;chameth.com.	3600	IN	SOA	chameth.com. root.chameth.com. 2026339162 7200 3600 86400 3600
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice how the TTL (the first number) decreases locally, but not on the server? That implies my local
resolver is reusing the same result when I do the second query, and the resolver on the server isn’t.
It turns out that on my desktop I use &lt;code&gt;systemd-resolved&lt;/code&gt;, which sends requests to Tailscale, which
passes them off to NextDNS. On the server they go straight to Tailscale and then onwards to NextDNS.
So &lt;code&gt;systemd-resolved&lt;/code&gt; is what’s causing the behaviour. Like Simon guessed right at the start.&lt;/p&gt;
&lt;p&gt;But why is it actually doing that? It’s kind of annoying. Well, it’s doing what it’s meant to.
&lt;a href=&#34;https://www.rfc-editor.org/rfc/rfc2308.html#section-5&#34;&gt;RFC2308 says&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A negative answer that resulted from a name error (NXDOMAIN) should
be cached such that it can be retrieved and returned in response to
another query for the same &amp;lt;QNAME, QCLASS&amp;gt; that resulted in the
cached negative response.&lt;/p&gt;
&lt;p&gt;A negative answer that resulted from a no data error (NODATA) should
be cached such that it can be retrieved and returned in response to
another query for the same &amp;lt;QNAME, QTYPE, QCLASS&amp;gt; that resulted in
the cached negative response.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Because the record doesn’t exist at all (there’s no other records for &lt;code&gt;_acme-challenge&lt;/code&gt;: why would
there be?) we get an &lt;code&gt;NXDOMAIN&lt;/code&gt; response, and that “should” be cached for &lt;em&gt;any&lt;/em&gt; query type. So
when Lego queries for the CNAME record, we’re priming the cache with “&lt;code&gt;_acme-challenge&lt;/code&gt; doesn’t
exist” with a potentially huge TTL on that cache entry. When we then try and query the TXT record
we created we get the cached &lt;code&gt;NXDOMAIN&lt;/code&gt; back.&lt;/p&gt;
&lt;p&gt;If the TXT record was created first then the CNAME query would get a &lt;code&gt;NODATA&lt;/code&gt; response instead,
which wouldn’t affect us as that &lt;em&gt;is&lt;/em&gt; understandably cached per query type. But the point in
Lego’s CNAME check is to decide &lt;em&gt;where&lt;/em&gt; to put the TXT record, so it can’t just flip the ordering.&lt;/p&gt;
&lt;p&gt;I apparently dodged this problem entirely on my servers, because nothing in that resolution path
implements this particular caching behaviour.&lt;/p&gt;
&lt;h3 id=&#34;but-why-now&#34;&gt;But why now?&lt;/h3&gt;
&lt;p&gt;But Simon had this working before. It’s possible that his existing deployments are having this issue &lt;em&gt;now&lt;/em&gt;, and are just retrying enough to win the race, but
he would’ve noticed if they failed this much when he set them up.&lt;/p&gt;
&lt;p&gt;So I did some archaeology. Here’s the timeline:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/go-acme/lego/commit/b594acbc2a17f1643568bb62d2ccf8250e444219&#34;&gt;February 2016&lt;/a&gt;: CNAME check added to deal with, well, CNAMEs.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/go-acme/lego/commit/06b3802346b4a4c172c66bcde49532e875dfda2d&#34;&gt;February 2016&lt;/a&gt;: Immediate follow-up making a failed CNAME check non-blocking.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/go-acme/lego/pull/2284&#34;&gt;September 2024&lt;/a&gt;: Option added to check propagation using custom recursive resolvers.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/go-acme/lego/pull/2847&#34;&gt;February 2026&lt;/a&gt;: That option now defaults to on.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And the mystery is solved: Lego v5 enabled the recursive check by default. That also handily explains why
I thought its behaviour was different. It used to be! There were still two things I didn’t understand:
&lt;em&gt;why&lt;/em&gt; was it enabled, and &lt;em&gt;why&lt;/em&gt; didn’t I realise when I upgraded to Lego v5? Looking back, I think this
line of the changelog is meant to cover the behaviour:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[lib,dnsprovider] New approach of the DNS calls&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The migration guide also mentions the flag being renamed, but &lt;em&gt;not&lt;/em&gt; the fact that the
default setting also flipped. Feels like burying the lede a little, but hey ho.&lt;/p&gt;
&lt;p&gt;As for why, there doesn’t seem to be any documented rationale. Given the recursive resolver
check was added to fix a &lt;a href=&#34;https://github.com/go-acme/lego/issues/2276&#34;&gt;fairly niche problem&lt;/a&gt;, it
seems like an odd decision to me. The new defaults mean that any recursive resolver that caches
&lt;code&gt;NXDOMAIN&lt;/code&gt; responses across query types will delay the propagation check, and likely fail it
entirely if the TTL is longer than the timeout. That’s… unfortunate to say the least.
I opened a &lt;a href=&#34;https://github.com/go-acme/lego/discussions/3261&#34;&gt;discussion suggesting the default gets flipped back&lt;/a&gt;
but it doesn’t sound like that will happen before v6.&lt;/p&gt;
&lt;p&gt;For Centauri, at least, I’ve fixed it by unconditionally disabling the recursive resolver check,
taking it back to the Lego v4 behaviour. And after all this, Simon’s original hunch turned out
to be spot on: it &lt;em&gt;was&lt;/em&gt; &lt;code&gt;systemd-resolved&lt;/code&gt; caching, but it was doing the correct thing. Pesky
old-man Lego was the one responsible for dropping a rake on the ground then immediately
stepping on it.&lt;/p&gt;
</content>
    </entry>
    <entry>
        <title>Monthly Meanderings: August 2026</title>
        <link href="https://chameth.com/monthly-meanderings-2026-08/"/>
        <updated>2026-09-01T00:00:00Z</updated>
        <id>https://chameth.com/monthly-meanderings-2026-08/</id>
        <content xml:lang="en" type="html">&lt;p&gt;Another month, another edition of “What’s Chris been up to?”. Fun fact: I very nearly published this post with a “[intro]” placeholder here instead of this
paragraph, despite proofreading it multiple times. “Yep, that’s where the intro will go, all good”. Now instead we have this weirdly meta introduction.&lt;/p&gt;
&lt;h3 id=&#34;website-updates-and-other-projects&#34;&gt;Website updates and other projects&lt;/h3&gt;
&lt;p&gt;A few website updates this month, and one of them neatly addresses the “other projects” bit too. First, I’ve added a new &lt;a href=&#34;https://chameth.com/fitness/&#34;&gt;fitness page&lt;/a&gt; which has some stats
and information about my cycling and running. I’ve also added some fitness-related widgets to support that page and these monthly summaries, and a new music one you can
see a bit further down.&lt;/p&gt;
&lt;p&gt;I’ve also added what I think is quite a fun new feature: a &lt;a href=&#34;https://chameth.com/feeds/posts/build/&#34;&gt;post feed builder&lt;/a&gt; that lets you build RSS feeds containing
only posts that are semantically similar (or dissimilar) to other posts. So if you want to only hear about my fitness journey you can pin a few of those
posts, or if you want to exclude posts that talk about LLMs you can exclude a few of those. It’s basically super-charged fuzzy tags, but it means I don’t
have to actually tag things.&lt;/p&gt;
&lt;p&gt;I blogged about &lt;a href=&#34;https://chameth.com/building-a-family-of-single-user-services/&#34;&gt;building a family of single-user services&lt;/a&gt; which is where most of my coding
energy went this month. Those projects were almost entirely authored by LLMs, and I’m not hating the process. The models have got a lot better at coding,
and I enjoyed getting to focus on the bigger picture aspects rather than getting bogged down in details.
Rest assured, all the writing on my website is still 100% me. I am the load-bearing seam, here.&lt;/p&gt;
&lt;h3 id=&#34;entertainment&#34;&gt;Entertainment&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://chameth.com/monthly-meanderings-2026-03/&#34;&gt;Back in March&lt;/a&gt; I said ‘I can’t do “what I listened to last month” stats until next month’, and then for the next four months completely forgot about it.
Well, not any more! Now I come equipped with some stats about my monthly music habits:&lt;/p&gt;
&lt;div class=&#34;played-albums raised-box&#34; data-title=&#34;Top albums · 1 Aug – 31 Aug 2026&#34;&gt;
    &lt;table class=&#34;plain-table&#34;&gt;
        &lt;thead&gt;
        &lt;tr&gt;
            &lt;th scope=&#34;col&#34;&gt;&lt;/th&gt;
            &lt;th scope=&#34;col&#34;&gt;&lt;/th&gt;
            &lt;th scope=&#34;col&#34;&gt;&lt;/th&gt;
            &lt;th scope=&#34;col&#34;&gt;Album&lt;/th&gt;
            &lt;th scope=&#34;col&#34;&gt;Artist&lt;/th&gt;
            &lt;th scope=&#34;col&#34; class=&#34;numeric&#34;&gt;Unique tracks played&lt;/th&gt;
            &lt;th scope=&#34;col&#34; class=&#34;numeric&#34;&gt;Total tracks played&lt;/th&gt;
        &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;1&lt;/td&gt;
            &lt;td class=&#34;movement movement-up&#34; title=&#34;Up from #4&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-up&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 11l-6 -6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 11l6 -6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/2Eyx1KIPPCkpSjNouXEX9n/cover.jpg&#34; alt=&#34;Cover art for Fallen&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;Fallen&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;Evanescence&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;8&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;40&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;2&lt;/td&gt;
            &lt;td class=&#34;movement movement-up&#34; title=&#34;Up from #5&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-up&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 11l-6 -6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 11l6 -6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/6EwJNS4EVKbNX7fmO7SDUM/cover.jpg&#34; alt=&#34;Cover art for Love, Drugs &amp;amp; Misery&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;Love, Drugs &amp;amp; Misery&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;Eva Under Fire&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;12&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;32&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;3&lt;/td&gt;
            &lt;td class=&#34;movement movement-up&#34; title=&#34;Up from #10&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-up&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 11l-6 -6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 11l6 -6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/3cry6pcCamk2ILXoxbEtP2/cover.jpg&#34; alt=&#34;Cover art for NEON VOID&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;NEON VOID&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;Sumo Cyco&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;6&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;23&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;4&lt;/td&gt;
            &lt;td class=&#34;movement movement-down&#34; title=&#34;Down from #1&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-down&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 13l-6 6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 13l6 6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/6gjRjALcL9fd1Kvbao3cGT/cover.jpg&#34; alt=&#34;Cover art for The Strange Case of…&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;The Strange Case of…&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;Halestorm&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;11&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;21&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;5&lt;/td&gt;
            &lt;td class=&#34;movement movement-up&#34; title=&#34;Up from #7&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-up&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 11l-6 -6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 11l6 -6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/7oyuMHKcWhcDtEN4RRkOmn/cover.jpg&#34; alt=&#34;Cover art for Coming in Hot&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;Coming in Hot&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;DIAMANTE&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;9&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;20&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;6&lt;/td&gt;
            &lt;td class=&#34;movement movement-up&#34; title=&#34;Up from #8&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-up&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 11l-6 -6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 11l6 -6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/2f649h2WKjYf6QAmhhNB3N/cover.jpg&#34; alt=&#34;Cover art for Everest&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;Everest&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;Halestorm&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;7&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;20&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;7&lt;/td&gt;
            &lt;td class=&#34;movement movement-up&#34; title=&#34;Up from #126&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-up&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 11l-6 -6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 11l6 -6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/4m7dM3xxiwL4lJ8Fx3fY45/cover.jpg&#34; alt=&#34;Cover art for Glory Days&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;Glory Days&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;Little Mix&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;5&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;20&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;8&lt;/td&gt;
            &lt;td class=&#34;movement movement-down&#34; title=&#34;Down from #3&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-down&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 13l-6 6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 13l6 6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/1Dvu79hzihfhAKvx3zIO5G/cover.jpg&#34; alt=&#34;Cover art for From Zero&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;From Zero&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;Linkin Park&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;9&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;19&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;9&lt;/td&gt;
            &lt;td class=&#34;movement movement-up&#34; title=&#34;Up from #79&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-up&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 11l-6 -6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 11l6 -6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/1osLFMjRSYHgnTTrEewJuU/cover.jpg&#34; alt=&#34;Cover art for KPop Demon Hunters (Soundtrack from the Netflix Film)&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;KPop Demon Hunters (Soundtrack from the Netflix Film)&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;KPop Demon Hunters Cast&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;7&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;19&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td class=&#34;position&#34;&gt;10&lt;/td&gt;
            &lt;td class=&#34;movement movement-up&#34; title=&#34;Up from #430&#34;&gt;
                &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;16&#34; height=&#34;16&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34; stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;icon icon-tabler icons-tabler-outline icon-tabler-arrow-up&#34; aria-hidden=&#34;true&#34;&gt;&lt;path stroke=&#34;none&#34; d=&#34;M0 0h24v24H0z&#34; fill=&#34;none&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M12 5l0 14&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M18 11l-6 -6&#34;&gt;&lt;/path&gt;&lt;path d=&#34;M6 11l6 -6&#34;&gt;&lt;/path&gt;&lt;/svg&gt;
            &lt;/td&gt;
            &lt;td class=&#34;art&#34;&gt;
                &lt;img src=&#34;https://chameth.com/music/albums/7DnR0XKfZNnAQHPQNe8hoI/cover.jpg&#34; alt=&#34;Cover art for No Need to Argue&#34; loading=&#34;lazy&#34;/&gt;
            &lt;/td&gt;
            &lt;td&gt;No Need to Argue&lt;/td&gt;
            &lt;td class=&#34;artist&#34;&gt;The Cranberries&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;6&lt;/td&gt;
            &lt;td class=&#34;numeric&#34;&gt;19&lt;/td&gt;
        &lt;/tr&gt;
        &lt;/tbody&gt;
    &lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;I suspect I won’t show this every month, as it’s probably going to be quite same-y. I, shockingly, tend to play music that I like more than that which I don’t.
I’ll usually either put on my “favourites” playlist, or my “daily mix” playlist which biases higher-rated tracks over lower ones. So effectively this just
becomes a noisy view over how highly I’ve rated the tracks in an album. Maybe I can control for that, but then I’m not sure what it’d actually be showing…
“Here are all the albums I played this month that I don’t usually play because I don’t like them as much as the other albums I’m not showing any more”?
It’s a bit wordy. Back to the drawing board with that idea, I think.&lt;/p&gt;
&lt;p&gt;I was away for a few weeks this month, including a few days at a small LAN event. Being at a computer gaming event meant that we, naturally, played some board games.
Apparently I hit an h-index of 23 from these plays. That’s 23 games I’ve played at least 23 times each. I’ve been at 22 since December last year: obviously
the higher the number goes, the harder it is to reach. I also go on and off games (and the whole hobby at times), which doesn’t help on that front. Anyway, here’s
&lt;del&gt;Wonderwall&lt;/del&gt; the plays for this month:&lt;/p&gt;
&lt;ul class=&#34;played-boardgames&#34;&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;Codenames (2015)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/178900/image.jpg&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/178900/image.jpg&#34; alt=&#34;Box art of Codenames&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;6 plays&lt;/p&gt;
  &lt;/li&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;Just One (2018)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/254640/image.jpg&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/254640/image.jpg&#34; alt=&#34;Box art of Just One&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;3 plays&lt;/p&gt;
  &lt;/li&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;Martian Fluxx (2009)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/35369/image.jpg&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/35369/image.jpg&#34; alt=&#34;Box art of Martian Fluxx&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;3 plays&lt;/p&gt;
  &lt;/li&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;6 nimmt! (1994)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/432/image.jpg&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/432/image.jpg&#34; alt=&#34;Box art of 6 nimmt!&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;1 play&lt;/p&gt;
  &lt;/li&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;Arkham Horror: Lovecraft Letter (2025)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/424784/image.jpg&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/424784/image.jpg&#34; alt=&#34;Box art of Arkham Horror: Lovecraft Letter&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;1 play&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No films this month, but my &lt;a href=&#34;https://chameth.com/wow/&#34;&gt;World of Warcraft&lt;/a&gt; habit has picked back up with the launch of the new season. They’ve made Mistweaver Monk, my previous specialisation, a
lot less fun to play, so I’m focusing on one of my many alts: a Restoration Shaman:&lt;/p&gt;
&lt;div class=&#34;wow-char-grid&#34;&gt;
&lt;div class=&#34;wow-char raised-box&#34; data-title=&#34;World of Warcraft Character Data&#34;&gt;
&lt;div class=&#34;overview&#34;&gt;
&lt;img src=&#34;https://chameth.com/wow/characters/Hirameth.png&#34; alt=&#34;Hirameth&#34; loading=&#34;lazy&#34;/&gt;
&lt;p class=&#34;detail&#34;&gt;Level 90&lt;/p&gt;
&lt;p class=&#34;detail&#34;&gt;Female Dwarf&lt;/p&gt;
&lt;p class=&#34;detail&#34;&gt;&lt;span class=&#34;wow-class-shaman&#34;&gt;Restoration Shaman&lt;/span&gt;&lt;/p&gt;
&lt;p class=&#34;detail&#34;&gt;320 average item level&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;info&#34;&gt;
&lt;h3 class=&#34;plain-header&#34;&gt;Hirameth&lt;span class=&#34;realm&#34;&gt;-Terenas&lt;/span&gt;&lt;/h3&gt;
&lt;h4 class=&#34;plain-header&#34;&gt;Professions&lt;/h4&gt;
&lt;table class=&#34;plain-table professions&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Midnight Blacksmithing&lt;/td&gt;&lt;td&gt;&lt;progress class=&#34;tier-progress&#34; value=&#34;100&#34; max=&#34;100&#34;&gt;&lt;/progress&gt;&lt;/td&gt;&lt;td class=&#34;tier-count&#34;&gt;100/100&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Midnight Inscription&lt;/td&gt;&lt;td&gt;&lt;progress class=&#34;tier-progress&#34; value=&#34;92&#34; max=&#34;100&#34;&gt;&lt;/progress&gt;&lt;/td&gt;&lt;td class=&#34;tier-count&#34;&gt;92/100&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Midnight Fishing&lt;/td&gt;&lt;td&gt;&lt;progress class=&#34;tier-progress&#34; value=&#34;35&#34; max=&#34;300&#34;&gt;&lt;/progress&gt;&lt;/td&gt;&lt;td class=&#34;tier-count&#34;&gt;35/300&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h4 class=&#34;plain-header&#34;&gt;Mythic+&lt;/h4&gt;
&lt;table class=&#34;plain-table mythic-plus&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Altar of Fangs&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;27:28&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;368&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Den of Nalorakk&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;28:10&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;369&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Kings&amp;#39; Rest&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+14&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;26:20&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;403&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Murder Row&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;25:04&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;375&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Ruby Life Pools&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;25:19&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;369&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Temple of Sethraliss&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;24:13&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;374&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;The Blinding Vale&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;20:23&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;377&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;dungeon&#34;&gt;Voidscar Arena&lt;/td&gt;
&lt;td class=&#34;level&#34;&gt;+12&lt;/td&gt;
&lt;td class=&#34;duration&#34;&gt;23:27&lt;/td&gt;
&lt;td class=&#34;rating&#34;&gt;373&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&#34;total&#34;&gt;
&lt;th colspan=&#34;3&#34; class=&#34;plain-header&#34;&gt;Total M+ rating&lt;/th&gt;
&lt;td class=&#34;rating&#34;&gt;3008&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&#34;links&#34;&gt;
&lt;a href=&#34;https://worldofwarcraft.blizzard.com/en-gb/character/eu/terenas/hirameth&#34;&gt;View on blizzard.com&lt;/a&gt;
&lt;a href=&#34;https://raider.io/characters/eu/terenas/Hirameth&#34;&gt;View on raider.io&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I don’t quite like it as much as Mistweaver in season 1, but it’s a lot nicer than Mistweaver in season 2. You can actually make health bars move when you press your healing buttons, rather than
ignoring half of them because they’re not worth using.&lt;/p&gt;
&lt;h3 id=&#34;fitness&#34;&gt;Fitness&lt;/h3&gt;
&lt;p&gt;My running kick is still going. I’ve “officially” finished the couch-to-5k programme I was doing, and I actually hit 5k on the last day of the programme. I didn’t actually mean to do that:
I was saving the 5k run for this week, but by the time I noticed I’d run “too far” it was close enough that I just kept going. I have an actual 5k race coming up
this month, which is exciting. Once that’s over I’m planning to work my way up to 10k. I’ve added a few widgets to keep track of some stats:&lt;/p&gt;
&lt;div class=&#34;workout-summary-sections&#34;&gt;
&lt;div class=&#34;workout-summary-section raised-box&#34; data-title=&#34;Cycling · 1 Aug – 31 Aug 2026&#34;&gt;
&lt;div class=&#34;stat-row&#34;&gt;
&lt;div class=&#34;stat&#34;&gt;
&lt;p class=&#34;stat-value&#34;&gt;3&lt;/p&gt;
&lt;p class=&#34;stat-label&#34;&gt;activities&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;stat&#34;&gt;
&lt;p class=&#34;stat-value&#34;&gt;6h 5m&lt;/p&gt;
&lt;p class=&#34;stat-label&#34;&gt;total time&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;stat&#34;&gt;
&lt;p class=&#34;stat-value&#34;&gt;103.6km&lt;/p&gt;
&lt;p class=&#34;stat-label&#34;&gt;total distance&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;stat&#34;&gt;
&lt;p class=&#34;stat-value&#34;&gt;42.3km&lt;/p&gt;
&lt;p class=&#34;stat-label&#34;&gt;longest distance&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul class=&#34;workout-summary-pbs&#34;&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;1km&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;2:17&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;1:59&lt;/span&gt;
&lt;/li&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;2km&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;5:34&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;4:29&lt;/span&gt;
&lt;/li&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;5km&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;14:51&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;12:52&lt;/span&gt;
&lt;/li&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;10km&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;36:00&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;27:53&lt;/span&gt;
&lt;/li&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;20km&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;1:43:33&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;1:13:26&lt;/span&gt;
&lt;/li&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;30km&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;no previous&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;1:54:23&lt;/span&gt;
&lt;/li&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;40km&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;no previous&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;2:55:46&lt;/span&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&#34;workout-summary-section raised-box&#34; data-title=&#34;Running · 1 Aug – 31 Aug 2026&#34;&gt;
&lt;div class=&#34;stat-row&#34;&gt;
&lt;div class=&#34;stat&#34;&gt;
&lt;p class=&#34;stat-value&#34;&gt;12&lt;/p&gt;
&lt;p class=&#34;stat-label&#34;&gt;activities&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;stat&#34;&gt;
&lt;p class=&#34;stat-value&#34;&gt;6h 16m&lt;/p&gt;
&lt;p class=&#34;stat-label&#34;&gt;total time&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;stat&#34;&gt;
&lt;p class=&#34;stat-value&#34;&gt;43.1km&lt;/p&gt;
&lt;p class=&#34;stat-label&#34;&gt;total distance&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;stat&#34;&gt;
&lt;p class=&#34;stat-value&#34;&gt;5km&lt;/p&gt;
&lt;p class=&#34;stat-label&#34;&gt;longest distance&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul class=&#34;workout-summary-pbs&#34;&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;800m&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;5:56&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;5:54&lt;/span&gt;
&lt;/li&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;1km&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;7:35&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;7:27&lt;/span&gt;
&lt;/li&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;1 mile&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;12:28&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;12:11&lt;/span&gt;
&lt;/li&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;2km&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;15:35&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;15:13&lt;/span&gt;
&lt;/li&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;3km&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;24:32&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;23:05&lt;/span&gt;
&lt;/li&gt;
&lt;li class=&#34;workout-summary-pb&#34;&gt;
&lt;span class=&#34;pb-trophy&#34;&gt;🏆️&lt;/span&gt;
&lt;span class=&#34;pb-label&#34;&gt;New PB&lt;/span&gt;
&lt;span class=&#34;pb-distance&#34;&gt;5km&lt;/span&gt;
&lt;span class=&#34;pb-previous&#34;&gt;no previous&lt;/span&gt;
&lt;span class=&#34;pb-time&#34;&gt;38:45&lt;/span&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;As well as the running, I did a couple of long (for me) cycle rides, clocking in at around 40 km. It’s a fun loop, but both times I did it I started flagging towards the end. The second time
I made sure to eat and drink plenty along the way, and my legs still raised objections about halfway round. My next goal is 50 km which would let me get to the next town over and back but
the route has a lot more elevation to go with its increased length, so it’s probably going to be quite a bit more taxing. I’m not sure when I’ll attempt that, as the weather is starting to
turn. I was previously bemused when people mentioned “cycling seasons” and switching to indoor trainers. Then I wanted to go out when it was raining, and suddenly I understood. Running in
the rain: surprisingly pleasant; cycling in the rain: no thank you.&lt;/p&gt;
&lt;h3 id=&#34;around-the-web&#34;&gt;Around the web&lt;/h3&gt;
&lt;h4 id=&#34;hacker-news-in-uncompromised-detailhttpsvalerockspostshacker-news&#34;&gt;&lt;a href=&#34;https://vale.rocks/posts/hacker-news&#34;&gt;Hacker News In Uncompromised Detail&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When I first read the title my gut reaction was “I know everything I need to know about Hacker News, right?”.
The title is accurate though. There’s so much interesting stuff, presented really cleanly. It’s a good read.&lt;/p&gt;
&lt;h4 id=&#34;a-proposal-for-new-netflix-content-rating-iconshttpsericwbaileywebsitepublisheda-proposal-for-new-netflix-content-rating-icons&#34;&gt;&lt;a href=&#34;https://ericwbailey.website/published/a-proposal-for-new-netflix-content-rating-icons/&#34;&gt;A proposal for new Netflix content rating icons&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Another dry sounding title, another fun article. It starts with a treatise on rating systems and
ends with a lovely satirical list of rating ideas.&lt;/p&gt;
&lt;h4 id=&#34;find-git-commits-by-commit-message-texthttpshamvockecombloggit-revisions-by-text&#34;&gt;&lt;a href=&#34;https://hamvocke.com/blog/git-revisions-by-text/&#34;&gt;Find Git commits by commit message text&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I like to think I’m relatively well versed in how to use Git. I can do an interactive rebase
and everything! This tip from Ham was new to me, though, and is incredibly useful.&lt;/p&gt;
&lt;h4 id=&#34;novice-marathon-training-planhttpswwwhalhigdoncomtraining-programsmarathon-trainingnovice-1-marathon&#34;&gt;&lt;a href=&#34;https://www.halhigdon.com/training-programs/marathon-training/novice-1-marathon/&#34;&gt;Novice Marathon Training Plan&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Hal Higdon has a whole bunch of training plans. He literally wrote the book on marathon training.
I’ll hopefully be following this plan next year, assuming I manage the 5k, 10k and half-marathon training!
This would be the perfect plan for someone who’s run a half marathon a while ago, and is tempted
to join me in my “marathon before I’m 40” goal…&lt;/p&gt;
</content>
    </entry>
    <entry>
        <title>Building a family of single-user services</title>
        <link href="https://chameth.com/building-a-family-of-single-user-services/"/>
        <updated>2026-08-27T00:00:00Z</updated>
        <id>https://chameth.com/building-a-family-of-single-user-services/</id>
        <content xml:lang="en" type="html">&lt;p&gt;Last month I &lt;a href=&#34;https://chameth.com/tracking-pbs-without-the-dark-patterns/&#34;&gt;made and wrote about&lt;/a&gt; a personal service
to track my workouts and record personal bests. Since then, things have escalated. I now have eight different
services running in the same “family”:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Collegiate Room&lt;/td&gt;
&lt;td&gt;Simple weather overview for my current location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;German Mall&lt;/td&gt;
&lt;td&gt;LLM broker for use by other services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gloss White&lt;/td&gt;
&lt;td&gt;Weight tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Olive Crosses&lt;/td&gt;
&lt;td&gt;Central service hub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pompei Band&lt;/td&gt;
&lt;td&gt;Workout and personal best tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speed Carp&lt;/td&gt;
&lt;td&gt;Spaced repetition system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tiger Island&lt;/td&gt;
&lt;td&gt;Reading history and library tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical Horse&lt;/td&gt;
&lt;td&gt;Location history and analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The names are all anagrams of something related to their function. Finding those anagrams brings me an unreasonable
amount of joy. Unscrambling them is an exercise left to the reader. On top of those eight, I currently have four other
service ideas sat in my todo list. It’s going to be a big family.&lt;/p&gt;
&lt;h3 id=&#34;a-postmortem-on-the-previous-hodge-podge&#34;&gt;A postmortem on the previous hodge-podge&lt;/h3&gt;
&lt;p&gt;Most of this functionality isn’t new to me. When I started out playing around with LLMs, I created a “personal data
aggregator” that just kept getting things bolted on. I &lt;a href=&#34;https://chameth.com/an-app-can-be-a-ready-meal/#what-ive-made&#34;&gt;said at the time&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;My biggest just-for-me project is a web app that I started to help me aggregate film recommendations. It’s since morphed into a general personal data aggregation service: it deals with data from GitHub, Todoist, Letterboxd, TMDB, Healthkit, and others. It also lets me make re-orderable lists, store recipes, and more. Parts of this could definitely be open sourced, and I might carve them out at some point, but it’s mostly a glorious hodge-podge of things specific to me. Having all these services in one place lets me make quick and dirty automations, for example: when I create a Todoist note on my phone or watch, I often forget to set the due date, so it doesn’t show up in the “Today” view. It was literally a few lines of code to plumb things together so any inbox task without a due date gets set to today automatically.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In a twist that &lt;em&gt;nobody&lt;/em&gt; could see coming, it turns out that a “glorious hodge-podge” is actually quite hard to maintain
and very annoying to work with. It didn’t help that it had a fairly extensive Svelte frontend and I really am not a fan
of JavaScript. I ended up using it less and less just because it wasn’t fun to use or work on, and I didn’t want to
waste the limited LLM tokens on doing massive refactors or reworks.&lt;/p&gt;
&lt;p&gt;I’ve got almost two decades of professional software development experience under my belt, so you’d think I would
have learnt the lessons on how to write maintainable software. Hell, my CV even mentions that as something I’m
particularly good at. So what went wrong?&lt;/p&gt;
&lt;p&gt;There were a few factors. One of the obvious things to blame is that it was written mostly with LLMs.
Most of that blame isn’t very honest, though. In the year or so since, LLMs have definitely got better at writing
good code; but I was manually reviewing all the code at the time, and telling it quite carefully what to implement
and how to do it. So the buck really stops with me.&lt;/p&gt;
&lt;p&gt;I think it’s actually more because it was my &lt;em&gt;first&lt;/em&gt; larger project using LLMs. It’s a bit like learning a new
programming language. I don’t think the first sizable project I’ve written in any language has ever been any
good. You can read and learn as much as you want, but you need to actually run into some problems headfirst to
get a grip on how the language works. Or at least I do. LLM coding agents are basically the same deal.&lt;/p&gt;
&lt;p&gt;What the LLMs actually did was mask several problems. The architecture wasn’t right for how the service developed.
It started out as a single-purpose app and gradually got things bolted on, without any major changes. Everything
ended up in horizontal slices: a massive database layer with every query used by every part of the app, a massive
HTTP layer, and so on. Working on a single feature became a tap dance all over the source tree. This sort of
friction is normally quite apparent when you’re writing code by hand, and at some point it hurts enough that
you throw in the towel and do some refactoring. But the LLM will happily plod on without exposing that pain,
so the project needed a more active kind of architecture management that it just didn’t get.&lt;/p&gt;
&lt;p&gt;The other problem they covered up was how brittle and unmaintainable the JavaScript frontend was. I’m not
even sure how I ended up with a Svelte frontend in hindsight. It’s my preferred framework, but I still only
reach for it as a last resort when there’s no other way to avoid using one. It seems like there were lots of
ways to avoid it here, but I seem to have… well… avoided them. Anyway, each feature got its own slew of
Svelte components, without much thought to reusability, shared patterns, or anything sensible. Some of them
were just thin wrappers around data supplied by Go, others had vast swathes of logic in them, with no real
rhyme or reason as to which way that would fall. It’s the same issue as before: a human would look at it,
say “WTF?” and suggest tidying it up; instead, the LLM just gets on with what you tell it to do.&lt;/p&gt;
&lt;p&gt;There’s also a big non-LLM factor: one of the primary reasons all of this unrelated functionality got
tossed into the same app was that I’d previously worked on a few projects with a microservice architecture
and really didn’t like it. My conclusion was they only really worked in large environments where you can
dedicate people or teams to working on all the boilerplate that happens from maintaining lots of
interconnected projects. Dumping everything and the kitchen sink into one badly architected monolith was,
perhaps, not the best rebound from that.&lt;/p&gt;
&lt;h3 id=&#34;pompei-band-and-beyond&#34;&gt;Pompei Band and beyond&lt;/h3&gt;
&lt;p&gt;When I wanted to implement &lt;del&gt;Strava at home&lt;/del&gt; my PB tracking app, it seemed fairly natural to build it out
as a new service, rather than bolt it on to the slowly decomposing hodge-podge that was giving off a funny
smell. Then when I wanted a weather dashboard, why not do it in the same style as Pompei Band? The same
serving architecture, the same page layout… I could even come up with a similar weird two-word name!&lt;/p&gt;
&lt;p&gt;Then wouldn’t it be nice if the weather dashboard knew where I was, so it could show the local weather?
I could just build that functionality in, but it’d also be a good opportunity to carve out the location
history feature from my old service and make a new, shinier one. Then basically every time I even thought
about going near the old service, I mentally sketched out a replacement service.&lt;/p&gt;
&lt;figure class=&#34;image right&#34; data-width=&#34;1230&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/building-a-family-of-single-user-services/oc.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/building-a-family-of-single-user-services/oc.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/building-a-family-of-single-user-services/oc.png&#34; alt=&#34;A screenshot of Olive Crosses, showing links to other services with icons based on their two-word names&#34; loading=&#34;lazy&#34; width=&#34;1230&#34; height=&#34;619&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;Olive Crosses showing links to the other services&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;At one point I had the thought that it’d be nice if the services could all link to one another, but I
didn’t want to have to touch every service whenever I added a new one. So I made a “hub” service, which
maintains the One True Catalogue, and provides a bit of JavaScript the others can all embed to add a
navigation sidebar.&lt;/p&gt;
&lt;p&gt;So after complaining about microservices and JavaScript, it sounds like I have landed on microservices
(or at least miniservices) and JavaScript? Sort of yes, but sort of no. Each service is still its own
thing and makes sense independent of the others. There are some cross-service dependencies, but they’re
very simple and don’t have the same cognitive or boilerplate overhead as a login service that talks to
an authentication service that queries a user manager and so on. And as for the JavaScript, it’s almost
exclusively used for progressive enhancement and a few limited client-side visual things like
maps and graphs. All of the logic lives in Go, which is a lot easier to maintain and test, and all of
the pages are simple Go templates.&lt;/p&gt;
&lt;p&gt;Having these services built in a way that I actually like has motivated me to play around a lot more
with new features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;My workout tracking app pulls photos from my private &lt;a href=&#34;https://immich.app&#34;&gt;Immich&lt;/a&gt; server that were
taken during workouts, and shows them alongside. Now I can see all my obligatory end-of-run selfies
in one place.&lt;/li&gt;
&lt;li&gt;The location history app can detect trips away from home, and get an LLM to automatically title them.
My most recent, very exotic, trip has the title “A fortnight in Milton Keynes and Coventry”. Each
trip gets its own little map, and in the future I want to pull in images like with workouts.&lt;/li&gt;
&lt;li&gt;My weight tracking app now exports weigh-ins to Garmin, as it doesn’t fully integrate with Apple
Health (it will export its own weights, but not import others, like the ones from my Withings scales).
Stupid walled gardens, but thankfully I can fix it myself.&lt;/li&gt;
&lt;li&gt;My book tracking app automatically cleans up the titles of books from the nonsense provided by Amazon
to something more palatable. “Killing Eve: Long Shot: A twist on obsession, betrayal and revenge from
Luke Jennings, author of the TV sensation Killing Eve” becomes “Long Shot (Killing Eve #5)”. It does
this by passing the title to an LLM with a bunch of rules.&lt;/li&gt;
&lt;li&gt;The hub generates me a snippet of Tailscale configuration that defines the minimal grants needed to
allow the intra-service dependencies to work.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The LLM integrations were inspired by my friend &lt;a href=&#34;https://dataforce.org.uk/&#34;&gt;Shane&lt;/a&gt; who recently
demonstrated an inventory tracking app he’d made, which used Haiku to process images of boxes and
describe the contents. I’ve dabbled with LLM integration before, and always disliked the process
of fiddling with the prompts, and debugging when the LLM gave a nonsensical response. So I built
German Mall, which makes it a lot easier. It lets me define a “workflow” consisting of a system
prompt, initial message, and expected response format. I can browse past runs, tweak the prompt,
rerun them to see the change, all without touching the services using them. Quick iteration loops
are the best. German Mall also provides markdown documentation for each workflow, so integrating
it downstream is simply a case of pointing a coding agent at the right documentation endpoint.&lt;/p&gt;
&lt;figure class=&#34;image full&#34; data-width=&#34;1482&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/building-a-family-of-single-user-services/gm.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/building-a-family-of-single-user-services/gm.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/building-a-family-of-single-user-services/gm.png&#34; alt=&#34;A screenshot of German Mall, showing the workflow configuration for tidying book titles: a form with fields for description, system prompt, user template, output schema, model, max tokens and temperature.&#34; loading=&#34;lazy&#34; width=&#34;1482&#34; height=&#34;1080&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;The “tidy book titles” workflow in German Mall&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It’s not all fun and games: having a bunch of services all following the same pattern means
you need to do cross-cutting maintenance on them now and then. But that’s surprisingly easy with LLMs.
I put all the projects in a common folder, and I launch cross-cutting tasks from that folder. Usually
I’ll make the change in one project, and then the cross-cutting task is simply “Look at commit XYZ in
./foo, make the same change to the other projects”.&lt;/p&gt;
&lt;p&gt;I’ve also ended up with a shared library used by all the projects to cut down on some boilerplate.
That’s normally something I’d resist, especially with personal projects, just because the extra overhead
of working in a separate project, managing releases of it, and so on, is a bit annoying. But, again,
LLMs can do almost all of that grunt work. They effectively reduce the “cost” of maintaining separate
services to a negligible level, leaving me to focus on the more fun aspects like coming up with new names.&lt;/p&gt;
</content>
    </entry>
</feed>
