<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" version="2.0">
  <channel>
    <title>jonno.nz</title>
    <link>https://jonno.nz/</link>
    <atom:link href="https://jonno.nz/feed.xml" rel="self" type="application/rss+xml"/>
    <description>Tech, teams, and projects — John Gregoriadis</description>
    <lastBuildDate>Wed, 09 Sep 2026 01:58:06 GMT</lastBuildDate>
    <language>en</language>
    <generator>Lume v2.4.2</generator>
    <author>
      <name>John Gregoriadis</name>
      <uri>https://jonno.nz</uri>
    </author>
    <item>
      <title>The tunnel</title>
      <link>https://jonno.nz/posts/the-tunnel/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/the-tunnel/</guid>
      <description>A short story about the darkness that keeps you safe.</description>
      <content:encoded>
        <![CDATA[<figure class="wide">
  <img src="https://jonno.nz/img/posts/the-tunnel-hero.jpg" alt="Abstract layers of charcoal and blue-black, with smoky copper embers along the edge." width="1774" height="887" loading="eager" fetchpriority="high">
</figure>
<p>The ground starts moving before you see the fire.</p>
<p>You spread your feet, reach for something solid. A faint voice echoes with your
name, but the sound arrives too slowly. Even your hands seem far away.</p>
<p>Then the storm comes through, and the world you recognise begins to burn.</p>
<p>You step into the darkness.</p>
<p>From inside, you can’t tell. You reach out on either side and find nothing.
There are no walls to follow, no curve or corner, no faint light to walk
towards.</p>
<p>There is ground beneath your feet. You test it with your weight, then take
another step.</p>
<p>You look back for anyone, someone, who made the echoes. You want them to follow.
You wait with your hand stretched out until your shoulder aches, but this place
will only hold you.</p>
<p>You lower your arm.</p>
<p>For a while, you count your footsteps. At a thousand, you start again. Somewhere
through the next thousand, you lose your place and begin at one, though you
can’t remember why the number matters.</p>
<p>You sleep. You wake with your cheek against the ground. You have no way of
knowing how long you’ve been here.</p>
<p>Outside, you hear something collapse.</p>
<p>You know that sound. You try to remember the rooms as they were, the things you
left where you expected to find them again. You could have walked through that
life with your eyes closed.</p>
<p>You know you will never go back.</p>
<p>Nothing will ever be the same again.</p>
<p>The fire keeps burning.</p>
<p>After a time, you notice that you can breathe. You hold out your hands. Though
you can’t see them, you can feel the skin, cool and unbroken beneath your
thumbs. The heat hasn’t reached you.</p>
<p>You sit with that for a long while.</p>
<p>Beyond this darkness, the fire is taking everything you thought you would return
to. In here, your soul has somewhere to survive it.</p>
<p>You wish knowing that made the walking easier.</p>
<p>Your feet hurt. You grow tired of waking to the same blackness. Sometimes you
stand still because you can no longer believe that moving makes any difference.
On those days, you can’t imagine an end. You struggle to remember having a life
before this.</p>
<p>There is nobody beside you to say how far you’ve come.</p>
<p>Then you get up.</p>
<p>You walk until you need to rest. You rest until you can walk. Sometimes you have
enough faith for hours. Sometimes you have to stop and find it again with one
foot still lifted from the ground.</p>
]]>
      </content:encoded>
      <pubDate>Sun, 06 Sep 2026 12:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/img/posts/the-tunnel-hero.jpg"/>
    </item>
    <item>
      <title>Shorten the distance to control</title>
      <link>https://jonno.nz/posts/shorten-the-distance-to-control/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/shorten-the-distance-to-control/</guid>
      <description>Learned helplessness grows when action stops changing the outcome. Sparring trains the return to control.</description>
      <content:encoded>
        <![CDATA[<p>Helplessness grows in the distance between the hit and your first act of
control.</p>
<p>In boxing, the hit might be a jab you never saw. Outside the ring, it might be a
rejection, a failure or a year where nothing works. The longer you go without
affecting what happens next, the easier it becomes to stop trying.</p>
<p>Modern
<a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC4920136/">learned helplessness research</a>
suggests the name is slightly backwards. Passivity under prolonged stress is the
default. Control is what gets learned.</p>
<p>In a <a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC7503322/">2020 experiment</a>, 100
adults experienced aversive noise they rated as equally unpleasant. The people
who could stop it reported less helplessness and responded faster than those
whose actions made no difference.</p>
<p>Sparring compresses that lesson into seconds. You get hit, make an adjustment
and change the next exchange.</p>
<h2>Law one: Find the smallest controllable lever</h2>
<p>After taking a clean shot, do not try to win the whole round back.</p>
<p>Exhale. Rebuild your guard. Move one foot. Find the smallest action that changes
what happens next.</p>
<p>The lever does not need to solve the whole problem. It only needs to reconnect
action with outcome.</p>
<p>Helplessness looks at everything and freezes. Control starts with the next
adjustable variable.</p>
<h2>Law two: Choose your stress instead of receiving it</h2>
<p>You cannot choose every hit, but you can influence what comes next.</p>
<p>Positioning and footwork narrow your opponent’s options. Change the range, close
an angle or offer a target, and you can draw them into a sequence you recognise.
Their response is never guaranteed, but it becomes less random.</p>
<p>You are no longer waiting for pressure to arrive. You are helping shape it, then
acting inside a pattern you have trained for.</p>
<p>Good sparring is voluntary and bounded. You choose the partner, intensity and
rules. There is a bell, a coach and a genuine option to stop.</p>
<p>Chosen stress still hurts. The difference is that you meet it with agency.</p>
<h2>Law three: Be flexible, not rigid</h2>
<p>A rigid boxer has one answer. When it stops working, the whole plan falls apart.</p>
<p>A flexible boxer changes range, rhythm, guard or direction. They do not need the
round to follow the original plan. They keep finding new ways to act.</p>
<p>Control does not mean forcing the world to obey you. It means retaining options
as the situation changes.</p>
<p>Rigidity tries to prevent the hit. Flexibility helps you recover from it.</p>
<h2>Law four: Build the thing the hit cannot touch</h2>
<p>If your confidence depends on never getting hit, the first clean jab owns it.</p>
<p>Build your identity around what remains available: attention, standards,
discipline and the willingness to return to your stance.</p>
<p>A punch can take the exchange. It does not get to decide who you are afterwards.</p>
<p>You will not control every round.</p>
<p>You can train the return.</p>
]]>
      </content:encoded>
      <pubDate>Tue, 01 Sep 2026 00:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/shorten-the-distance-to-control.png"/>
    </item>
    <item>
      <title>How Many Days of AI Does New Zealand Have?</title>
      <link>https://jonno.nz/posts/how-many-days-of-ai-does-new-zealand-have/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/how-many-days-of-ai-does-new-zealand-have/</guid>
      <description>
        AI sovereignty will not live in a flag-branded chatbot. It will live in data centres, power stations and contracts that let allied models run here.
      </description>
      <content:encoded>
        <![CDATA[<p>New Zealand knows how many days of diesel it has.</p>
<p>At 11:59pm on Sunday 23 August,
<a href="https://www.mbie.govt.nz/building-and-energy/energy-and-natural-resources/energy-generation-and-markets/liquid-fuel-market/fuel-supply-disruption-response/fuel-stock-and-shipping-updates">the answer was 44.8 days</a>
in the country or on ships headed here. Petrol was 49.2 days. Jet fuel was 42.</p>
<p>A separate Crown-controlled diesel reserve at Marsden Point sat outside those
numbers. MBIE publishes the count every Wednesday.</p>
<p>We do not know how many days of AI New Zealand has.</p>
<p>Days may be the wrong unit. You can put diesel in a tank. You cannot put next
month's inference in a tank. A token is made at the moment somebody asks for it,
using a model, a rack of accelerators, electricity, cooling, storage, networks
and people.</p>
<p>That makes the more useful question harder.</p>
<p>If foreign AI suppliers stopped taking our orders, how much useful inference
could New Zealand produce for itself?</p>
<p>At the moment, not enough of the answer is under our control.</p>
<p>A modest version of this argument says we should keep some open-weight models,
make systems portable and sign backup contracts. We should do all of that. It is
no longer enough.</p>
<p>New Zealand should build nationally controlled data centres and the power
infrastructure behind them. We should negotiate the right to run allied models
on those machines. We should keep using the best global services in normal
times, but own a strategic layer that nobody overseas can switch off for us.</p>
<p>We do not need to build a frontier model yet. We need to secure the means of
inference.</p>
<h2>Forty-four point eight days</h2>
<p>New Zealand's
<a href="https://www.mbie.govt.nz/building-and-energy/energy-and-natural-resources/energy-generation-and-markets/liquid-fuel-market/fuel-security-in-new-zealand/minimum-stockholding-obligation">minimum stockholding rules</a>
took effect in January 2025. Fuel importers must hold an average of 28 days of
petrol, 24 days of jet fuel and 21 days of diesel, either in eligible tanks here
or aboard ships inside our Exclusive Economic Zone.</p>
<p>Those are not literal countdown clocks. The obligation applies to individual
importers over a month, using historical demand. Distribution matters. Demand
can be reduced. A ship inside the EEZ counts even though the fuel is not yet in
a terminal. The public totals also include ships outside the EEZ that may still
be three weeks away.</p>
<p>The numbers are still useful. They tell the Government what exists, where it is
and how much time it might have if the next shipment does not arrive.</p>
<p><img src="https://jonno.nz/img/posts/how-many-days-of-ai-does-new-zealand-have-fuel-clock.svg" alt="New Zealand's fuel cover on 23 August 2026, split between in-country stock and cargo inside and outside the EEZ. The statutory references and separate Crown diesel reserve are noted."></p>
<p>The rules got a live test fourteen months after they began. The United States
and Israel
<a href="https://commonslibrary.parliament.uk/research-briefings/cbp-11075/">launched strikes on Iran on 28 February
2026</a>, and
Iran retaliated across the region.
<a href="https://www.mbie.govt.nz/about/news/impact-of-the-middle-east-conflict-on-our-fuel-security">Tanker traffic through the Strait of Hormuz fell to a near
standstill</a>.
By August, the
<a href="https://www.iea.org/reports/oil-market-report-august-2026">International Energy Agency was still
describing</a> the
passage as effectively closed.</p>
<p>This was not a distant problem for an island at the bottom of the Pacific.
Marsden Point stopped refining crude in 2022, so New Zealand now imports all of
its petrol, diesel and jet fuel as finished products.
<a href="https://www.mbie.govt.nz/building-and-energy/energy-and-natural-resources/energy-generation-and-markets/liquid-fuel-market/fuel-supply-disruption-response/fuel-security">In 2025, 51% came from
South Korea, 31% from Singapore, 9% from Malaysia and 3% from
Japan</a>.</p>
<p>We do not buy all of that fuel directly from the Gulf. We buy from Asian
refineries that buy crude in a global market shaped by the Gulf. One step
removed is not independent.</p>
<p>The pumps did not run dry. New Zealand remains in Phase 1 of the
<a href="https://www.mbie.govt.nz/building-and-energy/energy-and-natural-resources/energy-generation-and-markets/liquid-fuel-market/fuel-supply-disruption-response">Fuel Response Plan</a>,
with supply arriving normally and no purchase restrictions.</p>
<p>Prices carried the shock instead. In the
<a href="https://www.comcom.govt.nz/assets/Uploads/Fuel-price-monitoring-Data-to-18-August-2026.xlsx">Commerce Commission's national-average retail
series</a>,
regular 91 rose from $2.57 a litre on 28 February to a peak of $3.50 on 8 April.
Diesel rose from $1.88 to $3.89 by 13 April, slightly more than doubling.</p>
<p>The Government used the time in the buffer to create more options. It
<a href="https://www.beehive.govt.nz/release/government-widens-fuel-supply-options">temporarily accepted Australian fuel
specifications</a>
so importers could buy from more refineries. It contracted Z Energy to manage
<a href="https://www.beehive.govt.nz/release/new-zealand%E2%80%99s-strategic-diesel-reserve-ready-go">90 million litres of strategic diesel</a>
at Marsden Point, roughly nine days of normal consumption, with the Crown
controlling its release. In July, an
<a href="https://www.beehive.govt.nz/release/world-first-new-zealand-singapore-essential-supplies-agreement-now-force">essential supplies agreement with
Singapore</a>
came into force.</p>
<p>The buffer was never going to make New Zealand energy independent. Its job was
to buy time while the Government added options around the supply chain.</p>
<p>That is the useful part of the analogy.</p>
<h2>A token is not a barrel</h2>
<p>People keep calling AI the new oil. I understand why.</p>
<p>Both have concentrated choke points. Advanced chips are fabricated in a small
number of places. The latest accelerators are designed by a small number of
companies. Frontier models are controlled by an even smaller group. Access can
be rationed by price, contract, export controls or geopolitics.</p>
<p>The metaphor breaks at the important bit. Oil can be stored. Tokens cannot. They
are not even a standard commodity. A million tokens from one model are not
equivalent to a million from another, and a cheap answer that is wrong is not a
strategic reserve.</p>
<p>Compute is closer to the refinery. Inference is the utility it produces.</p>
<p>Electricity goes in. Useful machine work comes out. The quality and quantity
depend on the chips, model, software and skills available at that moment.</p>
<p>The national asset is therefore not a model file. It is a working conversion
system that New Zealand can allocate when other suppliers become unreliable.</p>
<p>That distinction matters because dependency is arriving quietly. In the
<a href="https://www.digital.govt.nz/dmsdocument/264~report-2026-cross-agency-survey-for-artificial-intelligence-ai-use-cases/html">2026 cross-agency survey</a>,
59 public organisations reported 545 AI use cases, up from 272 the year before.
Of those, 167 were operational, three times the 2025 count. Most helped staff
and back-office functions, but more than half directly or indirectly supported
public-facing services.</p>
<p>New Zealand does not stop functioning if frontier-model APIs go offline today.
Claims that AI is already critical national infrastructure run ahead of the
evidence.</p>
<p>The direction is still clear. The
<a href="https://www.digital.govt.nz/assets/Standards-guidance/Technology-and-architecture/AI/Public-Service-AI-work-programme-to-2027-A3.v1.pdf">Public Service AI Work Programme</a>
includes shared tools, strategic supplier agreements, an AI marketplace and a
public-facing assistant for Govt.nz. Businesses are building the same dependency
into software delivery, document processing, customer support, fraud detection
and research.</p>
<p>Dependency arrives one useful workflow at a time. Nobody holds a ceremony when
the manual fallback quietly stops being maintained.</p>
<h2>A cloud region is not a reserve</h2>
<p>New Zealand has made real progress on local cloud infrastructure.</p>
<p>Microsoft opened its
<a href="https://news.microsoft.com/en-nz/2024/12/12/new-zealands-first-hyperscale-cloud-is-open-for-business/">three-zone New Zealand North region in December
2024</a>.
AWS opened
<a href="https://press.aboutamazon.com/2025/9/amazon-launches-infrastructure-region-in-new-zealand">three availability zones here in September
2025</a>.
That improves latency, resilience and data residency. It is good infrastructure
and we should want more of it.</p>
<p>It is not the same as sovereign compute.</p>
<p>When Amazon Bedrock launched at the New Zealand endpoint in March 2026, AWS said
the initial Anthropic and Amazon models were available using
<a href="https://aws.amazon.com/about-aws/whats-new/2026/03/amazon-bedrock-asia-pacific-new-zealand/">cross-region inference</a>.
The front door was in New Zealand. The model execution did not have to be.</p>
<p>Even when the hardware is physically here, the owner, control plane, operating
keys, model licence and parent jurisdiction can remain somewhere else. Data
residency answers where the data is meant to sit. Sovereignty asks who can keep
the service running, change it, inspect it and allocate it during a crisis.</p>
<p><img src="https://jonno.nz/img/posts/how-many-days-of-ai-does-new-zealand-have-control-stack.svg" alt="A local cloud region is compared with a Crown-controlled strategic slice through power, data centres, accelerators, operating keys and deployable model rights."></p>
<p>New Zealand is not starting from zero. NeSI's standalone MBIE investment
<a href="https://www.mbie.govt.nz/science-and-technology/science-and-innovation/funding-information-and-opportunities/investment-funds/strategic-science-investment-fund/funded-infrastructure/new-zealand-escience-infrastructure">concluded on 30 June 2025</a>,
but its staff, assets and services were folded into the Crown-owned REANNZ the
next day. The resulting
<a href="https://www.mbie.govt.nz/science-and-technology/science-and-innovation/funding-information-and-opportunities/investment-funds/strategic-science-investment-fund/funded-infrastructure/eresearch-infrastructure-platform">eResearch Infrastructure Platform</a>
has $69.65 million of government funding contracted through June 2030. It
combines REANNZ's network with the former NeSI high-performance computing
services. Earth Sciences New Zealand has a fourth-generation supercomputer, and
local providers such as
<a href="https://catalystcloud.nz/services/iaas/compute/compute-c2-gpu/">Catalyst</a> and
<a href="https://datacom.com/nz/en/solutions/cloud/hybrid-and-private/sovereign">Datacom</a>
offer New Zealand-operated GPU and sovereign-cloud services.</p>
<p>Those are foundations to build on. They are not yet a published national reserve
for public services and the wider economy. The two-site reserve I propose below
does not exist today. REANNZ's August 2026 hardware inventory, for example,
lists
<a href="https://docs.nesi.org.nz/Batch_Computing/Hardware/#gpus">48 GPUs across four types</a>.
That is valuable research infrastructure. It is not a frontier-scale fleet.</p>
<p>REANNZ has explored something larger. In November 2025 it issued a request for
information for a
<a href="https://www.reannz.co.nz/news-and-events/request-for-information-ai-infrastructure-platform">national AI infrastructure platform</a>.
But its June 2026 performance plan still said it would
<a href="https://www.reannz.co.nz/assets/Uploads/Statement-of-Performance-Expectations/REANNZ-SPE-2026.pdf">work with officials to secure funding for AI infrastructure at
scale</a>.
That project was still an ambition, not a reserve New Zealand could count on.</p>
<p>Nor do we have a public inventory showing how many accelerators exist across the
country, which workloads they can run, who controls their schedulers or what
remains usable if international networks and vendor control planes are
unavailable.</p>
<p>We have data-centre geography. We have not yet demonstrated sovereign frontier
inference.</p>
<h2>Power is the reserve</h2>
<p>The first limit on domestic compute is not ambition. It is electricity.</p>
<p>New Zealand's power system set a new demand record of
<a href="https://www.transpower.co.nz/news/new-zealands-electricity-use-hits-all-time-high-temperatures-plummet">7,415 MW on 6 August 2026</a>.
The system stayed up, but low wind and cold weather made it tight enough for
large industrial users to reduce demand.</p>
<p>The price story is less comfortable. Across households and small businesses,
power prices rose by an average
<a href="https://www.ea.govt.nz/news/general-news/data-shows-68-increase-to-power-bills-across-households-and-small-businesses/">8% in 2025 and another 6.8% in the first half
of 2026</a>.
Higher lines charges caused 54% of the latest increase and are expected to keep
rising through 2030 as the country replaces old assets and expands the grid.</p>
<p>Generation became more expensive too. The average monthly wholesale price at the
important Ōtāhuhu node was about $100 per MWh from 1997 to 2018. From 2019
to early 2026 it averaged <a href="https://www.ea.govt.nz/news/eye-on-electricity/breaking-the-link-between-gas-supply-and-power-prices-and-what-it-means-for-new-zealands-energy-future/">$160 per
MWh</a>,
with declining domestic gas supply a major cause.</p>
<p>There is plenty to like in the underlying system. In 2025, New Zealand generated
44,140 GWh of electricity and
<a href="https://www.mbie.govt.nz/building-and-energy/energy-and-natural-resources/energy-statistics-and-modelling/energy-publications-and-technical-papers/energy-in-new-zealand/energy-in-new-zealand-2026/electricity">88.5% came from renewable sources</a>.
New geothermal, wind, solar, hydro upgrades and batteries are arriving. The
problem is making enough firm power available at the right place and time,
including cold, dry, windless periods.</p>
<p><img src="https://jonno.nz/img/posts/how-many-days-of-ai-does-new-zealand-have-power.svg" alt="Four measures of New Zealand's power constraint: recent retail increases, the long-run wholesale shift, record peak demand and Transpower's 2035 data-centre scenarios."></p>
<p>AI makes that physical constraint harder to ignore. Transpower's draft
<a href="https://static.transpower.co.nz/public/bulk-upload/documents/System%20Operator%20Strategy%20Phase%202%20Strategic%20Priorities.pdf?VersionId=Wk9OwooL_E5PRNCT0JZtCD4wnpWsCCFb">2026 System Operator Strategy</a>
puts 2035 data-centre nameplate demand between 350 MW and 700 MW across five
scenarios. Transpower is careful to say estimates of AI's share rely on
imperfect proxies. These are scenarios, not a forecast.</p>
<p>Even the low end is material on a grid whose record peak is 7.4 GW.</p>
<p>Cheap and reliable energy is not a magic GDP dial. Economies can become more
energy efficient, and electricity alone does not create productive companies.
But energy is enabling infrastructure. When it becomes expensive or scarce, it
constrains investment and output.</p>
<p>Modelling commissioned by MBIE estimated that higher electricity and gas prices
since 2017 left New Zealand's real GDP in 2025 about
<a href="https://www.mbie.govt.nz/dmsdocument/31665-government-response-to-review-of-electricity-market-performance-enhancing-new-zealands-security-september-2025-proactiverelease-pdf">1.25%, or $5.2 billion, below a lower-price
counterfactual</a>.
That is a modelled scenario, not an observed one-for-one law. It is still a
large estimate of the cost of getting this input wrong.</p>
<p>For AI, the connection is unusually direct. Electricity and chips are converted
into an input used to write software, discover materials, analyse data and run
services. A country with abundant, firm power can make more of that input. A
country without it rents the output from somewhere else.</p>
<p>This creates a hard rule for any national compute programme: every megawatt of
new strategic compute should arrive with genuinely additional generation,
firming and grid capacity.</p>
<p>Renewable certificates are not enough. A data centre that claims an annual wind
offset while drawing scarce power on a cold, still morning has not solved the
system problem. Large compute loads should pay their full connection and
transmission costs, be interruptible outside protected workloads, and underwrite
the new generation and storage needed to serve them.</p>
<p>Otherwise sovereign AI becomes a scheme in which households and existing
industry subsidise somebody else's server racks.</p>
<h2>Nationalise the strategic layer</h2>
<p>By nationalise, I do not mean seize every AWS rack or put the whole electricity
market inside a ministry.</p>
<p>New Zealand already owns useful levers. Transpower is a state-owned enterprise.
The Crown retains controlling stakes in Genesis, Mercury and Meridian. Many
lines companies are owned by councils or community trusts.
<a href="https://www.ea.govt.nz/your-power/new-zealands-electricity-sector/">The ownership mix is already partly
public</a>. We
should use that public balance sheet and control deliberately.</p>
<p>I would create a Crown-controlled compute utility.</p>
<p>It should own the strategic assets: land, substations, accelerator hardware,
operating keys, the capacity scheduler and the contracts that determine who gets
access. Private companies can design, build and operate parts of it.
Universities, local cloud providers, iwi investment entities and New Zealand
institutional capital should participate. The Crown should retain majority
control or a golden share over the emergency capability.</p>
<p>Start with a modular 2 to 5 MW accelerator reserve spread across two physically
separated sites. Depending on the hardware and design, that is roughly hundreds
to a low thousand modern accelerators. It is not enough to train the next
frontier model. It is enough to run useful national inference, adapt open
models, evaluate new releases, support research and keep selected services
alive.</p>
<p>Build the sites, substations and generation contracts with an owned path to
expand. Capacity should grow when audited demand justifies it, not when a
minister needs a large number for a press release.</p>
<p><img src="https://jonno.nz/img/posts/how-many-days-of-ai-does-new-zealand-have-reserve.svg" alt="A proposed national compute reserve combines a Crown-controlled baseline, reserved commercial capacity and allied compacts, all built on additional electricity infrastructure."></p>
<p>The utility would serve three markets in normal times. Researchers and public
interest projects receive allocated access. New Zealand startups and smaller
firms buy subsidised capacity where a lack of compute blocks growth. Commercial
users pay market rates and help keep the fleet current. A protected slice stays
available for continuity testing and emergencies.</p>
<p>This is not an invented category. Canada has committed up to
<a href="https://ised-isde.canada.ca/site/ised/en/canadian-sovereign-ai-compute-strategy">C$2 billion to
domestic commercial capacity, public supercomputing and a compute access
fund</a>.
The United Kingdom's public AI Research Resource includes
<a href="https://www.gov.uk/government/publications/ai-research-resource/airr-advanced-supercomputers-for-the-uk">5,448 Nvidia GH200 superchips at Isambard-AI and 1,024 Intel GPUs at
Dawn</a>.</p>
<p>New Zealand is smaller. That is an argument for selecting the useful layer, not
for owning none of it.</p>
<p>The model strategy should be allied and plural.</p>
<p>For open-weight models, keep signed copies of the weights, licences, runtimes,
software bills of materials and evaluations here. Test them on the reserve every
month. An untested fallback discovered during the outage is not a fallback.</p>
<p>For closed frontier systems, do not pretend an API contract gives us the
weights. It usually will not. Negotiate reserved capacity, local-processing
evidence, exit clauses, portable prompts and logs, customer-held keys and tested
failover. Where a supplier will licence a model for on-premises deployment, make
that part of a government or allied procurement deal.</p>
<p>Then build reciprocal compute agreements with Australia, Canada, the United
Kingdom and Singapore. The aim is not autarky. It is enough local control and
enough allied alternatives that one foreign company's decision does not become
New Zealand's outage.</p>
<h2>Nuclear-free does not have to mean reactor-free</h2>
<p>If compute becomes an economic input on the scale I expect, generation becomes
technology policy.</p>
<p>New Zealand should build more geothermal, wind, solar, hydro upgrades, batteries
and transmission now. These are available, increasingly economic and can add
capacity this decade.</p>
<p>We should also make civilian nuclear generation a real option.</p>
<p>That does not require abandoning New Zealand's opposition to nuclear weapons.
The
<a href="https://www.legislation.govt.nz/act/public/1987/0086/latest/whole.html">Nuclear Free Zone, Disarmament, and Arms Control Act
1987</a>
bans nuclear explosive devices and visits by nuclear-powered ships. It does not
expressly ban a stationary civilian power station. In May 2026, New Zealand
itself told the Nuclear Non-Proliferation Treaty review conference that while we
do not use nuclear energy,
<a href="https://www.mfat.govt.nz/en/media-and-resources/nuclear-non-proliferation-treaty-2026-review-conference-main-committee-iii">we recognise states' right to do so</a>
under proper safety, security and waste standards.</p>
<p>The legal misunderstanding is the easy part.</p>
<p>New Zealand does not have a modern reactor regulator, an experienced nuclear
workforce, a civil-liability regime or a pathway for spent fuel and
decommissioning. The International Atomic Energy Agency's
<a href="https://nucleus.iaea.org/sites/nids/capacity/milestones/SitePages/Home.aspx">milestones approach lists 19 separate infrastructure
issues</a>
a new nuclear country must resolve. Building that capability takes longer than
building the plant.</p>
<p>The economics are not ready either. New Zealand's grid makes a conventional
gigawatt-scale reactor awkward. One unit would equal roughly 13% of our latest
record peak, a very large single failure for the system to cover.</p>
<p>Small modular reactors fit our scale better. They are also still an emerging
commercial product. The IEA expects the
<a href="https://www.iea.org/reports/the-path-to-a-new-era-for-nuclear-energy/executive-summary">first commercial SMR projects around
2030</a>.
Australia's 2025–26 GenCost work estimates 2030 SMR electricity at
<a href="https://www.csiro.au/-/media/Energy/GenCost-2025-26-Final/GenCost_2025-26_Final_Report_20260715.pdf">A$336 toA$546 per MWh</a>,
far above its estimates for wind and solar. Those are Australian modelling
assumptions, not New Zealand bids, but a small first-time market is unlikely to
begin with a cost advantage.</p>
<p>So no, a reactor will not lower next winter's power bill or keep an API online
next year.</p>
<p>That is not an argument for refusing to prepare. It is an argument for starting
before we need one.</p>
<p>I would commission a 12 to 18 month civilian nuclear-readiness programme now. It
should compare small reactors with geothermal, storage, transmission and other
firm generation on total system cost. It should map plausible sites, seismic and
cooling constraints, grid integration, regulation, fuel, waste, insurance,
workforce and emergency planning. It should include iwi and host communities
before a site appears on a map, not after a deal is announced.</p>
<p>Deployment should have hard gates: a reference reactor operating in a comparable
OECD market, an all-in price that can beat New Zealand's firm alternatives, an
independent regulator, a funded waste plan and durable host consent.</p>
<p>I think we should prepare to build if those tests are met.</p>
<p>Keeping nuclear weapons out of New Zealand is a moral and strategic position.
Refusing to examine civilian reactors is not the same thing. It is simply
choosing to have one fewer option in an electricity-constrained future.</p>
<h2>Whose sovereignty?</h2>
<p>Public ownership solves only part of this.</p>
<p>Fuel does not have a language, a history or an opinion about who owns knowledge.
Models do.</p>
<p>Manatū Taonga's
<a href="https://www.mch.govt.nz/publications/long-term-insights-briefing-2025">2025 Long-term Insights Briefing</a>
warns that global models can misappropriate Māori cultural intellectual
property, flatten differences between iwi and hapū, and present one answer as
correct where several traditions exist.</p>
<p>A model running on Crown-owned GPUs does not solve that. Feeding Māori language,
history and knowledge into a national system without Māori authority over how it
is used would reproduce the extraction with a New Zealand data centre attached.</p>
<p>The compute utility therefore needs Māori governance over Māori data, knowledge
and language, not a consultation workstream added after procurement. The same
applies to the land, water and generation used by its data centres.</p>
<p>It also needs public allocation rules. In a constrained period, who gets the
machines? Emergency management? Hospitals? The electricity system itself?
Science? Export firms? The answer should be decided before the queue forms and
published wherever national security allows.</p>
<p>National control without legitimate governance is only a different owner.</p>
<h2>The ability to make inference</h2>
<p>New Zealand does not need its own OpenAI yet.</p>
<p>It needs a national compute inventory, two controlled sites, additional power,
tested open-weight models, allied deployment rights, portable systems and people
who rehearse the switch. It needs to know what runs on day one, day seven and
day thirty when the normal suppliers are not available.</p>
<p>The fuel reserve buys time because somebody counted the tanks, wrote the rules
and kept a physical option under Crown control.</p>
<p>An AI reserve will not be a warehouse full of tokens. It will be a factory we
can turn on.</p>
<p>A country that cannot turn its own electricity into trusted inference is renting
part of its future productive capacity by the token.</p>
]]>
      </content:encoded>
      <pubDate>Mon, 31 Aug 2026 00:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/how-many-days-of-ai-does-new-zealand-have.png"/>
    </item>
    <item>
      <title>Ukraine’s Drone Advantage Is an Engineering Loop</title>
      <link>https://jonno.nz/posts/ukraines-drone-advantage-is-an-engineering-loop/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/ukraines-drone-advantage-is-an-engineering-loop/</guid>
      <description>
        A technical look at Ukraine’s drone stack, its battlefield engineering loop, and the evidence behind balloon-launched aircraft.
      </description>
      <content:encoded>
        <![CDATA[<p>Ukraine has turned drone warfare into an engineering feedback loop.</p>
<p>A crew flies a system until Russian electronic warfare finds a weakness. Operators report the failure. Engineers change the radio, navigation or software, then send a new batch back to the front.</p>
<p>I spent years building teams that shipped software several times a day. The mechanics feel familiar: release something small, inspect the result and fix what broke.</p>
<p>The consequences in Ukraine sit on another scale. A failed software release gives you an incident channel and a rough morning. A failed drone may expose its operator or leave a unit without observation.</p>
<p>The engineering impresses me. The reason it exists is bloody grim.</p>
<p>In May 2026, KettleTech Labs released footage of a fixed-wing Hornet drone hanging beneath a helium balloon. The balloon climbed to about 8,250 metres, released the aircraft and let it stabilise into a glide.</p>
<p>Reports claimed the Hornet finished with 95% battery charge and travelled 42 kilometres. That sounds amazing, until you compare the reports.</p>
<p><a href="https://defence-blog.com/ukraine-tests-hornet-strike-drone-launched-from-aerostat/">Defence Blog says</a> the balloon carried the Hornet 42 kilometres before release. <a href="https://en.defence-ua.com/weapon_and_tech/ukraines_hornet_uavs_targeting_crimea_corridor_could_fly_farther_longer_with_balloon_deployment-18586.html">Defense Express says</a> the drone landed 42 kilometres from its launch point after gliding.</p>
<p>Those descriptions cover different parts of the flight.</p>
<p><img src="https://jonno.nz/img/posts/ukraines-drone-advantage-is-an-engineering-loop-flight-profile.svg" alt="Two flight profiles drawn on one shared scale. In the Defence Blog account the balloon carries the Hornet 42 km downrange before releasing it, and the glide that follows adds an unstated distance. In the Defense Express account the balloon climbs near the launch point and the drone glides the full 42 km to landing, needing a glide ratio just over 5:1"></p>
<p>A 42 kilometre glide from 8.25 kilometres up would require a glide ratio a little above 5:1. A fixed-wing aircraft can manage that. Wind, release position and the missing telemetry still matter.</p>
<p>The same reports put the Hornet’s normal range between 150 and 200 kilometres, then estimate a balloon-assisted range between 190 and 300 kilometres. The manufacturer has not published those numbers.</p>
<p>The footage supports one successful release test. It does not prove that Ukrainian crews have launched Hornets from free balloons over Russian targets.</p>
<h2>Three balloon systems</h2>
<p>News reports keep using “balloon”, “aerostat” and “drone carrier” for three different systems.</p>
<p>Ukraine has used tethered aerostats as radio relays and sensor platforms for years. A cable holds the balloon above friendly territory while supplying power to the equipment underneath it.</p>
<p>Aerobavovna CEO Iurii Vysoven <a href="https://spectrum.ieee.org/airships-drones-ukraine">told IEEE Spectrum</a> that his company had deployed dozens of these systems by April 2025. He said a radio repeater raised 500 metres could cover a radius of around 80 kilometres. That figure comes from the company, though the benefit follows basic radio geometry: lifting an antenna extends its line of sight.</p>
<p>These aerostats let crews place the ground station farther from the front. They can also carry cameras and radio-direction equipment.</p>
<p><img src="https://jonno.nz/img/posts/ukraines-drone-advantage-is-an-engineering-loop-aerostat.jpg" alt="Three Ukrainian soldiers in camouflage stand on grassland holding the tether lines of a white helium aerostat as it rises. A sensor and antenna payload hangs on a strut beneath the envelope, and a green ground trailer carrying the winch sits at the left of the frame"></p>
<p><em>Ukrainian Special Operations Forces raising an Aerobavovna ARB12 aerostat, August 2024. Photo by <a href="https://commons.wikimedia.org/wiki/File:Aerobavovna_ARB12_aerostat_in_use_by_Ukrainian_SoF.jpg">Vysoven</a>, <a href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0</a>. Worth noting what you are looking at: the photographer is the manufacturer's own founder, which is the same source the deployment numbers come from.</em></p>
<p>A second design turns the tethered aerostat into an interceptor tower. Photos published in March 2025 showed a thermal camera and a fixed-wing interceptor mounted under an Aerobavovna balloon. <a href="https://militarnyi.com/en/news/balloon-launched-interceptor-drones-ukraine-developing-new-system-to-counter-shahed-drones/">Militarnyi reported</a> that an operator could release the interceptor after the camera detected an incoming Shahed.</p>
<p>Vysoven told IEEE that engineers had only started those trials. Public photos proved that the prototype existed. They did not prove a working air-defence network.</p>
<p>The third system cuts the tether and lets the wind carry the balloon east. It may carry a decoy, radio relay or another aircraft.</p>
<p><a href="https://euromaidanpress.com/2026/05/15/weaponizing-the-westerlies-ukrainian-balloons-sow-havoc-over-russia/">Euromaidan Press reported</a> that Ukrainian forces had sent more than 1,000 free balloons into Russia. A retired Ukrainian colonel supported the account, alongside operators who withheld their names. Russian monitoring channels also reported balloons during a large drone raid in September 2025.</p>
<p>Ukraine has not published an official count. I would treat the 1,000 figure as a credible report rather than a confirmed total.</p>
<p>A fourth idea sits in development. In June 2026, a Ukrainian company presented DART, a small balloon-launched rocket designed to continue on a fixed course after switching off its navigation receiver. The company said that would deny radio jammers a signal to corrupt during the last part of flight.</p>
<p><a href="https://militarnyi.com/en/news/ukraine-ew-resistant-dart-rocket-balloons/">Militarnyi published</a> developer renders and specifications. The developer also said DART still needed Ukrainian military codification. It belongs in the prototype column for now.</p>
<h2>Jamming reaches into the whole stack</h2>
<p>The airframe gets most of the photos. The communications and navigation stack decides whether it reaches useful airspace.</p>
<p>An FPV pilot needs a low-latency command link and a video feed. A fixed-wing strike drone may combine satellite navigation with inertial sensors, terrain matching or visual odometry. An onboard computer can track the selected object once the radio link drops.</p>
<p>Russian electronic warfare attacks the command link, video return and satellite navigation. Russian units also use radio emissions to locate drone crews. Ukrainian jammers can disrupt friendly aircraft if units fail to coordinate their spectrum use.</p>
<p><a href="https://static.rusi.org/tactical-developments-third-year-russo-ukrainian-war-february-2205.pdf">RUSI’s February 2025 field research</a> found that 60–80% of Ukrainian FPV drones failed to reach their targets in the sectors its researchers examined. Operator skill, weather and electronic warfare changed the rate.</p>
<p>The same report estimated that tactical drones caused 60–70% of damaged and destroyed Russian systems. Ukrainian officers still told the researchers that they needed artillery. Drones could find or immobilise a vehicle, while artillery delivered a heavier effect in poor weather and under dense jamming.</p>
<p><img src="https://jonno.nz/img/posts/ukraines-drone-advantage-is-an-engineering-loop-attrition.svg" alt="Two bars from RUSI's February 2025 field research. Between 60% and 80% of Ukrainian FPV drones fail to reach their target, lost to jamming, weather and operator error. Tactical drones are nonetheless credited with 60% to 70% of damaged and destroyed Russian systems"></p>
<p>Fibre-optic FPVs solve the radio-link problem with a physical strand of glass that unwinds behind the aircraft. A jammer cannot corrupt commands or video travelling through that cable.</p>
<p>The cable brings its own problems. A <a href="https://www.foi.se/rest-api/report/FOI%20Memo%208897">Swedish Defence Research Agency study</a> found that the spool adds weight, reduces endurance and can snag on terrain. The fibre carries data rather than electrical power, so the drone still depends on its battery.</p>
<p><img src="https://jonno.nz/img/posts/ukraines-drone-advantage-is-an-engineering-loop-fibre-fpv.webp" alt="A Ukrainian soldier holds a fibre-optic FPV drone in one hand. The black cylindrical spool canister slung under the airframe is about as large as the drone's own body, and the fibre pays out through a port in its base"></p>
<p><em>A fibre-optic FPV under test, February 2025. The spool canister in the operator's palm is the endurance cost the FOI study describes: it is nearly the size of the airframe carrying it. Photo by <a href="https://commons.wikimedia.org/wiki/File:UA_fiber-optic_FPV_drone_01.webp">ArmyInform</a>, <a href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>.</em></p>
<p>Engineers have also pushed more work onto the aircraft.</p>
<p><a href="https://www.reuters.com/world/europe/ukraine-rolls-out-dozens-ai-systems-help-its-drones-hit-targets-2024-10-31/">Reuters reported in October 2024</a> that Ukraine had deployed dozens of domestic automation systems. NORDA Dynamics said it had sold more than 15,000 copies of software that lets a pilot select an object through the camera, then hands the final approach to computer vision.</p>
<p>Call that terminal guidance. A human chooses the target before the software tracks it.</p>
<p>Ukraine’s Defence Ministry described a more advanced interceptor in June 2026. <a href="https://mod.gov.ua/en/news/next-generation-interceptors-ukrainian-drones-already-autonomously-take-down-shahed-type-ua-vs">The ministry said</a> the system automates 95% of an interception cycle. An operator watches the air picture, selects a target and authorises engagement. The interceptor handles guidance, identification and tracking after that command.</p>
<p>“AI drone” covers a huge range of capability, from keeping a truck centred in the camera to coordinating several aircraft. Treating all of it as autonomous target selection makes the technology sound more mature than the evidence supports.</p>
<h2>The battlefield plugs into the factory</h2>
<p>Ukraine connects these aircraft to a wider software system.</p>
<p><a href="https://www.act.nato.int/article/delta-system-cwix/">NATO describes DELTA</a> as a cloud-based integration platform and national data lake. It combines reports from drones, radars, satellites and ground units into a shared battlefield view.</p>
<p>Ukraine added Vezha for drone-video streaming and Mission Control for flight planning. Mission Control also helps units avoid sending several FPVs through the same patch of spectrum.</p>
<p>The result looks less like a fleet of remote-control aircraft and more like a distributed application. Sensors create tracks. Commanders assign work. Drone crews execute missions, then report what happened.</p>
<p>Ukraine has wired those results into procurement.</p>
<p>A <a href="https://www.csis.org/analysis/unleashing-us-military-drone-dominance-what-united-states-can-learn-ukraine">2025 CSIS study based on more than 50 interviews</a> describes a commercial-first system. Private companies fund working prototypes. Military units test them, buy useful models and send feedback to engineers without waiting for a multiyear programme.</p>
<p>By January 2026, Brave1 Market gave participating manufacturers a dashboard showing confirmed hits, target types, operating distance and product rankings. <a href="https://armyinform.com.ua/en/2026/01/07/real-time-feedback-manufacturers-can-now-see-the-effectiveness-of-their-products-online-in-brave1-market/">ArmyInform documented the dashboard</a> and the data available to suppliers.</p>
<p>In June, Ukraine’s Defence Ministry said military units had <a href="https://mod.gov.ua/en/news/over-500-000-drones-ordered-by-the-military-through-brave1-market-using-combat-points">ordered more than 500,000 drones through Brave1 Market</a>. Ordered does not mean produced or delivered, though it shows the scale of the purchasing system.</p>
<p>I started my engineering career in support at Vend. The person sitting closest to the failure often had the best product signal. You lose that signal when it passes through five teams and a quarterly planning process.</p>
<p>Ukraine has shortened that route from operator to engineer.</p>
<p><img src="https://jonno.nz/img/posts/ukraines-drone-advantage-is-an-engineering-loop-loop.svg" alt="The engineering loop drawn as a closed cycle. Drone crews and sensors feed a shared battlefield picture, which produces a mission result. The result reaches suppliers as a combat-data dashboard, the manufacturer turns it into a hardware or software update, units order what works through the marketplace, and the new batch reaches the crews. Russian jamming and countermeasures sit inside the loop, exposing the failures the mission result records"></p>
<p><em>Brave1 appears twice because it does two jobs: it carries combat results back to suppliers as data, and it is where units order what works. The same platform closes the loop in both directions.</em></p>
<p>Speed creates mess as well.</p>
<p><a href="https://www.csis.org/analysis/how-and-why-ukraines-military-going-digital">Another CSIS study</a> found more than 550 unmanned systems on one government-supported marketplace. Units gained choice, while maintainers inherited incompatible parts, firmware and training requirements.</p>
<p>Central procurement can take months and distort feedback before engineers receive it. Unit-level purchasing moves faster, though it weakens standardisation and long-term planning. Ukraine now uses digital marketplaces and combat data to keep the speed while reducing some of that fragmentation.</p>
<p>The metrics need scrutiny too. A confirmed strike makes a clean dashboard event. A drone lost to jamming may produce little data. Units can favour targets that generate more purchasing points. Manufacturers will optimise around whatever the system measures.</p>
<p>The balloon test fits this engineering culture. It combines cheap lift, an existing fixed-wing drone, automatic release and onboard stabilisation. Engineers can test each part without waiting for a new aircraft programme.</p>
<p>Wind still controls the free balloon’s route. Weather can cancel a launch window. The drone needs navigation and, in many missions, a communications path after release. A balloon can save battery and add altitude, though it cannot remove those constraints.</p>
<p>The Hornet footage leaves one basic detail unresolved: did the balloon drift 42 kilometres before release, or did the drone glide that distance afterwards?</p>
<p>The outlets disagree. KettleTech Labs has the telemetry. The public has a video, several contradictory captions and no confirmed combat release. For now, that is where the evidence stops.</p>
<p><img src="https://jonno.nz/img/posts/ukraines-drone-advantage-is-an-engineering-loop-evidence.svg" alt="The claims in this post ranked by how well the public evidence supports them. Strongest: the 60-80% FPV failure rate, from independent field research. Then tethered aerostats flying as relays, sourced to the company that sells them. Then 1,000+ free balloons sent into Russia, reported but with no official count. Then a single filmed Hornet balloon release. Weakest, with no public evidence at all: Hornets flown from balloons against Russian targets"></p>
]]>
      </content:encoded>
      <pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/ukraines-drone-advantage-is-an-engineering-loop.png"/>
    </item>
    <item>
      <title>Prove the Machine Wrong</title>
      <link>https://jonno.nz/posts/prove-the-machine-wrong/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/prove-the-machine-wrong/</guid>
      <description>A parking camera joined two visits together, then handed Keith Miller the job of proving it wrong.</description>
      <content:encoded>
        <![CDATA[<p>Keith Miller got two parking fines from a camera in Miramar. He got them
cancelled by proving his own innocence.</p>
<p>Miller shopped at New World Miramar in December, then visited again in January.
Smart Compliance Management runs the number plate cameras in the car park. Its
system missed one departure and joined the entry from one trip to the exit from
another. It billed him for the hours between them.</p>
<p><a href="https://www.stuff.co.nz/nz-news/360975651/man-wrongly-fined-twice-parking-companys-ai-powered-cameras-takes-fight-commerce-commission">He told Stuff</a>
the system had &quot;critical flaws&quot; and caught too many innocent people. His appeal
worked because he could show his car parked outside his house between the two
visits.</p>
<p>I spent six years working on billing platforms at Vend. Once software writes a
number onto an invoice, support treats it as fact. The angry customer reaches
them, while the logs and assumptions sit somewhere else.</p>
<p>Smart Compliance held the camera data and gave Miller the homework.</p>
<h2>One missing departure</h2>
<p><img src="https://jonno.nz/img/posts/prove-the-machine-wrong-anpr-camera.jpg" alt="An automatic number plate recognition camera unit mounted on a parking services vehicle, lens pointed at the road"></p>
<p><em>ANPR camera on a parking services truck. Photo by
<a href="https://commons.wikimedia.org/wiki/File:ANPR_Camera_Front.jpg">Mbrickn</a>,
<a href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>, via Wikimedia
Commons.</em></p>
<p>Smart Compliance's setup does two jobs. A camera model reads the plate, then an
ordinary billing rule subtracts the entry time from the exit time.</p>
<p>Plate recognition has an error rate because another car can block the view or
dirt can hide a character. Drivers in Belmont, County Durham hit the same
failure in 2022, at a car park run by the same company. The BBC reported
hundreds of £60 charges because
<a href="https://www.bbc.co.uk/news/uk-england-tyne-61736566">the camera &quot;thinks the car was there and it definitely wasn't&quot;</a>.</p>
<p>The billing rule still returns a number when the event log is missing a
departure. The team building that rule has to decide what a gap means. This one
treated the log as complete and sent a breach notice.</p>
<p><img src="https://jonno.nz/img/posts/prove-the-machine-wrong-parking-log.svg" alt="Two visits shown as four number plate events. The first departure is missing, so the system joins the first arrival to the second departure and invents one long stay."></p>
<p>Drivers can appeal, although the process costs time they may not have. Older
drivers in Belmont told the BBC they paid because arguing cost more than the
fine.</p>
<h2>Section 137 presumes the machine worked</h2>
<p>Section 137 of our
<a href="https://www.legislation.govt.nz/act/public/2006/0069/latest/DLM393979.html">Evidence Act 2006</a>
covers evidence produced by a machine, device or technical process. If that kind
of system ordinarily does what a party claims, a court presumes it did so on
this occasion unless someone supplies evidence to the contrary.</p>
<p>Miller's parking appeal never reached a court, so section 137 did not decide his
fine. Smart Compliance followed the same default: accept the output, then ask
the accused person to find the contrary evidence.</p>
<p>That presumption suits a speedometer or a breathalyser with a known calibration
process. Software can produce evidence by applying a rule to incomplete data. A
model can also return a score that no physical instrument measured.</p>
<p>England and Wales use a similar common-law presumption. The Post Office relied
on data from its Horizon accounting system to prosecute postmasters for losses
that did not exist. Bugs in Horizon showed phantom shortfalls, and people who
could not inspect the system pleaded guilty, paid the money or went to prison.</p>
<p>The
<a href="https://www.postofficehorizoninquiry.org.uk/sites/default/files/2025-07/Post%20Office%20Horizon%20IT%20Inquiry%20Final%20Report%20Volume%201%20-%20Accessible%20Version.pdf">Horizon inquiry found</a>
that Post Office staff knew the system could make errors while the organisation
maintained the fiction that its data was accurate. Courts convicted many
hundreds using Horizon evidence, and the Post Office held thousands liable for
losses that never happened.</p>
<p>The inquiry also heard about 13 people whose families attributed their deaths by
suicide to Horizon. Sir Wyn Williams said he could not make a definitive causal
finding, but he would not rule it out.</p>
<p>Australia's Robodebt scheme used arithmetic too. The government divided annual
income by 26 fortnights and treated the result as a debt. Its Royal Commission
called the scheme
<a href="https://robodebt.royalcommission.gov.au/publications/report">&quot;a crude and cruel mechanism&quot;</a>.</p>
<p>Auditors could inspect the rules behind both systems. The people accused by them
still lacked the access and money to do it.</p>
<h2>Seventy-three candidates</h2>
<p>Facial recognition can run as designed and return the wrong person.</p>
<p>Detroit police ran grainy shop security footage through facial recognition,
selected Robert Williams, then arrested him outside his house in front of his
family. The city
<a href="https://www.aclu.org/cases/williams-v-city-of-detroit-face-recognition-false-arrest">settled his case</a>
and changed its policy. Police now need independent evidence before they can
turn a face match into an arrest.</p>
<p>Porcha Woodruff was eight months pregnant when Detroit police arrested her over
a carjacking. She spent ten hours in custody and had contractions after her
release. Prosecutors dropped the charges.</p>
<p>The
<a href="https://law.justia.com/cases/federal/district-courts/michigan/miedce/5%3A2023cv11886/371448/78/">court order in her civil case</a>
records the chain. A facial search returned 73 candidates. An analyst chose
Woodruff as the lead, and two colleagues approved the choice. The carjacking
victim then picked her from a photo lineup. Police got a warrant.</p>
<p>The judge called the arrest troubling, then dismissed Woodruff's claims at
summary judgment because her lawyer had not shown that the officer lacked
probable cause under current law. Woodruff appealed, and
<a href="https://dockets.justia.com/docket/circuit-courts/ca6/25-1788">that case remains pending</a>.</p>
<p>Several people turned one of 73 candidates into probable cause. A malfunction
test would miss the blurry source image and everything police did with the
candidate list.</p>
<h2>Someone picked 92.5%</h2>
<p><img src="https://jonno.nz/img/posts/prove-the-machine-wrong-checkout-camera.jpg" alt="A screen mounted above a supermarket self-checkout lane, showing live footage of the shopper standing in front of it"></p>
<p><em>Camera and live screen at a supermarket self-checkout. Photo by
<a href="https://commons.wikimedia.org/wiki/File:Camera_and_video_screen_at_Sainsbury%27s_self_checkout_2022.jpg">Folkestonesurvey</a>,
<a href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0</a>, via Wikimedia
Commons. Cropped.</em></p>
<p>Foodstuffs North Island scanned 225,972,004 faces across 25 supermarkets during
its six-month trial. The system produced 1,742 alerts, and staff confirmed 1,208
matches.</p>
<p>The system falsely matched Te Ani Solomon, a Māori woman shopping at New World
Westend in Rotorua on her 47th birthday. She
<a href="https://www.1news.co.nz/2024/04/22/rotorua-mother-wrongly-identified-by-supermarket-as-a-thief/">offered staff three forms of ID</a>.
They still made her leave. Foodstuffs apologised and called it human error.</p>
<p>Foodstuffs blamed human error. It chose the model and threshold, while store
staff built the watchlist and acted on alerts.</p>
<p>The trial started with a 90% match threshold. After two harmful
misidentifications, Foodstuffs raised the minimum for staff action to 92.5% and
tightened its process. The Privacy Commissioner found no similar harmful
incident after those changes.</p>
<p><img src="https://jonno.nz/img/posts/prove-the-machine-wrong-threshold.svg" alt="Foodstuffs trial flow: 225,972,004 face scans led to 1,742 alerts and 1,208 confirmed matches. After two harmful misidentifications, the action threshold rose from 90 to 92.5 per cent."></p>
<p>The
<a href="https://www.privacy.org.nz/assets/DOCUMENTS/20250603-FRT-Inquiry-Report-A1082856.pdf">Commissioner's inquiry</a>
found that the trial complied with the Privacy Act. It estimated a 16% reduction
in serious harmful behaviour, and warned readers not to apply that figure beyond
the trial. Two trained staff had to verify an alert before anyone acted.</p>
<p>Retail workers get assaulted, and the trial suggests the system helped. It also
put innocent customers in front of staff who thought a computer had already
identified them.</p>
<p>Foodstuffs still decides how many false matches it will tolerate, and a customer
pays for each one.</p>
<p>The UK Ministry of Justice opened a call for evidence in 2025 after Horizon. It
<a href="https://www.gov.uk/government/calls-for-evidence/use-of-evidence-generated-by-software-in-criminal-proceedings/use-of-evidence-generated-by-software-in-criminal-proceedings-call-for-evidence">proposed separating material a device captures from evidence software generates</a>.
A photograph would sit on one side. An accounting balance or automated fraud
score would sit on the other. The call has closed, and the government has not
published a response.</p>
<p>Our
<a href="https://www.lawcom.govt.nz/about-us/news-and-media/law-commission-to-review-automated-decision-making-by-government">Law Commission has started preparatory work on automated government decisions</a>.
It says New Zealand has no single legal framework for agencies using these
systems.</p>
<p>Section 137 belongs in that work. Anyone who wants to use a generated score
against a person should show where it came from and what they did to check it.</p>
<p>Keith Miller had a photo of his car at home, so he got his $85 back. I'd rather
Smart Compliance proved the stay before it sent the letter.</p>
]]>
      </content:encoded>
      <pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/prove-the-machine-wrong.png"/>
    </item>
    <item>
      <title>I Parsed Every Law New Zealand Has Ever Passed</title>
      <link>https://jonno.nz/posts/i-parsed-the-nz-statute-book/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/i-parsed-the-nz-statute-book/</guid>
      <description>
        38,064 XML files, 241,456 amendment events, the oldest from 1872. What the entire NZ statute book looks like as data, and why counting 'a change' turned out to be the hard part.
      </description>
      <content:encoded>
        <![CDATA[<p>The research notes said New Zealand's legislation XML records its amendment history in machine-readable attributes. I built my first parser against those attributes. They do not exist.</p>
<p>Not &quot;exist but renamed&quot;. Not &quot;exist in some files&quot;. Zero occurrences, in any of 38,064 files, in either version of the official schema. The real history lives in little <code>&lt;history-note&gt;</code> blocks of nearly plain English: dates written out like &quot;2 July 2001&quot;, the amending act named by title only, no identifier of any kind, like a Parliament that never expected anyone to check. Day one of the build, and the map I'd been handed described a country that wasn't there.</p>
<p>Some scope before we go on. New Zealand publishes its whole statute book as open data, free of copyright: every public act and every regulation made under one, 15,306 works in all. Strictly, that's every law the official archive still publishes rather than every law ever passed, so the title is doing what titles do, and now you know. Parsed out, those works yield <strong>241,456 amendment events</strong>: every recorded edit to every act and regulation, what it did, which act did it, and when. The oldest is dated 5 October 1872. The newest landed in June 2026. Nobody had turned any of this into a churn metric before, which is how a weekend curiosity became the project.</p>
<h2>What counts as &quot;a change&quot;</h2>
<p>Findings later. Caveats first, because on this dataset the caveats are half the findings.</p>
<p>One event in my graph is one provision-level operation: a single section, subsection, heading or defined term inserted, repealed, substituted or amended. That sounds like a clean unit. Three things ruin it.</p>
<p><strong>Big numbers are usually one act, not many decisions.</strong> The busiest day in the statute book's history is 28 October 2021, with 4,619 recorded changes, and 4,283 of them came from a single statute, the Secondary Legislation Act 2021, a mass administrative reclassification. The Income Tax Act's worst year (1,544 changes in 2009) was about 79% one omnibus tax bill. Whenever you see &quot;amended X times&quot;, read it as &quot;X operations, mostly from a handful of acts&quot;, never &quot;X separate choices&quot;.</p>
<p><strong>Changes arrive on a timetable.</strong> Since 2008, a quarter of every change to New Zealand law took effect on just four days of the year: 1 January, 1 April, 1 July or 1 October. 1 July alone carries 15,864. Commencement dates are a drafting convention, not evidence of haste, but it means any &quot;three edits every two days&quot; cadence you compute is an average over a process that actually moves in quarterly bursts.</p>
<p><strong>Even &quot;amended N times&quot; depends on a convention you have to pick.</strong> Is a repealed subsection an amendment? By the convention my tracker publishes (insertions, substitutions and textual amendments), the Income Tax Act 2007 has 11,577 recorded amendments, the most in the country. Count every operation including the provisions repealed or revoked out of it and you get 14,294. Both numbers are defensible. Neither is wrong. You just have to pick one, say so, and never mix them, and most public claims about &quot;how often law changes&quot; do not tell you which one they picked.</p>
<p>That last one bit me in miniature. The Water Services Entities Act was amended 832 times in August 2023 by its own authors. Or 820 times, if you exclude the twelve provisions that were repealed rather than rewritten. The tracker now says &quot;recorded changes&quot; instead of &quot;amendments&quot; specifically to stop the two conventions colliding in one sentence.</p>
<h2>Mirror first, ask questions later</h2>
<p>The bulk XML archive lives on the <em>old</em> legislation website, the one that got replaced in March and now carries a polite notice that it will be switched off &quot;when the new website is performing well&quot;. The new site has no bulk archive. Reading that sentence is what turned this from a weekend idea into a now-or-never job: I mirrored the lot, 38,064 files, 16 gigabytes, at a deliberately polite request rate spread over a couple of days. The site's firewall has opinions about impolite scripts, and I had no intention of being the reason the archive went away early.</p>
<p>My first run spent an hour dutifully downloading regulations about walnut export licensing, because I'd built the work queue last-in-first-out without thinking about it, which is the most me bug imaginable.</p>
<h2>Parsing English that thinks it's data</h2>
<p>A history note reads roughly like this: <em>Section 5(1): amended, on 2 July 2001, by section 149 of the Something Something Act 2001.</em> A date spelled out in words. An operation verb. A citation by title, no ID, to an act that might be anywhere in 154 years of legislation. The parser's job is to turn tens of thousands of those into graph edges: this act amended that one, on this date, in this way.</p>
<p>Two things about that went better than anyone deserves. Every one of the 15,306 works parsed without a single XML error, which says something lovely about the Parliamentary Counsel Office's data discipline, whatever the rest of this series implies about everyone upstream of them. And 93.7% of the history notes resolved cleanly into the graph. The stragglers are mostly old acts cited by title in eras of creative spelling, and they sit in a review queue rather than in the numbers.</p>
<p>Before publishing anything, the graph had to reproduce facts already on the public record: the Resource Management Act's long saga, the bright-line test's ping-pong, the Three Waters repeal. It did. Then I let it talk.</p>
<h2>Fourteen years, as a survival curve</h2>
<p>The dataset holds 49,639 provision-repeal events: individual sections struck out of living acts. Treat each one as a death, take the age of the parent act at that moment, and the median is <strong>fourteen years</strong>.</p>
<p>Be careful with that number, because the loose version reads better and I've used it myself: &quot;a law is typically fourteen years old when parts of it get struck out.&quot; The precise version is event-weighted: heavily hacked-about acts contribute thousands of deaths each, so the median tells you the age of the typical act at the typical amputation, not how long a law survives before its <em>first</em> repeal. The per-act survival curve (time to first strike-out, censoring the untouched) is the proper follow-up and it's on the list.</p>
<p>Even the honest version earns its keep, though. New Zealand makes thirty-year infrastructure commitments, governs them on a three-year electoral clock, and writes them into laws whose typical strike-out happens at age fourteen. Those three numbers do not fit together, and the misfit is what the rest of the series is about.</p>
<h2>The politics is the demo, not the thesis</h2>
<p>Amendment events to acts per completed parliamentary term since 2008: John Key's three terms ran 13,426, then 15,144, then 14,584. Jacinda Ardern's two ran 17,538 and 16,818. The current term sits at 12,710 with months still on the clock. Different parties, different decades, different crises, same band. Whoever you came to convict, the chart declines to cooperate: the churn isn't red or blue, it's the machine.</p>
<p>The counter-fact cuts the same way. New Zealand has a reputation as the west's fastest <em>repealer</em>, but by share of edits that delete law, the current government is the lowest since 2008 at 13.8%, and Key's second and third terms were the highest, both above 18%. The reputation comes from a handful of flagship reversals done at speed and under urgency, not from bulk deletion. About one in five operations across the whole dataset removes law rather than adding it; Parliament runs a permanent demolition crew alongside the builders.</p>
<p>If you want the politics proper, that's <a href="https://jonno.nz/posts/zero-metres-of-track/">Part 1</a> (what the cancelled projects cost) and <a href="https://jonno.nz/posts/fastest-repealers-in-the-west/">Part 2</a> (why the machine is built for whiplash), and a version of the story ran in The Spinoff. This post is the appendix that wouldn't fit: the pipeline those pieces stand on.</p>
<p>The data keeps coughing up side quests too. The oldest act still in force dates to 1865, and the current government has amended a clutch of statutes written in 1908. The Ombudsmen Act 1975 has been touched by 311 different other acts, the most cross-referenced law in the country, because every new public body gets bolted onto its schedule. And there's a whole register of provisions Parliament passed that no government ever switched on, including the internet-disconnection penalty from 2011's &quot;Skynet&quot; copyright law, fully drafted and dormant for fifteen years. That one's its own post.</p>
<h2>The tracker, and the tape</h2>
<p>Everything above is live at <strong><a href="https://whiplash.jonno.nz/">whiplash.jonno.nz</a></strong>: the per-term chart, the most-amended leaderboard, a lane-by-lane view of the statute book, and a map of thirty cancelled projects with a scrubbable timeline. The counting rules are published next to every number, because after the 820-versus-832 business I trust a figure exactly as far as I can see its convention.</p>
<p>The pipeline is deliberately boring: a mirror script, the parser, a SQLite graph, static JSON aggregates the site serves. No framework survived contact with the problem. All of it, mirror to graph to aggregates, is at <a href="https://github.com/jonnonz1/nz-statute-book">github.com/jonnonz1/nz-statute-book</a>, and the source archive it reads from is public and copyright-free, so you can check every number in this post against the same primary source I used.</p>
<p>Go on.</p>
]]>
      </content:encoded>
      <pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/i-parsed-the-nz-statute-book.png"/>
    </item>
    <item>
      <title>Your Show HN dies in 7 hours</title>
      <link>https://jonno.nz/posts/your-show-hn-dies-in-7-hours/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/your-show-hn-dies-in-7-hours/</guid>
      <description>
        I scraped all 41,301 Show HN posts from the last year. Half the comments a launch will ever get arrive inside 7.2 hours, and success doesn't slow the clock.
      </description>
      <content:encoded>
        <![CDATA[<p>At 1pm UTC on a Friday last June, an airline pilot posted a Show HN:
interactive graphs and globes built from years of his own flight logs. It went
huge. 1,539 points, the third biggest Show HN of the entire year. By dinner he
had 123 comments.</p>
<p>On day three the thread got 3 comments. In week two it got zero. The third
best launch out of more than forty thousand, and the conversation was over
before the weekend ended.</p>
<p>I wanted to know whether that shape is the exception or the rule, so I scraped
every Show HN from the last 12 months using the
<a href="https://hn.algolia.com/api">Algolia HN API</a>: 41,301 posts, plus the full
comment tree of every launch that got at least ten comments. About 100,000
comment timestamps. The scraper, the analysis, and the data are all in
<a href="https://github.com/jonnonz1/hn-attention-cliff">one repo</a> if you want to
check my working.</p>
<p>The pilot's flatline is the rule. The only unusual thing about his launch is
that it had a peak.</p>
<h2>The median launch gets two points</h2>
<p>I went in to measure decay speed. The distribution of outcomes stopped me
before I got there.</p>
<p>The median Show HN in my 12 months of data earned 2 points and 0 comments.
61.7% of launches got no comments at all, and 78.9% got one or none. A
single upvote from a stranger puts you in the top third.</p>
<p><img src="https://jonno.nz/img/posts/hn-attention-outcomes.svg" alt="Histogram of Show HN outcomes: 41,301 launches, median 2 points, 79% got one comment or none"></p>
<p>The distribution is a power law with a brutal knee. The 90th percentile is 8
points. Fewer than 2% of launches clear 100. Picture your launch day and you
picture the front page. Picture two points and silence instead: that's the
median experience, by a wide margin.</p>
<p>I've launched products before (Shopim in 2014, Spotrisk in 2020) and I still
found these numbers confronting. Every founder I know treats the launch as
the milestone. The milestone, statistically, is a shrug.</p>
<h2>The half-life is 7.2 hours</h2>
<p>HN doesn't publish vote timestamps, so I'm using comment timing as the proxy
for attention. That's a real limitation and I'll come back to it, but
comments are the visible half of engagement, and they're what founders
refresh the page for.</p>
<p>For the 2,066 launches with at least ten comments, I built each launch's
cumulative comment curve: what share of its lifetime comments had arrived by
each hour. The median curve crosses 50% at 7.2 hours.</p>
<p><img src="https://jonno.nz/img/posts/hn-attention-hero.svg" alt="Median cumulative comment curve across 2,066 Show HN launches. Half of all comments arrive inside 7.2 hours, 90% by 26 hours"></p>
<p>Half of everything anyone will ever say about your launch is said before the
first workday ends. 90% is done by hour 26. Even the slow quartile of
launches crosses halfway by hour 18.</p>
<p>The mechanism is no mystery. HN's ranking formula divides a story's points by
its age raised to the power 1.8, which <a href="http://www.righto.com/2013/11/how-hacker-news-ranking-really-works.html">Ken Shirriff documented years
ago</a>.
Time sits in the denominator with a bigger exponent than votes get in the
numerator, so gravity wins no matter how good your day is going. The front
page is a conveyor belt into a furnace, by design. It's why HN stays
interesting, and it's why your launch can't stay visible.</p>
<p>I sanity-checked the half-life two ways (median of per-launch crossing times,
and the crossing of the median curve) and they agree within 20 minutes.
Excluding founders answering their own threads, 19% of all comments, moves
the number by four minutes. The 7-hour figure is not an artefact of chatty
founders.</p>
<h2>Success buys volume, not time</h2>
<p>My first guess was that big launches would decay slower. A front-page hit
keeps earning impressions, so surely the conversation stretches out.</p>
<p>It doesn't. The top decile of measurable launches (268 points or more, the
year's genuine hits) has a median half-life of 7.6 hours. Everything below
that decile: 7.1 hours. The biggest launches of the year run on the same
clock as a launch that scraped together ten comments.</p>
<p><img src="https://jonno.nz/img/posts/hn-attention-top12.svg" alt="Small multiples of the 12 biggest Show HN launches of the year, all showing the same decay curve shape"></p>
<p>Look at the top 12 launches of the year, the ones every founder dreams about.
Homebrew 6.0.0: half done in 8 hours. The 3,346-point monster at number one:
half done in 6.3. Two of the twelve flatlined at launch and only took off a
day or more later, which is HN's
<a href="https://bengtan.com/blog/open-secrets-hacker-news/">second-chance pool</a>
doing its thing, where moderators re-launch overlooked stories with a fresh
timestamp. The rescue mechanism exists because without it, nothing gets a
second look.</p>
<p>Success multiplies how many people show up. It does nothing to change when
they leave.</p>
<h2>After hour 48, it's over</h2>
<p>For the median launch, 4.2% of lifetime comments arrive after hour 48. Pool
every comment in the dataset together and the number rises to 17%, because
the giant launches have longer conversations in absolute terms. Either way
you cut it, the second-day cliff is real: 71% of launches are more than 90%
finished by hour 48.</p>
<p><img src="https://jonno.nz/img/posts/hn-attention-tail.svg" alt="4.2% of a launch's lifetime comments arrive after hour 48"></p>
<p>And comments are the durable end of attention. Traffic dies faster.
<a href="https://harrisonbroadbent.com/blog/hacker-news-traffic-spike-anatomy/">Harrison Broadbent's front-page traffic
data</a>
shows over half the visitor spike gone within 8 hours of submission. The
comment thread is the long tail. The clicks are gone by tea time.</p>
<h2>A launch is a moment, distribution is a campaign</h2>
<p>The takeaway I keep landing on: the launch spike is real, and it's worth
having, and it cannot be your distribution plan. It's 48 hours. You can't
build a company on 48 hours of attention.</p>
<p>What worked for the products I've been involved with was never the spike. It
was the boring compounding stuff: showing up in the places your users
already are, week after week, shipping visibly, and giving people a reason to
come back after the thread dies. The launch is the starting gun, and most of
us have been treating it as the race.</p>
<h2>Why I'm building Shipyard</h2>
<p>Full disclosure: this analysis had a motive. Staring at forty thousand
launches that got a median of zero comments is a big part of why I'm
building <a href="https://goshipyard.app/">Shipyard</a>, a feed where you post what
you've built and it keeps collecting honest reviews long after launch day,
instead of vanishing down page 40 of /newest.</p>
<p>If the numbers above made you wince, Shipyard is my answer to them. The data
stands on its own either way, and if you only take one thing from this post,
take the 7 hours, and plan your next launch knowing the clock is already
running.</p>
<h2>Methodology</h2>
<p>Everything is reproducible from <a href="https://github.com/jonnonz1/hn-attention-cliff">the
repo</a> with <code>make reproduce</code>:
every number above traces to a named function in <code>analyze.py</code>, and the
scraped data ships in the repo. The corpus is every story tagged <code>show_hn</code> on
the Algolia HN API, posted 18 June 2025 to 18 June 2026, so every story has
at least 14 days of comment history. &quot;Lifetime comments&quot; means live comments
within 14 days of posting.</p>
<p>Caveats, stated plainly: comments are a proxy, and comments are not votes and
not traffic. The decay curves only describe the 5% of launches with ten or
more comments, since the median launch has too few comments to have a curve
at all. The API excludes flagged and dead posts, which makes the medians
kinder than reality. And HN's second-chance pool re-timestamps a small number
of stories; 99 comments that predate their re-stamped story were dropped and
are counted in the repo's sanity checks.</p>
]]>
      </content:encoded>
      <pubDate>Wed, 01 Jul 2026 12:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/img/posts/hn-attention-hero.png"/>
    </item>
    <item>
      <title>The Fastest Repealers in the West</title>
      <link>https://jonno.nz/posts/fastest-repealers-in-the-west/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/fastest-repealers-in-the-west/</guid>
      <description>
        In 2025 Parliament sat 644 hours and spent a third of them in urgency. The churn isn't a personality flaw. We built the company this way.
      </description>
      <content:encoded>
        <![CDATA[<p>In 1979 a law professor named Geoffrey Palmer wrote that New Zealand had the fastest lawmakers in the West. In 2024, <a href="https://thespinoff.co.nz/politics/25-06-2024/geoffrey-palmer-on-rapid-reforms-you-should-learn-from-history-not-repeat-it">he updated the diagnosis</a>: &quot;We've gone from being the fastest lawmakers in the west to the fastest repealers in the west.&quot;</p>
<p>Palmer would know. He went on to be Prime Minister, and the machine he described in <em>Unbridled Power?</em> is the one we still run: one chamber of Parliament, no upper house, no written constitution, and almost nothing a government with a one-vote majority can't do before lunch. Most democracies have speed bumps. We bought the racing package.</p>
<p>Part 1 of this series counted the bill: well over a billion dollars in directly sunk and cancellation costs from a single change of government, plus billions more spent redoing and replacing. This part is about why it keeps happening, because the answer isn't that we elect unusually flighty people. The answer is the company structure.</p>
<h2>A company with no board</h2>
<p>Stay with the picture from Part 1: New Zealand as a company whose entire exec team faces replacement every three years. Now look at the governance around that team.</p>
<p>Most companies of consequence have a board that long-horizon decisions must clear, a constitution that takes more than a staff vote to change, and shareholders who can force a special resolution. NZ Inc has none of that. Parliament is one chamber, its decisions need 61 votes out of 120, and the few protected rules we do have (six electoral provisions, <a href="https://teara.govt.nz/en/referendums/page-5">entrenched since 1956</a>) don't cover a single spending commitment, infrastructure plan, or health strategy. A hospital programme and a tweak to dog registration fees enjoy exactly the same legal durability: none.</p>
<p>The three-year term turns that freedom into a metronome. Section 17 of the Constitution Act gives a government roughly 36 months, and anyone who's run a delivery team can sketch the rhythm from there. Year one, ship something visible. Year two, panic about the things that aren't visible yet. Year three, campaign. A <a href="https://link.springer.com/article/10.1007/s11127-024-01143-7">2024 study in <em>Public Choice</em></a> tracked 22 democracies over two decades and found exactly this shape: tax reforms cluster right after elections and evaporate as the next one approaches. Every democracy has the cycle. Ours just spins faster, with fewer brakes, and the 100-day plan has hardened it into ritual: each new exec team now publishes a list of the previous team's projects it will kill, and gets graded on killing them quickly.</p>
<h2>A third of the year in urgency</h2>
<p>Speed shows up in the record as something with a technical name: urgency. It's the parliamentary setting that compresses or skips the normal stages of making law, including select committee, which is the one stage where the public, experts, and officials get to find the bugs.</p>
<p>Every engineer reading this knows what shipping without review does to quality, and you don't have to be an engineer to follow the numbers. In 2025 the House sat 644 hours, the most in decades, and spent 32% of those hours under urgency, also a record (the long-run average sitting load is around 508 hours, per analysis by David Farrar). By Newsroom's count this April, more than half of the government's legislation has had at least one stage under urgency, and around 30 bills have gone through with no select committee scrutiny at all. There's a <a href="https://nzpt.cjs.nz/">community-built tracker</a> keeping score in real time: as I write this, it shows 21 separate urgency occasions this term, touching 57% of all bills before the House — 125 of 219.</p>
<p>I want to be fair about what those numbers mean. Urgency is sometimes legitimate, and every government uses it. But a third of the legislative year in skip-review mode isn't an exception regime. It's the workflow. And law made at that speed is exactly the law most likely to need repealing, which feeds the cycle we counted in Part 1.</p>
<h2>The fix that ran out of time</h2>
<p>Here's my favourite piece of evidence that the problem is structural, because you couldn't script it better.</p>
<p>The obvious patch is a longer term: four years instead of three, more runway between campaigns. It's one of the rare ideas with support across the aisle, and this Parliament actually had a bill for it, the <a href="https://en.wikipedia.org/wiki/Term_of_Parliament_(Enabling_4-year_Term)_Legislation_Amendment_Bill">Term of Parliament (Enabling 4-year Term) Legislation Amendment Bill</a>. Voters had said no twice before, emphatically (68.1% against in 1967, 69% in 1990), so the plan was to do it properly: select committee, a binding referendum, the works.</p>
<p>The select committee stripped out the bill's novel design. The referendum got shelved on time grounds. And in February this year the bill formally stalled, unfinished, as the parliamentary clock ran down toward the election.</p>
<p>The fix for the three-year term ran out of time in a three-year term.</p>
<h2>Even the things that survive get taxed</h2>
<p>You might reasonably ask: can't anything be made to stick? Two case studies, and they're the two edges of the same blade.</p>
<p>The Zero Carbon Act passed in 2019 with 119 of 120 votes, about as close to corporate consensus as this country produces. It survived, technically. But in October 2025 the government cut the 2050 methane target so far that the top of the new range is the bottom of the old one, despite the Climate Change Commission advising movement in the opposite direction. Climate policy consultant Dr Christina Hood <a href="https://www.sciencemediacentre.co.nz/">put it plainly</a>: she was not aware of any government, anywhere, that had weakened an existing legislated domestic climate target. Near-unanimity bought the framework six years of life before the dial got turned anyway.</p>
<p>The other case is quieter and costs more. The NZ Super Fund exists to pre-pay the retirement of people who are currently in school: contributions go in now, compound for decades, and pay out when today's eight-year-olds retire. It's the single most generational financial commitment on the country's books. In 2009 a new government suspended contributions, and they stayed suspended for eight years. The fund survived; the suspension still happened. <a href="https://nzsuperfund.nz/about-the-guardians/purpose-and-mandate/contributions-model/">The Guardians' own estimate</a> of what it cost, in missed contributions and the returns they would have earned by the time payments resumed in 2017, is around $24.5 billion.</p>
<p>That's the number I'd put on a billboard, because it makes the stakes concrete in a way potholes never will. One pause, made inside one three-year window under fiscal pressure, quietly compounding into a $24.5 billion hole in a fund for people who couldn't vote on it. The light rail fiasco cost $228 million and got years of headlines. The Super Fund pause cost a hundred times more and most people couldn't date it within five years. The most expensive churn is the kind nobody films.</p>
<h2>The honest counterargument</h2>
<p>Before anyone fits this series for a constitutional reform t-shirt, the other side of the ledger deserves its say, because it's not weak.</p>
<p>In a country with one chamber and no written constitution, the three-year election is close to the only real check we have. Shorten the leash because governments misbehave, runs the argument, and don't be surprised when a <em>bad</em> government uses a four-year leash to entrench worse. Hipkins supports a four-year term; so does Seymour, eventually; the voters, twice asked, have preferred to keep their grip.</p>
<p>Locking things in has its own pathologies. Supermajority requirements hand a veto to whoever can muster 31% of the House, which is how minority blocking works everywhere it exists. And when Parliament did once try to entrench actual policy (an anti-privatisation clause in the Three Waters legislation, 2022, passed under urgency at 60%), constitutional lawyers across the spectrum called it a dangerous precedent and it was unwound within weeks, with the then Prime Minister conceding the mistake. Entrenchment, it turns out, is a weapon both teams can fire.</p>
<p>All of which leaves a genuinely uncomfortable shape: the accountability mechanism and the churn engine are the same machine. You can't simply slow it down without asking who benefits from the slack.</p>
<p>So if term length alone won't fix it, and entrenchment cuts both ways, what's left? My answer is unglamorous: you start by measuring it. No one in New Zealand currently publishes a churn metric — how much of each Parliament's lawmaking is spent unpicking the last Parliament's work, which sectors get whiplashed hardest, how long a law actually lives. You can't manage what you don't measure, and right now nobody's even measuring.</p>
<p>So I measured it. I downloaded the entire statute book, all 38,064 XML files of it — every act and regulation, reprints and all — and parsed every amendment and repeal back through 2008. That's Part 3, and the numbers are better than I hoped and worse than you'd think.</p>
]]>
      </content:encoded>
      <pubDate>Fri, 19 Jun 2026 00:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/fastest-repealers-in-the-west.png"/>
    </item>
    <item>
      <title>Six Years, $228 Million, Zero Metres of Track</title>
      <link>https://jonno.nz/posts/zero-metres-of-track/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/zero-metres-of-track/</guid>
      <description>Auckland Light Rail: six years, $228 million, zero metres of track. A running tally of what changing our minds costs.</description>
      <content:encoded>
        <![CDATA[<p>Auckland Light Rail ran for six years, spent <a href="https://www.beehive.govt.nz/release/government-cancels-auckland-light-rail">$228 million</a>, and laid zero metres of track.</p>
<p>At its peak the project was paying about $920,000 a week to two engineering firms. Not to build anything. To plan, re-plan, and re-plan again, through three different versions of the route. In January 2024 a new government cancelled it as part of a 100-day plan, and the public got nothing back for a quarter of a billion dollars except some very expensive PDFs.</p>
<p>I want to be upfront about what this series is, because the material is political and the timing is an election year. This isn't a political hit job. It's pattern recognition on the public record, and the pattern has both parties' fingerprints all over it.</p>
<h2>The same meeting, every three years</h2>
<p>I've spent a little bit of time working with exec teams, at startups and at big companies, and I've seen my share of strategy rewrites. Even one rewrite, done inside one company, with everyone trying their best, costs you about a year. Roadmaps get binned. Half-finished work gets written off. Your best people quietly update their CVs. The new strategy is usually about 70% the old strategy with new diagrams.</p>
<p>New Zealand runs like a company that's contractually required to put its entire exec team up for replacement every three years. Each incoming team arrives with a mandate to show movement in a hundred days, and the fastest way to show movement is to bin whatever the last team was building. The reorg is the announcement. The announcement is the work.</p>
<p>Any one of these execs might be competent on their own. The dysfunction is the team, and the team is permanent: it spans parties, decades and ideologies, and it cannot leave a predecessor's decision alone. Meanwhile the shareholders of NZ Inc can't sell their shares. A decent chunk of them are still at primary school.</p>
<p>That's the frame. Now the receipts.</p>
<p>The Cook Strait ferries are the cleanest one. In 2021 KiwiRail signed a fixed-price contract for two new rail-capable ferries, with port upgrades to match. By December 2023 the cost had escalated badly and the incoming government cancelled the lot. By then <a href="https://www.rnz.co.nz/news/political/560273">$507.3 million had already been spent</a>, and the bill kept climbing as it wound down: <a href="https://www.1news.co.nz/2025/08/15/how-much-it-cost-to-cancel-irex-ferry-contract-671-million/">the final cost came to $671 million</a>, including $222 million to settle the shipbuilding contract with Hyundai. So: $671 million, no ferries. Then we ordered different ferries anyway, for $1.86 billion, due 2029. The old rail-enabled Aratere got retired in the meantime, which severed the Cook Strait rail link for roughly five years.</p>
<p>We paid $222 million to not get ships, and then bought ships.</p>
<p>Three Waters took seven years from the Havelock North outbreak to a full legislative framework, then got <a href="https://www.dia.govt.nz/Water-services-reform-about-the-reform-programme">repealed under urgency</a> in an afternoon in February 2024. You can think the reform was wrong and still notice the maths: the $120 to $185 billion of ageing pipes that justified it didn't get repealed with it.</p>
<p>The Resource Management Act replacement is my favourite, in the way a slow-motion replay of a faceplant is someone's favourite. Parliament spent years building the RMA's successor, passed it in August 2023, and the next government repealed it 122 days later. The replacement's replacement arrived in late 2025, and The Spinoff noted its principles &quot;all but mirror&quot; the laws that got repealed. Thirty-plus years of everyone agreeing the RMA must go, and the RMA is still here, having outlived its own successor.</p>
<p>Te Pūkenga merged sixteen polytechnics into one national institute in 2020. On 1 January 2026 ten of them stood back up as independent polytechnics, with the institute itself due to be wound up by the end of the year. Between those two restructures: 855 staff gone, $9.5 million in redundancy payouts, and $325 million allocated to stand up the new polytechs that look a lot like the old polytechs. If you've ever survived a corporate restructure followed by a de-restructure, you know exactly what those six years felt like from inside.</p>
<p>The Smokefree generation law passed in December 2022 as a world first, got studied by half the planet, and was repealed before a single clause took effect.</p>
<p>And if you want the purest specimen of the whole genre, it's the <a href="https://www.opespartners.co.nz/tax/bright-line">bright-line test</a>. National invented it in 2015 at two years. Labour stretched it to five, then ten. National snapped it back to two in 2024. Four settings in nine years, on a tax rule that decides what happens when ordinary people sell a house. Both teams, same dial.</p>
<p>Worth noting who started and stopped each of these. Labour built ALR, iReX, Three Waters and Te Pūkenga; National stopped them. National invented the bright-line test; Labour extended it; National reverted it. And Labour announced a $785 million harbour cycle bridge in June 2021, then cancelled it itself by October, which proves you don't even need to change the government to get the U-turn. The cycle doesn't care who's driving.</p>
<h2>Only money actually spent</h2>
<p>Time to add it up, and this is where I need to be careful, because the temptation with a topic like this is to quote the scariest number available.</p>
<p>Politicians on every side do exactly that. When light rail was cancelled, ministers cited builds costing &quot;$15 billion, rising to $29.2 billion&quot;. Those are projections of money we now won't spend. Counting avoided future costs as waste is fantasy accounting, and the moment you do it, anyone with a calculator can dismiss your whole argument.</p>
<p>So the rules for this series are boring on purpose. Count money that actually left the public account on things that were then cancelled or reversed: sunk planning, design, contracts, land, plus the penalties paid to walk away. Never count &quot;would have cost&quot; figures. Attribute anything contested to whoever claims it, like Labour's Tangi Utikere putting the full iReX cancellation at $1.16 billion once you include ongoing maintenance, a figure that bundles in things I won't count.</p>
<p>On those rules: light rail's $228 million (the cancellation figure ministers cited; the Cabinet paper itself documents $178.5m, the other $49.5m unexplained), iReX's $671 million all up, the taxpayer share of <a href="https://en.wikipedia.org/wiki/Let%27s_Get_Wellington_Moving">Let's Get Wellington Moving</a> ($109.7 million of the $180.7 million it spent over nine years, mostly on consultants; Wellington's ratepayers carried the rest), Te Pūkenga's redundancy bill, and the $80 million-plus in public service redundancy payouts across 24 agencies as the workforce swung up by 13,000 and back down again.</p>
<p>That's well over a billion dollars in directly sunk and cancellation costs from a single change of government, counted conservatively. A billion dollars is roughly half a new Dunedin Hospital, a project which has itself been rescoped so many times that 35,000 people marched down George Street about it.</p>
<p>I lived on the North Shore through the announcement years, and the ritual became familiar: the render, the press conference at the empty site, the revised render, the quiet line in a later Budget. SkyPath, then the standalone cycle bridge, then neither. Light rail to the airport in three different flavours. Somewhere around 2021 I stopped believing artists' impressions the way I'd stopped believing crypto whitepapers.</p>
<p>Here's the backdrop that turns this from annoying into expensive. Te Waihanga's 2022 stocktake found that from 2010 to 2019 New Zealand was the biggest infrastructure investor in the OECD as a share of GDP, while sitting near the bottom 10% of high-income countries for how much infrastructure each dollar buys. We're not under-spending. We're spending like a rich country and delivering like a country that re-litigates its decisions every electoral cycle, into an infrastructure deficit Te Waihanga and Sense Partners size at somewhere between $100 and $210 billion over 30 years.</p>
<p>I grew up in Christchurch, and the rebuild taught me what both versions of this look like on the ground. The bits that got locked in and left alone got built; the city got a genuinely world-class convention centre and a repaired arts centre. The bits that kept getting re-litigated dragged on so long they became civic jokes. The stadium took fifteen years of arguments and only opened this year. Same city, same decade, same money. The difference was whether each new set of decision-makers honoured the last set's call.</p>
<h2>When stopping is the right call</h2>
<p>A fair pushback: sometimes cancelling a project is the competent move, and an exec team that can't kill anything is its own kind of disaster.</p>
<p>True, and the record backs it in places. Treasury papers showed about 80% of the iReX cost escalation was seismic strengthening at the ports, work that's needed no matter whose ferries dock there. Pulling the pin on a runaway project can be governance working, and I'd rather have a government willing to stop a bad bet than one that rides it down out of pride.</p>
<p>The target of this series is narrower: the re-litigation of settled, long-horizon decisions. Ferries wear out on a schedule that doesn't care about coalition agreements. Pipes corrode on chemistry's timetable, not the electoral one. When the underlying problem has a 30-year horizon and the decision-making has a 3-year one, reversal isn't prudence. It's the exec team marking its territory, and the bill lands on shareholders who can't vote yet.</p>
<p>So that's Part 1: the cost, counted conservatively, with the fantasy numbers left out.</p>
<p>Part 2 asks why New Zealand specifically does this more than nearly anyone, because it turns out we're rigged for it in ways most democracies aren't, and the man who wrote the book on it in 1979 has updated his diagnosis.</p>
<p>Part 3 is where I stop reading the receipts and start pulling them. The entire statute book is published as open data: every act, every amendment, every repeal. I've started parsing all of it, back to 2008, to measure the churn properly. It's a personal project and a few evenings of code, not a royal commission. But nobody else has built it, most of NZ Inc's shareholders haven't been born yet, and somebody should be keeping the receipts.</p>
]]>
      </content:encoded>
      <pubDate>Fri, 12 Jun 2026 00:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/zero-metres-of-track.png"/>
    </item>
    <item>
      <title>What Happens When a Worm Drives Claude?</title>
      <link>https://jonno.nz/posts/what-happens-when-a-worm-drives-claude/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/what-happens-when-a-worm-drives-claude/</guid>
      <description>
        I wired a live C. elegans connectome into the control loop of a Claude coding agent, then watched what the worm did when the code broke.
      </description>
      <content:encoded>
        <![CDATA[<p>A few weeks ago I was on the couch with my eight-year-old, and she was going on
about worms. Kids ask the kind of questions we forget to ask as adults, the ones
that drop off once we get all mature and sensible. That conversation is the
reason a worm ended up driving Claude.</p>
<p>Right now everyone builds LLM agents the same way. Take a model, bolt more tools
onto it, hand it a bigger toolbox. I wanted to try the opposite. Leave the model
alone, and put a brain inside the loop to do the steering.</p>
<p>I'll say this up front: it's a fun project, not a serious one. But the thing it
does is genuinely strange, so it's worth a watch.</p>
<div style="position:relative;aspect-ratio:16/9;margin:2rem 0;">
  <iframe src="https://www.youtube-nocookie.com/embed/9ydt-dVCY8Q"
    title="What Happens When a Worm Drives Claude?" loading="lazy"
    allow="accelerometer; clipboard-write; encrypted-media; picture-in-picture; web-share"
    allowfullscreen style="position:absolute;inset:0;width:100%;height:100%;border:0;"></iframe>
</div>
<p>(Can't see the embed? <a href="https://youtu.be/9ydt-dVCY8Q">Watch it on YouTube</a>.) All
the code is <a href="https://github.com/jonnonz1/c302">on GitHub</a> if you'd rather pull
it apart yourself.</p>
<h2>The setup is a car</h2>
<p>Think of it as a car. Claude (Sonnet 4) is the engine. A big engine with nobody
at the wheel. It has five tools: read, grep, write, bash, and run the tests.
That's the lot.</p>
<p>The driver is a small controller program. Every tick it looks at what Claude
just did and decides what Claude does next. It never writes a line of code
itself. It just shapes the behaviour, the way you'd drive a car without ever
touching the pistons.</p>
<p>And the driver doesn't read the code either. It reads the dashboard. Five
numbers, every tick, and only these five: how many tests are failing, the share
passing, how many files Claude has touched, how many ticks have gone by, and the
last tool it used. That's the whole view of the world.</p>
<p>Out the other side there are seven knobs. The big one is the gear: diagnose,
edit, test, or stop. Then a handful more, like how much risk to take, how long
to think, how widely to read, how hard to commit, and how done it reckons it is.
Five numbers in, seven knobs out.</p>
<h2>So who turns the knobs?</h2>
<p>The worm.</p>
<p>It's C. elegans, a roundworm about a millimetre long. It has 302 neurons, and
scientists mapped every single connection between them back in 1986 (the year I
was born, which I'm choosing not to read anything into). It's one of the only
nervous systems we understand all the way down.</p>
<p>I took 14 of those neurons and ran them live in a simulation. Four of them do
the heavy lifting. One is a salt sensor, and to a worm salt means food, so I
wired it to reward. Its opposite I wired to bad results. One drives the worm
forward, so I wired it to how much work is left. One throws it into reverse, so
I wired it to errors.</p>
<p>I didn't make any of that wiring up. It's the published connectome. All I did
was connect the worm's senses to the agent's situation and let it run.</p>
<p>(If you want the longer argument for why a worm of all things, I made the case
when I
<a href="https://jonno.nz/posts/what-if-a-worm-could-make-ai-agents-smarter/">first started this</a>.)</p>
<h2>One run, 14 ticks, 45 seconds</h2>
<p>Does it crash, or does it do something useful?</p>
<p>It starts at baseline with one failing test. Claude opens a few files, runs a
search. The tests are failing, so the worm leans on the accelerator. I never
wrote a rule that says &quot;press the accelerator when there's work to do&quot;. It falls
straight out of the wiring.</p>
<p>Then Claude runs the tests and gets a real failure. The error neuron lights up,
the worm changes gear, and Claude makes its first proper edit. Risk drops, it
reads less, it commits harder. Again, no rule for that combination. The error
signal and the work-left signal crossed a threshold together, and the worm
turned that into &quot;stop wandering, make a change&quot;. It found a scent.</p>
<p>A test goes green. Then another. The worm is cooking. If the run ended there I'd
have gone home happy.</p>
<p>It didn't. The second edit was sloppy and broke something else. The pass rate
drops, the errors jump, and the punish neuron (my favourite, for the record)
lights up hot.</p>
<p>This is the moment I cared about. The obvious move is to panic, drop back to
square one, and start over. The worm doesn't. It stays in gear, eases off, gets
careful, reads before it edits. Two ticks later the careful edit lands and the
pass rate climbs back higher than it was before the break.</p>
<p>It recovered without restarting, which is exactly what a good pair would do.
Look at the diff, don't burn the house down and start fresh. And nothing in the
wiring connects a regression to a change of gear. The punish signal pulls one
knob down. It doesn't yank the worm out of edit mode, because that's a different
knob entirely.</p>
<p>By tick 14 the last test goes green, ten out of ten, and the worm stops and
parks the car. Not because I told it to stop at ten, but because there was
nothing left pulling it forward.</p>
<h2>Same brain, one of them alive</h2>
<p>So I ran it properly. About 200 times. The live worm averaged a 0.96 pass rate.</p>
<p>Then I ran the exact same connectome, same wiring, same neurons, but
pre-recorded, like playing a tape of the worm's brain instead of letting it
react. That version averaged 0.87, and the live worm beat it every single time.
Same brain. The only difference is one of them was alive and getting feedback,
and the other was just replaying.</p>
<p>Now the part I have to be honest about. I ran a colder version where Claude
hadn't indexed the repo first, and plain Claude with no worm and no driver beat
every controller I built, the live worm included. The worm is great at following
a gradient once the ground is mapped. It cannot plan its way around a codebase
it has never seen. So this isn't &quot;worm beats Claude&quot;. It's &quot;feedback beats no
feedback&quot;, which is a smaller and far more useful claim. (The full methodology
and numbers are
<a href="https://github.com/jonnonz1/c302/blob/main/research/PAPER.md">in the paper</a>.)</p>
<h2>What I took from it</h2>
<p>Give your agent a way to know when it's done. The baseline that couldn't tell
kept running long after the job was finished, burning money for nothing.</p>
<p>Don't restart on a setback. The worm that held its nerve through the regression
did most of the real work.</p>
<p>And feedback beats wiring. Two identical brains, and the live one won purely
because it was in the loop. The data you feed an agent and the loop you close
around it matter more than how clever the thing in the middle is.</p>
<h2>The bit that isn't about a worm</h2>
<p>I built this in about three weeks, a couple of evenings here and there, on
roughly $50 of API credits. I used Claude Code as my research assistant, which
means my brain was steering Claude to build a thing that lets a worm's brain
steer Claude.</p>
<p>A few years ago a question this daft would have been a research project. Now
it's a weekend and a coffee budget. The cost of chasing a strange idea has
fallen through the floor.</p>
<p>So chase the strange ideas. The code's
<a href="https://github.com/jonnonz1/c302">on GitHub</a>. Go on.</p>
]]>
      </content:encoded>
      <pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/what-happens-when-a-worm-drives-claude.png"/>
    </item>
    <item>
      <title>The holes that kill you are the ones you never tested</title>
      <link>https://jonno.nz/posts/the-holes-that-kill-you-are-the-ones-you-never-tested/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/the-holes-that-kill-you-are-the-ones-you-never-tested/</guid>
      <description>The Swiss cheese model trains you to count slices. The holes that actually kill you are the ones you never tested.</description>
      <content:encoded>
        <![CDATA[<p>Every time something goes down, the first instinct in the room is to add a
layer. Another replica. A second region. One more health check in front of the
thing that broke. It feels like progress, and the Swiss cheese model hands that
instinct a lovely picture to point at.</p>
<p>You know the diagram even if you've never heard the name. Stacked slices of
cheese, each slice a layer of defence, every slice riddled with holes. An
accident only makes it through when the holes in all the slices happen to line
up. <a href="https://en.wikipedia.org/wiki/Swiss_cheese_model">James Reason</a> built it to
explain how hospitals and aircraft fail, and it's still the best tool we have
for explaining why a system with five safety nets can still face-plant.</p>
<p>The picture has a side effect though. It teaches you to count slices. And
counting slices is mostly the wrong job.</p>
<p>Take redundancy, since that's where slice-counting does the most damage. The
model says two of everything beats one. Two servers, two regions. That holds
right up until both copies share a single reason to die at the same instant.</p>
<p>On 19 July 2024 CrowdStrike
<a href="https://www.crowdstrike.com/en-us/blog/channel-file-291-rca-available/">shipped a content update</a>
to its Falcon sensor and bricked around 8.5 million Windows machines, by
Microsoft's estimate, inside a few hours. Every one of those hosts was
&quot;redundant&quot; in somebody's architecture diagram. Made no difference. They ran the
same agent and swallowed the same bad file at the same moment, so they all
dropped together. When the failure is perfectly correlated like that, redundancy
buys you nothing. NASA's reliability folks put it bluntly: if one fault can take
out the backup too, two subsystems fail twice as often as one.</p>
<p><img src="https://jonno.nz/img/posts/the-holes-that-kill-you-are-the-ones-you-never-tested-fig-1.png" alt="A blueprint cross-section of stacked redundant defence layers, each riddled with holes, with a single skewer passing through a hole aligned in the exact same spot on every layer."></p>
<p>There's a quieter version of the same trap hiding in your SLOs. String together
a dozen services that are each up 99.9% of the time and your ceiling is about
98.8% before you've written a line of your own code, because every dependency is
one more slice with its own holes. You can't promise more reliability than the
flakiest thing you lean on, and each extra nine costs more than the last.</p>
<p>The slices you can draw on a whiteboard are the safe ones. The holes that get
you are the ones nobody can see: the failover that's never been run, the
assumption that stopped being true six months ago when no one was looking, the
dead code still sitting in production behind a flag.</p>
<p>Knight Capital is the one I'd tattoo on a junior engineer. On 1 August 2012 they
switched on new trading code and pushed it to seven of their eight servers. On
the eighth, a reused flag woke a dead function called Power Peg that should have
been ripped out years earlier. For about 45 minutes it hurled orders into the
market with nothing counting the fills:
<a href="https://www.sec.gov/files/litigation/admin/2013/34-70694.pdf">more than four million executions and 397 million shares</a>.
Knight's own books put the loss at roughly $440 million, and the firm didn't
survive the week.</p>
<p>Pull it apart and every hole was survivable on its own: dead code left in the
build, a flag doing double duty, a deploy one person ran with nobody reviewing
it, 97 warning emails before the open that everyone read as noise. None of those
sinks you alone. Line them up on a single Tuesday morning and the company is
gone.</p>
<p>That's Reason's real point, and it's a good one. The model is right. It's just
coarse. Richard Cook said it better in
<a href="https://how.complexsystems.fail/">How Complex Systems Fail</a>: complex systems
run in a degraded state the whole time, stuffed with small faults, staying up
because people quietly hold them together. Catastrophe needs a few of those
faults to meet. A sixth slice does nothing about the holes already living in the
five you've got.</p>
<p>Your architecture diagram and your runbook describe the system you imagine you
have. The real system is whatever your on-call engineer does at 3am to keep it
limping along. Erik Hollnagel calls that gap
<a href="https://www.england.nhs.uk/signuptosafety/wp-content/uploads/sites/16/2015/10/safety-1-safety-2-whte-papr.pdf">work-as-imagined versus work-as-done</a>,
and the holes love living in it.</p>
<p>The useful work is dragging those invisible holes into daylight while you still
get to pick the timing. Adrian Cockcroft has the perfect name for skipping it.
He calls an untested failover
<a href="https://www.gremlin.com/blog/adrian-cockroft-chaos-engineering-what-it-is-and-where-its-going-chaos-conf-2018">&quot;availability theatre&quot;</a>:
you've got the runbook, you've got the standby, you feel great, and you have no
clue whether any of it works because you've never once pulled the plug to watch.</p>
<p><img src="https://jonno.nz/img/posts/the-holes-that-kill-you-are-the-ones-you-never-tested-fig-2.png" alt="A blueprint cross-section of the same holey layers slid apart and probed by hand, a thin light beam catching two holes about to line up."></p>
<p>This is why the teams who are good at this spend their hours on stuff that looks
unglamorous. They run game days and break things in production on purpose, the
way Netflix set Chaos Monkey loose to kill its own servers at random, just to
prove the system could take it. They write
<a href="https://sre.google/sre-book/postmortem-culture/">blameless postmortems</a>,
because the day people get punished for an outage is the day they stop telling
you where the holes are. They treat reliability as a budget they spend, not a
feeling they chase.</p>
<p>Most of that is a people problem wearing a technology costume. The
highest-leverage slice in your whole stack is usually the on-call engineer at
3am, and whether your culture lets them say &quot;yeah, I broke it, here's how&quot;
without flinching. No cloud provider sells that one.</p>
<p>I've built billing and payments platforms where downtime means somebody doesn't
get paid, and shipped to production many times a day. The urge to bolt on
another layer never goes away. It just gets more expensive, and more comforting.</p>
<p>By all means keep your redundancy. Keep the regions and the replicas. Just don't
kid yourself that a standby you've never failed over to is a second slice of
cheese. It's a hole with a comforting label, and you'll find out which one it
really is on the worst possible morning.</p>
]]>
      </content:encoded>
      <pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/the-holes-that-kill-you-are-the-ones-you-never-tested.png"/>
    </item>
    <item>
      <title>The Laws of Human Nature</title>
      <link>https://jonno.nz/posts/the-18-laws-of-human-nature/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/the-18-laws-of-human-nature/</guid>
      <description>An interactive read-through of Robert Greene's 18 laws — 18 cards, full essays inside each.</description>
      <content:encoded>
        <![CDATA[<style>
.laws-feature {
  --font-text: var(--sans);
  --font-mono: var(--mono);
  --text-muted: var(--muted);
  --text-bright: var(--bright);
  --border: var(--rule);
  --border-strong: rgba(255, 255, 255, 0.18);
  --bg-surface: #10151c;
  --accent-hover: #e0bb75;
  --tag-color: #7eb8a8;
  --social-tint: #9aaecb;
  --bg-surface-2: #19243480;
}
.has-laws-reader #laws-root {
  /* Keep the following prose below the viewport while the reader starts. */
  min-height: 100vh;
}
.laws-feature *,
.laws-feature *::before,
.laws-feature *::after {
  box-sizing: border-box;
}
.laws-feature button {
  font-family: inherit;
  cursor: pointer;
}

/* Section rule used between intro and the map */
.laws-feature .section-rule {
  display: flex; align-items: baseline; gap: 0.75rem;
  margin: 3rem 0 1.5rem;
  padding-bottom: 0.65rem;
  border-bottom: 1px solid var(--border);
}
.laws-feature .section-rule h2 {
  font-family: var(--font-mono); font-size: 0.75rem; font-weight: 400;
  color: var(--text-muted); letter-spacing: 0.06em; text-transform: uppercase;
  margin: 0;
  border-bottom: 0;
  padding: 0;
}
.laws-feature .section-rule .count {
  font-family: var(--font-mono); font-size: 0.75rem; color: var(--accent);
}
.laws-feature .section-rule .spacer { flex: 1; }
.laws-feature .section-rule .hint {
  font-family: var(--font-mono); font-size: 0.7rem; color: var(--text-muted);
}

/* Laws app */
.laws-app {
  --gap: 1rem;
  --card-pad: 1.1rem;
}
.laws-toolbar {
  margin-bottom: 1.75rem;
  border: 1px solid var(--border);
  border-radius: 0.3rem;
  background: rgba(255, 255, 255, 0.015);
  overflow: hidden;
}
.lt-row {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.75rem 1rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
}
.lt-label {
  color: var(--text-muted); text-transform: uppercase;
  letter-spacing: 0.08em; font-size: 0.62rem;
  min-width: 4.5rem;
}
.lt-progress .lt-bar {
  flex: 1; height: 4px; background: var(--border); border-radius: 2px; position: relative; overflow: hidden;
}
.lt-progress .lt-fill {
  position: absolute; top: 0; left: 0; bottom: 0;
  background: linear-gradient(90deg, var(--accent), var(--accent-hover));
  border-radius: 2px;
  transition: width 0.35s cubic-bezier(.4,0,.2,1);
  box-shadow: 0 0 8px rgba(212, 168, 83, 0.4);
}
.lt-progress .lt-count { color: var(--text-bright); font-size: 0.75rem; }
.lt-progress .lt-count b { color: var(--accent); font-weight: 500; }
.lt-progress .lt-count .of { color: var(--text-muted); margin-left: 0.1rem; }
.lt-progress .lt-clear {
  background: none; border: 1px solid var(--border);
  color: var(--text-muted); padding: 0.25rem 0.55rem;
  border-radius: 0.15rem; font-family: inherit; font-size: 0.62rem;
  text-transform: uppercase; letter-spacing: 0.08em;
  transition: color 0.15s, border-color 0.15s;
}
.lt-progress .lt-clear:hover {
  color: var(--accent); border-color: rgba(212, 168, 83, 0.4);
}

/* Theme tokens */
.laws-feature [data-theme="self"]   { --t-color: var(--accent);      --t-glow: 212, 168, 83; }
.laws-feature [data-theme="others"] { --t-color: var(--tag-color);   --t-glow: 126, 184, 168; }
.laws-feature [data-theme="social"] { --t-color: var(--social-tint); --t-glow: 154, 174, 203; }

/* Sigil animations */
@keyframes sig-draw { to { stroke-dashoffset: 0; } }
@keyframes sig-dot  { to { opacity: 1; } }
@keyframes sig-pulse {
  0%, 100% { transform: scale(1); filter: none; }
  50%      { transform: scale(1.4); }
}

/* Theme band grid */
.grid-view { display: flex; flex-direction: column; gap: 2rem; }
.theme-band {}
.tb-head {
  display: grid;
  grid-template-columns: auto auto 1fr auto;
  align-items: baseline;
  gap: 0.85rem;
  padding: 0.65rem 0 0.75rem;
  margin-bottom: 1rem;
  border-bottom: 1px solid var(--border);
  position: relative;
}
.tb-head.with-stripe::before {
  content: ''; position: absolute; left: 0; bottom: -1px; height: 1px; width: 4rem;
  background: var(--t-color); opacity: 0.7;
}
.tb-range {
  font-family: var(--font-mono); font-size: 0.65rem;
  color: var(--t-color); letter-spacing: 0.12em;
  padding: 0.15rem 0.5rem; border: 1px solid currentColor;
  border-radius: 0.15rem; opacity: 0.75;
}
.tb-label {
  font-family: var(--font-text); font-size: 1.05rem;
  font-weight: 500; color: var(--text-bright); letter-spacing: -0.02em;
  margin: 0;
}
.tb-sub {
  font-family: var(--font-mono); font-size: 0.7rem;
  color: var(--text-muted); letter-spacing: 0.02em;
}
.tb-count {
  font-family: var(--font-mono); font-size: 0.72rem;
  color: var(--t-color);
}
.tb-count .of { color: var(--text-muted); }
.tb-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gap);
}

/* Law card */
.law-card {
  text-align: left;
  background: rgba(255, 255, 255, 0.015);
  border: 1px solid var(--border);
  border-radius: 0.35rem;
  padding: var(--card-pad);
  color: var(--text);
  transition: border-color 0.18s, background 0.18s, transform 0.18s;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  position: relative;
  overflow: hidden;
  min-height: 13rem;
  font-family: var(--font-text);
}
.law-card::before {
  content: ''; position: absolute; top: 0; left: 0; bottom: 0; width: 2px;
  background: var(--t-color); opacity: 0; transition: opacity 0.18s;
}
.law-card:hover {
  border-color: rgba(255, 255, 255, 0.14);
  background: rgba(255, 255, 255, 0.025);
}
.law-card:hover::before { opacity: 0.5; }
.law-card.is-active {
  border-color: var(--t-color);
  background: rgba(var(--t-glow), 0.06);
}
.law-card.is-active::before { opacity: 1; }
.law-card.is-read .lc-title { color: var(--text-muted); }
.law-card.is-read .lc-illus { opacity: 0.55; }
.lc-illus {
  width: 56px; height: 56px;
  display: flex; align-items: center; justify-content: center;
  transition: opacity 0.18s, transform 0.25s cubic-bezier(.4,0,.2,1);
}
.lc-illus img {
  width: 100%; height: 100%; display: block;
}
.law-card:hover .lc-illus {
  transform: translateY(-1px);
}
.lc-body { display: flex; flex-direction: column; gap: 0.45rem; }
.lc-meta { display: flex; align-items: center; justify-content: space-between; }
.num-glyph {
  font-family: var(--font-mono); font-size: 0.7rem;
  color: var(--t-color); letter-spacing: 0.05em;
  opacity: 0.85;
}
.lc-read {
  font-family: var(--font-mono); font-size: 0.6rem;
  color: var(--tag-color); text-transform: uppercase; letter-spacing: 0.1em;
  padding: 0.1rem 0.4rem; border: 1px solid rgba(126, 184, 168, 0.3);
  border-radius: 0.15rem; opacity: 0.85;
}
.lc-title {
  font-family: var(--font-text);
  font-size: 1.05rem;
  font-weight: 500;
  color: var(--text-bright);
  letter-spacing: -0.02em;
  line-height: 1.25;
  text-wrap: balance;
  transition: color 0.18s;
  margin: 0;
}
.lc-essence {
  font-size: 0.85rem;
  line-height: 1.55;
  color: var(--text-muted);
  text-wrap: pretty;
  margin: 0;
}

/* Theme badge */
.theme-badge {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-family: var(--font-mono); font-size: 0.62rem;
  color: var(--t-color); text-transform: uppercase; letter-spacing: 0.1em;
  padding: 0.2rem 0.55rem;
  border: 1px solid currentColor; border-radius: 0.15rem;
  opacity: 0.85;
  white-space: nowrap;
}
.tb-dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; }

/* Modal — reusable wrapper around native <dialog> via window.Modal */
dialog.laws-modal {
  border: none;
  padding: 0;
  margin: auto;
  background: transparent;
  color: var(--text);
  width: min(48rem, 92vw);
  max-width: 48rem;
  max-height: 88vh;
  border-radius: 0.6rem;
  overflow: visible;
  box-shadow:
    0 30px 80px -20px rgba(0, 0, 0, 0.7),
    0 0 0 1px var(--border-strong);
  /* allow the inner panel to define its own backdrop colour */
}
dialog.laws-modal::backdrop {
  background:
    radial-gradient(ellipse at center, rgba(0,0,0,0.6) 0%, rgba(12, 21, 32, 0.92) 70%);
  -webkit-backdrop-filter: blur(8px) saturate(140%);
  backdrop-filter: blur(8px) saturate(140%);
  animation: modal-backdrop-in 0.18s ease-out;
}
dialog.laws-modal[open] {
  animation: modal-in 0.22s cubic-bezier(.2,0,.2,1);
}
@keyframes modal-in {
  from { opacity: 0; transform: translateY(8px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0)   scale(1); }
}
@keyframes modal-backdrop-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Focus panel — now lives inside the dialog */
.focus-panel {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border-strong);
  border-radius: 0.6rem;
  background:
    linear-gradient(180deg, rgba(255,255,255,0.025), transparent 30%),
    var(--bg-surface);
  overflow: hidden;
  position: relative;
  max-height: 88vh;
}
.focus-panel::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: linear-gradient(90deg, transparent, var(--t-color), transparent);
  opacity: 0.85;
  z-index: 3;
}

/* Scrollable body inside the panel; head + foot stay docked */
.fp-scroll {
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
.fp-scroll::-webkit-scrollbar { width: 8px; }
.fp-scroll::-webkit-scrollbar-track { background: transparent; }
.fp-scroll::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: 4px;
}
.fp-scroll::-webkit-scrollbar-thumb:hover {
  background: rgba(212, 168, 83, 0.45);
}

.fp-head {
  display: flex; justify-content: space-between; align-items: center;
  padding: 0.9rem 1.5rem;
  border-bottom: 1px solid var(--border);
  background: rgba(0,0,0,0.25);
  flex-shrink: 0;
}
.fp-head-l { display: flex; align-items: center; gap: 0.85rem; flex-wrap: wrap; }
.fp-head-l .num-glyph { font-size: 0.78rem; }
.fp-close {
  background: none; border: 1px solid var(--border);
  color: var(--text-muted); padding: 0.25rem 0.65rem;
  border-radius: 0.15rem; font-family: var(--font-mono); font-size: 0.62rem;
  text-transform: uppercase; letter-spacing: 0.08em;
  transition: color 0.15s, border-color 0.15s;
  white-space: nowrap;
}
.fp-close:hover { color: var(--accent); border-color: rgba(212, 168, 83, 0.4); }
.fp-hero {
  display: grid;
  grid-template-columns: 180px 1fr;
  gap: 1.75rem;
  padding: 2rem 1.75rem 1.75rem;
  align-items: start;
  border-bottom: 1px solid var(--border);
}
.fp-hero-illus {
  width: 180px; height: 180px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--border);
  background:
    radial-gradient(circle at center, rgba(var(--t-glow), 0.12), transparent 70%),
    rgba(0,0,0,0.28);
  border-radius: 0.5rem;
  padding: 1.25rem;
  position: relative;
  overflow: hidden;
}
.fp-hero-illus::before {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(circle at top left, rgba(var(--t-glow), 0.1), transparent 60%);
  pointer-events: none;
}
.fp-hero-illus img {
  width: 100%; height: 100%; display: block; position: relative; z-index: 1;
  filter: drop-shadow(0 4px 14px rgba(var(--t-glow), 0.18));
}
.fp-hero-text { min-width: 0; }
.fp-title {
  font-family: var(--font-text);
  font-size: 1.85rem;
  font-weight: 500;
  color: var(--text-bright);
  letter-spacing: -0.025em;
  line-height: 1.12;
  margin: 0 0 0.85rem;
  text-wrap: balance;
}
.fp-essence {
  font-size: 1.05rem;
  line-height: 1.6;
  color: var(--text);
  margin: 0 0 1.1rem;
  text-wrap: pretty;
}
.fp-pull {
  border-left: 2px solid var(--t-color);
  padding: 0.25rem 0 0.25rem 1rem;
  margin: 0 0 1.1rem;
  color: var(--text-muted);
  font-style: italic;
  font-size: 1.02rem;
  line-height: 1.5;
  text-wrap: balance;
}
.fp-kw { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.fp-kw .kw {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--tag-color);
}

/* Article in focus panel */
.fp-article {
  padding: 1.25rem 1.75rem 1.5rem;
  max-width: 44rem;
  margin: 0 auto;
}
.fp-section {
  padding-top: 2.5rem;
  margin-top: 0;
}
.fp-section:first-child { padding-top: 1.5rem; }
.fp-section-label {
  display: flex; align-items: baseline; gap: 0.65rem;
  margin-bottom: 1.25rem;
  padding-bottom: 0.65rem;
  border-bottom: 1px solid var(--border);
}
.fp-section-n {
  font-family: var(--font-mono);
  font-size: 0.65rem;
  color: var(--t-color);
  letter-spacing: 0.1em;
  padding: 0.15rem 0.5rem;
  border: 1px solid currentColor;
  border-radius: 0.15rem;
  opacity: 0.85;
}
.fp-section-name {
  font-family: var(--font-mono);
  font-size: 0.74rem;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.fp-section-body {
  font-family: var(--font-text);
  font-weight: 400;
  font-size: 1.02rem;
  line-height: 1.78;
  color: var(--text);
}
.fp-section-body p {
  margin: 0 0 1.35em;
  text-wrap: pretty;
}
.fp-section-body p:last-child { margin-bottom: 0; }
.fp-section-body em { font-style: italic; color: var(--text-bright); }
.fp-section-body strong, .fp-section-body b {
  font-weight: 500; color: var(--text-bright);
}
.fp-section-body h4 {
  font-family: var(--font-text);
  font-size: 1.05rem;
  font-weight: 500;
  color: var(--text-bright);
  letter-spacing: -0.015em;
  margin: 2rem 0 0.85rem;
  line-height: 1.3;
  display: flex; align-items: baseline; gap: 0.65rem; flex-wrap: wrap;
  border-bottom: 0;
  padding: 0;
}
.fp-section-body h4:first-child { margin-top: 0.25rem; }
.fp-section-body h4 .ex-year {
  font-family: var(--font-mono);
  font-size: 0.65rem;
  color: var(--text-muted);
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.15rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 0.15rem;
}
.fp-section-body ol, .fp-section-body ul {
  padding-left: 0;
  list-style: none;
  counter-reset: fpitem;
  margin: 0;
}
.fp-section-body ol li {
  counter-increment: fpitem;
  padding: 0.5rem 0 0.75rem 2.75rem;
  position: relative;
  border-bottom: 1px solid var(--border);
  margin-bottom: 0.25rem;
}
.fp-section-body ol li:last-child { border-bottom: none; }
.fp-section-body ol li::before {
  content: counter(fpitem, decimal-leading-zero);
  position: absolute;
  left: 0;
  top: 0.7rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--t-color);
  letter-spacing: 0.06em;
  padding: 0.1rem 0.45rem;
  border: 1px solid currentColor;
  border-radius: 0.15rem;
  opacity: 0.85;
  line-height: 1.4;
}
.fp-section-body ul li {
  padding: 0.25rem 0 0.35rem 1.5rem;
  position: relative;
}
.fp-section-body ul li::before {
  content: '—';
  position: absolute;
  left: 0;
  color: var(--t-color);
  opacity: 0.6;
}

/* Focus footer */
.fp-foot {
  display: flex; justify-content: space-between; align-items: center;
  padding: 0.85rem 1.5rem;
  border-top: 1px solid var(--border);
  background: rgba(0,0,0,0.32);
  flex-wrap: wrap;
  gap: 0.75rem;
  flex-shrink: 0;
}
.fp-foot-l, .fp-foot-r { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
.fp-link {
  background: none; border: 1px solid var(--border);
  color: var(--text-muted);
  padding: 0.4rem 0.8rem; border-radius: 0.15rem;
  font-family: var(--font-mono); font-size: 0.68rem;
  text-transform: uppercase; letter-spacing: 0.08em;
  transition: color 0.15s, border-color 0.15s;
  display: inline-flex; align-items: center; gap: 0.4rem;
  white-space: nowrap;
}
.fp-link:hover { color: var(--text-bright); border-color: var(--border-strong); }
.fp-arrow { color: var(--accent); font-size: 0.78rem; line-height: 1; }
.fp-mark {
  background: none; border: 1px solid var(--border);
  color: var(--text-muted);
  padding: 0.4rem 0.85rem; border-radius: 0.15rem;
  font-family: var(--font-mono); font-size: 0.68rem;
  text-transform: uppercase; letter-spacing: 0.08em;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
  display: inline-flex; align-items: center; gap: 0.45rem;
  white-space: nowrap;
}
.fp-mark:hover { color: var(--text-bright); border-color: var(--border-strong); }
.fp-mark.is-read {
  color: var(--tag-color);
  border-color: rgba(126, 184, 168, 0.4);
  background: rgba(126, 184, 168, 0.06);
}
.fp-tick { font-size: 0.8rem; line-height: 1; }
/* Reading paths */
.reading-paths {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.75rem;
  margin: 1rem 0 2.5rem;
}
.rp-card {
  padding: 1rem 1.1rem;
  border: 1px solid var(--border);
  border-radius: 0.3rem;
  background: rgba(255, 255, 255, 0.015);
  transition: border-color 0.15s, background 0.15s;
  color: var(--text);
  display: block;
}
.rp-card:hover {
  border-color: rgba(212, 168, 83, 0.3);
  background: rgba(212, 168, 83, 0.03);
  color: var(--text);
}
.rp-card .rp-tag {
  font-family: var(--font-mono); font-size: 0.62rem;
  color: var(--accent); letter-spacing: 0.1em; text-transform: uppercase;
  display: block; margin-bottom: 0.5rem;
}
.rp-card .rp-title {
  font-size: 0.98rem;
  font-weight: 500;
  color: var(--text-bright);
  letter-spacing: -0.015em;
  margin-bottom: 0.4rem;
}
.rp-card .rp-desc {
  font-size: 0.82rem;
  color: var(--text-muted);
  line-height: 1.5;
  margin: 0;
}
.rp-card .rp-laws {
  margin-top: 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.65rem;
  color: var(--text-muted);
  letter-spacing: 0.06em;
}
.rp-card .rp-laws b { color: var(--accent); font-weight: 500; }

/* Make all interactive bits friendly to touch */
.laws-feature button,
.laws-feature .rp-card,
.laws-feature dialog.laws-modal button {
  touch-action: manipulation;
}

/* Responsive */
@media (max-width: 900px) {
  .laws-feature .tb-cards { grid-template-columns: repeat(2, 1fr); }
  .laws-feature .reading-paths { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
  /* Grid + cards */
  .laws-feature .tb-cards { grid-template-columns: 1fr; }
  .laws-feature .tb-head { grid-template-columns: auto 1fr; }
  .laws-feature .tb-head .tb-sub { display: none; }

  /* Modal sizing — nearly fullscreen with safe margin, dvh for iOS dynamic toolbar */
  .laws-feature dialog.laws-modal {
    width: 96vw;
    max-width: 96vw;
    max-height: 92vh;
    max-height: 92dvh;
    border-radius: 0.5rem;
  }
  .laws-feature .focus-panel {
    max-height: 92vh;
    max-height: 92dvh;
    border-radius: 0.5rem;
  }
  /* Cheaper backdrop blur on phones */
  .laws-feature dialog.laws-modal::backdrop {
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
  }

  /* Head/foot tighten and grow tap targets */
  .laws-feature .fp-head { padding: 0.7rem 1rem; }
  .laws-feature .fp-close {
    padding: 0.5rem 0.85rem;
    min-height: 36px;
    font-size: 0.66rem;
  }
  .laws-feature .fp-foot {
    padding: 0.7rem 0.85rem;
    gap: 0.5rem;
  }
  .laws-feature .fp-foot-l,
  .laws-feature .fp-foot-r {
    gap: 0.4rem;
    flex-wrap: wrap;
  }
  .laws-feature .fp-link,
  .laws-feature .fp-mark {
    padding: 0.5rem 0.75rem;
    min-height: 36px;
    font-size: 0.66rem;
  }

  /* Hero stack + comfy reading */
  .laws-feature .fp-hero {
    grid-template-columns: 1fr;
    gap: 1rem;
    padding: 1.25rem 1.1rem;
  }
  .laws-feature .fp-hero-illus {
    width: 100%;
    max-width: 180px;
    height: 140px;
    margin: 0 auto;
  }
  .laws-feature .fp-title { font-size: 1.35rem; }
  .laws-feature .fp-essence { font-size: 0.98rem; line-height: 1.55; }
  .laws-feature .fp-pull { font-size: 0.95rem; }
  .laws-feature .fp-article { padding: 1rem 1.1rem 1.25rem; }
  .laws-feature .fp-section-body { font-size: 0.98rem; line-height: 1.72; }
  .laws-feature .fp-section-body ol li { padding-left: 2.5rem; }

  /* Toolbar / progress on phones */
  .laws-feature .lt-row { padding: 0.65rem 0.85rem; gap: 0.6rem; }
  .laws-feature .lt-label { min-width: 3.5rem; }
  .laws-feature .lt-progress .lt-clear { padding: 0.3rem 0.55rem; }
}

@media (max-width: 420px) {
  /* Very small phones — keep buttons readable, allow foot to wrap to two rows */
  .laws-feature .fp-link,
  .laws-feature .fp-mark { padding: 0.45rem 0.6rem; font-size: 0.62rem; }
  .laws-feature .fp-head { padding: 0.6rem 0.85rem; }
  .laws-feature .fp-title { font-size: 1.2rem; }
  .laws-feature .fp-hero-illus { height: 120px; max-width: 140px; }
  .laws-feature .fp-section-label { gap: 0.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  .laws-feature *,
  .laws-feature *::before,
  .laws-feature *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
</style>
<div class="laws-feature">
<div class="section-rule">
  <h2>The 18 laws</h2>
  <span class="count">18</span>
  <span class="spacer"></span>
  <span class="hint">tap a card to read it in full</span>
</div>
<script>document.documentElement.classList.add("has-laws-reader");</script>
<div id="laws-root"></div>
<div class="section-rule" style="margin-top: 3.5rem;">
  <h2>Reading paths</h2>
  <span class="count">3</span>
</div>
<div class="reading-paths">
  <div class="rp-card">
    <span class="rp-tag">canonical</span>
    <div class="rp-title">Start at the beginning</div>
    <p class="rp-desc">Greene's order: master yourself, then decode others, then handle the social dynamics. Builds the vocabulary you need for the later laws.</p>
    <div class="rp-laws"><b>01</b> → <b>18</b></div>
  </div>
  <div class="rp-card">
    <span class="rp-tag">most useful</span>
    <div class="rp-title">If you only read five</div>
    <p class="rp-desc">The five that change the most about how you read a room. Irrationality, role-playing, envy, defensiveness, aimlessness.</p>
    <div class="rp-laws"><b>01</b> · <b>06</b> · <b>08</b> · <b>13</b> · <b>15</b></div>
  </div>
  <div class="rp-card">
    <span class="rp-tag">most uncomfortable</span>
    <div class="rp-title">The ones that sting</div>
    <p class="rp-desc">Greene at his most surgical. Repression, grandiosity, envy, death denial. Read these slowly and with someone who'll tell you the truth.</p>
    <div class="rp-laws"><b>03</b> · <b>04</b> · <b>08</b> · <b>10</b></div>
  </div>
</div>
</div>
<p>I recently finished Robert Greene's <em>The Laws of Human Nature</em> and it wouldn't
leave me alone. Eighteen laws, each one a separate pattern in how people
actually behave (not how we like to think we behave). I kept catching myself
watching the patterns play out in real life: in meetings, in news cycles, in my
own head.</p>
<p>So I built the thing above. Eighteen cards, one per law. Tap any of them to read
my full take, with two real-world examples and a short list of behavioural
things you can do this week. About a thousand words a law, around twenty
thousand total. Your read-progress sticks between visits.</p>
<p>If a card grabs you, that's the one to start with. If not, the canonical order
at the top is a fine path. Either way, the actual book is much richer than this
map. <strong>Robert Greene</strong> wrote it; if you want the deep version,
<a href="https://www.amazon.com.au/Laws-Human-Nature-Robert-Greene/dp/1781259194">grab a copy on Amazon</a>{target=&quot;_blank&quot;
rel=&quot;noopener&quot;}.</p>
<p>A note on attribution. The names, the structure and the underlying observations
are all Greene's (<em>The Laws of Human Nature</em>, Profile Books, 2018). The writing
is mine, the modern examples are mine, and any wrong reading of his ideas is
mine.</p>
<script defer src="https://unpkg.com/react@18.3.1/umd/react.production.min.js" crossorigin="anonymous"></script>
<script defer src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.production.min.js" crossorigin="anonymous"></script>
<script defer src="https://jonno.nz/laws/laws-data.js"></script>
<script defer src="https://jonno.nz/laws/laws-content.js"></script>
<script defer src="https://jonno.nz/lib/modal.js"></script>
<script defer src="https://jonno.nz/laws/laws-app.js"></script>
]]>
      </content:encoded>
      <pubDate>Tue, 26 May 2026 12:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/the-18-laws-of-human-nature.png"/>
    </item>
    <item>
      <title>Conscious Minimalism</title>
      <link>https://jonno.nz/posts/conscious-minimalism/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/conscious-minimalism/</guid>
      <description>How I build: small, narrow, niche, AI-native.</description>
      <content:encoded>
        <![CDATA[<p>I build with Conscious Minimalism.</p>
<p>Conscious Minimalism means every decision, every feature, every commitment is a
choice. Nothing ships by default. The shape grows with the customer, not the
roadmap.</p>
<p>I obsess over the interface, not the internals. The boundary is what you live
with, and what you refactor and scale.</p>
<p>I optimise for speed to de-validation. The faster I kill a bad idea, the less
sunk cost owns me.</p>
<p>I fix one narrow painpoint, not a broad category or a platform play. One thing,
done properly.</p>
<p>I aim to be the best at that one feature, not one of the good ones in the list.</p>
<p>I chase niche over crowded. Niche means the idea is more unique, with a better
outcome than the legacy approach.</p>
<p>Crowded is a signal of competition. Without a 10x better solution, you probably
will not make it.</p>
<p>I bet on AI-native platforms and APIs. Anything that does not use AI as a
primitive is already legacy.</p>
]]>
      </content:encoded>
      <pubDate>Thu, 14 May 2026 12:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/conscious-minimalism.png"/>
    </item>
    <item>
      <title>The dent and the crater</title>
      <link>https://jonno.nz/posts/the-dent-and-the-crater/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/the-dent-and-the-crater/</guid>
      <description>How a bad year can change the way you lead, and how to keep your team talking to you.</description>
      <content:encoded>
        <![CDATA[<blockquote>
<p>&quot;The world breaks every one and afterward many are strong at the broken
places. But those that will not break it kills.&quot;</p>
<p>Ernest Hemingway, <em>A Farewell to Arms</em> (1929)</p>
</blockquote>
<p>Some of the senior leaders I've worked with changed after a bad year. A board
turned against them, a launch failed, or a cofounder left. Months later, they
were still organising their work around making sure it couldn't happen again.</p>
<p>You can see it in small decisions. A leader adds another approval because the
last launch went wrong. They dismiss a pitch because it resembles a company they
once backed. They stop asking for advice on the decision that hurt them. Each
choice has an explanation, and experience gives them plenty of reasons to sound
convincing.</p>
<p>I think of the original setback as a dent. The crater develops when avoiding
another setback starts to determine how you work. You spend less time asking
whether a new idea is good and more time looking for the part that could hurt
you again.</p>
<p>Some caution makes sense. You should understand why a launch failed and change
the things that contributed to it. The difficulty is noticing when you've
extended that caution to decisions that have little to do with the failure.</p>
<p>A new hire brings an idea and you tell them you've seen it before. You close
your door and call it focus. You put another review on the calendar without
asking what the existing review missed. From your side, you're applying what
you've learned. The people working with you may experience it as a growing
reluctance to hear them out.</p>
<p>You might not notice until they stop bringing you rough ideas. They wait until
they can defend a proposal before sharing it, or they ask someone else. You get
fewer opportunities to help while the work is taking shape. If you've also
pulled back from customer calls, you can be making decisions with less
information while feeling more certain about them.</p>
<p>The leaders I've seen stay open over time have people they can talk to about
this. Some use a coach. Others meet with a few peers who know enough about the
job to challenge their account of a difficult quarter. A partner or a mate
outside the industry can help too, especially if they aren't impressed by your
title and will tell you when you're being unreasonable.</p>
<p>Those conversations need room for the part you're embarrassed about. You might
have handled the board meeting well and still be afraid of going through it
again. You might know the team needs more autonomy and find yourself checking
their work anyway. Saying that to someone gives you a chance to examine it.</p>
<p>It also helps to have something outside work where your role carries no weight.
The leaders I know who ride, surf or build things make time for an activity
where they can be a beginner, get something wrong, and carry on. You need some
part of your life that doesn't depend on being the person with the answer.</p>
<p>This takes attention because your team will adapt to how you behave. If you cut
people off, they learn to keep difficult conversations short. If you dismiss an
idea before asking a question, they get more cautious about suggesting one. They
have work to get through and will find a way to do it with the leader they have.</p>
<p>That adaptation can make the problem harder to spot. Meetings become smoother.
People bring fewer disagreements to you. You may take that as evidence that the
team is working well together, especially after a period when everything felt
difficult.</p>
<p>Meanwhile, the people closest to the work are choosing what to tell you based on
the reaction they expect. By the time a problem reaches your desk, they may have
spent weeks trying to solve it without involving you.</p>
]]>
      </content:encoded>
      <pubDate>Wed, 06 May 2026 12:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/the-dent-and-the-crater.png"/>
    </item>
    <item>
      <title>I Built a Read-Later Chrome Extension Because Pocket Died</title>
      <link>https://jonno.nz/posts/built-a-read-later-chrome-extension-because-pocket-died/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/built-a-read-later-chrome-extension-because-pocket-died/</guid>
      <description>
        Pocket shut down in July 2025. I needed somewhere to dump the articles I keep telling myself I'll read later. So I built a Chrome extension that does exactly that and nothing else. Local-only, no accounts, no tracking.
      </description>
      <content:encoded>
        <![CDATA[<p>I open about thirty tabs a day. I read maybe three of them. The rest are &quot;oh
that looks interesting, I'll come back to that,&quot; and then they sit there until
my browser starts choking and I close everything in a fit of guilt.</p>
<p>Bookmarks aren't the answer. Bookmarks go into a folder I never open. Pocket was
the answer, except
<a href="https://support.mozilla.org/en-US/kb/future-of-pocket">Mozilla shut Pocket down in July 2025</a>,
which I found out the way most people did: by going to save something and
discovering the service was gone.</p>
<p>So I built
<a href="https://chromewebstore.google.com/detail/read-later/oedgonnjlnokhocngfjmflchbjdmgmag">Read Later</a>.
It's a Chrome extension. It saves the page you're on. That's the whole pitch.</p>
<h2>What it actually is</h2>
<p>You hit <code>Cmd+Shift+L</code> and the current tab gets saved with its title, URL, and
favicon. Add a tag if you want. Done. There's a popup if you'd rather click a
button, and a context menu if you'd rather right-click a link without opening it
first.</p>
<p>There's also a full-tab &quot;shelf&quot; view at <code>Cmd+Shift+K</code> where all your saved
articles live. Search, filter by tag, mark as read, archive. The aesthetic is
warm parchment and moss green because I got tired of every productivity tool
looking like a Linear screenshot.</p>
<p>That's it. No AI summaries, no recommendations, no &quot;people who saved this also
saved...&quot;, no newsletter, no account.</p>
<h2>Why I didn't just use bookmarks</h2>
<p>I tried. Bookmarks are designed for things you'll come back to many times. A
read-later list is the opposite, you read each thing once and then it's done.
The lifecycle is different. Stuffing them into the same UI is why my bookmarks
bar has been a graveyard for ten years.</p>
<p>The other read-later apps that survived Pocket all want an account, a sync
server, and usually a subscription. For something whose whole job is &quot;remember
this URL until I read it,&quot; that's a lot of infrastructure. I wanted local
storage and nothing else.</p>
<h2>Local-only on purpose</h2>
<p>Everything lives in <code>chrome.storage.local</code>. Nothing leaves your browser. There's
no server because there's nothing for a server to do. If you want to back up
your list, there's an Export button that gives you JSON. If you want to move it
to another machine, Import takes the JSON back. If you want to share your shelf
with someone, &quot;Copy as Markdown&quot; gives you a clean list you can paste into Slack
or an email.</p>
<p>This was a deliberate call. Sync is the feature that turns a small tool into a
service, and a service needs accounts, and accounts need a backend, and a
backend needs my time and money forever. JSON in, JSON out is the version of
&quot;sync&quot; that costs me nothing and gives the user complete control. Want it on two
machines? Export on one, Import on the other. Good enough.</p>
<h2>Shipping to the Chrome Web Store</h2>
<p>The real experiment was the launch. I've shipped plenty of things over the years
but never to the Chrome Web Store, and I wanted to see what that pipeline felt
like end to end. Pretty smooth, no drama. You pay the developer fee, fill in the
listing, upload a zipped manifest, wait for review. Mine went through first
time.</p>
<p>The work was in the listing copy and the screenshots. You're suddenly writing
for a discovery page where people decide in three seconds whether to click
install. That's a different muscle from writing a README. It made me think
harder about what the extension actually does for someone who isn't me.</p>
<p>I've got a folder on my laptop called &quot;side projects&quot; with about a dozen things
in it, each solving some specific bit of friction in my own life. Most of them
stay there. This one made it out because the friction was real, the build took
an afternoon, and putting something on the Chrome Web Store turned out to be a
satisfying little exercise.</p>
<p>If you've got tabs piling up and don't want to hand your reading list to a
startup, it's
<a href="https://chromewebstore.google.com/detail/read-later/oedgonnjlnokhocngfjmflchbjdmgmag">there</a>.
Source on <a href="https://github.com/jonnonz1/read-later">GitHub</a>, MIT licensed. Pin
the leaf icon and have a go.</p>
]]>
      </content:encoded>
      <pubDate>Tue, 05 May 2026 12:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/built-a-read-later-chrome-extension-because-pocket-died.png"/>
    </item>
    <item>
      <title>The product market fit gauntlet</title>
      <link>https://jonno.nz/posts/product-market-fit-is-a-gauntlet/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/product-market-fit-is-a-gauntlet/</guid>
      <description>Finding product market fit means revising the product, explaining what changed and keeping the team able to respond.</description>
      <content:encoded>
        <![CDATA[<p>Before you find product market fit, you can spend months shipping useful work
without knowing whether you're building a business. Two customers love it. Four
leave. Another will buy if you build a feature you're not sure belongs in the
product.</p>
<p>I've seen good teams make that search harder by committing to a roadmap or an
architecture before they understood who would pay. The work looked sensible when
they started it. A few months later, they needed to change direction and found
they had made that expensive.</p>
<p>The pressure reaches the team long before the numbers look convincing. Hiring,
customer commitments and technical decisions all have to leave you enough room
to learn.</p>
<p><img src="https://jonno.nz/img/posts/pmf-gauntlet/gauntlet-loop.svg" alt="The search for product market fit: test a hypothesis, ship, listen to customers and adapt. A rigid roadmap, delayed communication and premature scaling slow the cycle."></p>
<h2>Be clear about the job you're hiring for</h2>
<p>Before PMF, the metric you're chasing may change every six weeks. The answer to
&quot;what are we doing in three months?&quot; may depend on customer conversations you
haven't had yet. You need people who can work with that uncertainty without
waiting for someone to resolve it for them.</p>
<p>Some excellent operators prefer a clearer problem and a business with customers
whose needs they understand. They can struggle in a company that keeps changing
what it wants to be. That tells you something about the fit between the person
and the job, rather than their ability.</p>
<p>Be specific about this when hiring. Explain how often priorities have changed,
what you know about the market, and which assumptions you're testing. Someone
should be able to decide whether that sounds like work they want to do.</p>
<p>Calling it a fast-paced environment tells them very little. Tell them about the
feature you stopped building last month and why.</p>
<h2>Give the team a reason to keep going</h2>
<p>Timing, the economy and regulatory decisions can change demand while you're
building. You can have a good team and a plausible product and still struggle to
find buyers.</p>
<p>A clear purpose helps the team decide which changes are worth making. They need
to understand the customer problem well enough to recognise it when the original
product idea fails. Otherwise, each change of plan can feel like starting the
company again.</p>
<p>I've watched teams lose confidence when the founder could no longer explain why
they were pursuing a market. People could see that the numbers were poor, but
they couldn't tell what the company had learned or what would justify another
attempt.</p>
<p>The founder has to make those decisions while dealing with their own
disappointment. It's hard to reconsider a product when you've spent years
building it and persuading people to join you. Treating every result as a
verdict on your ability makes it harder to hear what customers are saying.</p>
<h2>Keep the product cheap to change</h2>
<p>At this stage, I care about how quickly a team can respond when a customer call
changes its understanding of the problem. A useful change should be possible in
days or weeks. If it needs a quarter of coordination, I want to know why.</p>
<p>Premature architecture decisions can account for a lot of that delay. A team
splits out microservices before it needs them. Someone brings infrastructure
from their last job. A small interface change now touches four repositories.
Engineers keep shipping, but more of their time goes into coordinating the work.</p>
<p>In his <a href="https://pmarchive.com/guide_to_startups_part4.html">2007 PMF essay</a>,
Marc Andreessen argues that founders may need to rewrite the product or move
markets to find a fit. Those options depend on the company being able to make
the change. Contracts, process and architecture can each turn a reasonable
decision into six months of work.</p>
<p>I prefer familiar tools and a small number of components until there's a reason
to add more. A design that accommodates every possible future is a lot to
maintain while you're working out which future is plausible.</p>
<h2>Look after the customers who bought the first version</h2>
<p>Your early customers may have bought something different from the product you're
now trying to build. Their revenue matters, and so do the commitments you made
to them.</p>
<p>Keeping every customer happy can pull you into custom work. Moving too quickly
can lose the customers paying your bills. You have to decide which requests
improve the product for the market you're pursuing, and which ones you are
fulfilling to honour an existing agreement.</p>
<p>Explain that distinction to the team. An engineer fixing a problem for an early
customer should know why that work takes priority, even if it doesn't advance
the new roadmap. Customers deserve the same clarity about what you'll support
and where the product is going.</p>
<p>Their feedback can help you refine the direction. It can also be a request for a
service you never intended to offer. Those need different responses.</p>
<h2>Share what you're learning</h2>
<p><img src="https://jonno.nz/img/posts/pmf-gauntlet/discipline-vs-fragility.svg" alt="Three ways a team can become less able to change: scaling architecture before it is needed, holding to a roadmap after the evidence changes, and keeping customer knowledge in the founder's head."></p>
<p>Product teams need to revise the roadmap when the evidence changes. A plan
records what you intend to test and why. After a month of customer
conversations, some of those assumptions may be wrong. Defending the old plan
because people have committed to it wastes the learning you've paid for.</p>
<p>That doesn't make changing priorities free. Work gets abandoned, people get
frustrated, and customers may be waiting for something you promised. Explain
what changed and what you will finish before moving on.</p>
<p>Founders can also get ahead of their teams without realising it. You've had the
customer calls, heard why a deal fell through and noticed a pattern in the data.
Your team is making decisions with last week's information. A request that seems
obvious to you may look arbitrary to them.</p>
<p>I think of this as comprehension debt. Small delays in sharing context build up
until the founder and the team are working from different assumptions. Sending
the revised priorities is only part of the job. People need enough of the
reasoning to make the next decision themselves.</p>
<h2>Be specific about your advantage</h2>
<p>I wouldn't count on an AI feature alone to keep competitors away. A similar
interface may be easy to build; the harder question is why a customer would keep
choosing your product once they have alternatives.</p>
<p><a href="https://www.latitudemedia.com/news/in-the-age-of-ai-can-startups-still-build-a-moat/">Anna Demeo's argument about AI moats</a>
puts weight on access to useful data, understanding a customer's workflow and
distribution. Those are more useful things to investigate than assuming the
model or a feature list will give you a lasting lead.</p>
<p>For an early company, these are assumptions to test too. Access to data helps if
you can use it to improve a result the customer cares about. An integration
matters if it makes the product useful in their daily work. A distribution
agreement matters if it reaches buyers.</p>
<p>You still have to make decisions before the evidence is complete. A board update
needs to account for a noisy quarter. An engineer needs to know why a feature
has moved down the list. Both deserve the reasoning you have, including the
parts you haven't worked out yet.</p>
]]>
      </content:encoded>
      <pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/product-market-fit-is-a-gauntlet.png"/>
    </item>
    <item>
      <title>Change management</title>
      <link>https://jonno.nz/posts/change-management/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/change-management/</guid>
      <description>On the personal version of change management — the long, weird middle bit between who you were and who you're becoming.</description>
      <content:encoded>
        <![CDATA[<p>There's a whole business discipline called change management. Frameworks,
certifications, consultancies, the lot. Every big company has someone running it
during a restructure or a tech migration. Nobody runs it for you when your life
turns over.</p>
<p>Which is strange, because the personal version is the harder problem — and right
now, more people are facing it than at any point in recent memory.</p>
<p>More than
<a href="https://www.cnbc.com/2026/04/24/20k-job-cuts-at-meta-microsoft-raise-concern-of-ai-labor-crisis-.html">92,000 tech workers have been laid off in 2026 alone</a>,
bringing the total close to 900,000 since 2020. Meta cut 8,000 jobs last week.
Microsoft offered buyouts to 7% of its US workforce — the first time in its
51-year history. Oracle has started cuts that could reach 30,000 by year end.
Closer to home, Xero, Sharesies, Spark, One NZ and Eroad have all run their own
rounds. AI is the headline reason, but the impact lands the same regardless of
the cause: hundreds of thousands of people closing a laptop and discovering
their working identity has just been deleted.</p>
<p>That's a lot of people being handed a forced version of personal change
management without ever signing up for the course.</p>
<p>The business framing has the right insight buried in it.
<a href="https://wmbridges.com/about/what-is-transition/">William Bridges</a> made a
distinction in the 90s that most people miss: change is external, transition is
internal. Change is the new org chart, the redundancy email, the merger.
Transition is what happens inside people's heads while all that is going on.
Change can happen overnight. Transition takes as long as it takes.</p>
<p>Personal change management is just transition without the org chart.</p>
<p>I've been through a few years of it now. Not one big event — more like a slow
stack of endings, some chosen, some not. Companies, relationships, versions of
myself I'd been building for a decade. The kind of stretch where you don't
really notice you're changing until you look up one day and the old you is gone.</p>
<p>That's the part nobody warns you about. Real change isn't transformation. It's a
controlled demolition followed by a slow rebuild, with a long, weird middle bit
where neither the old you nor the new you is really there.</p>
<h2>Something has to die</h2>
<p>The thing that goes is usually the organising self. Whatever the old you was
arranged around — a fear, a need for approval, a story about who you had to be,
an ambition that was really a wound. When that goes, the structure it was
holding up collapses. That's the death. It's real.</p>
<p>What survives is everything that wasn't load-bearing on the old arrangement.
Your humour, your curiosity, the way you actually see people, the things you
genuinely care about. Those don't die because they weren't propping anything up.
They were just you, underneath.</p>
<p>The disorienting part is feeling like a stranger to yourself and entirely
continuous, at the same time. Both are true. The continuous parts are
continuous. The organising self is gone. You're in between.</p>
<h2>The middle is the work</h2>
<p>Bridges calls this the neutral zone. The old reality has gone, the new one isn't
there yet. He says it's the hardest phase to manage, and most organisations rush
through it because it looks unproductive. People do the same thing to
themselves.</p>
<p>The temptation is to build a new identity fast, because the empty space is
uncomfortable. Don't. Whatever you grab in a hurry will be made of whatever was
lying around — which usually means the old patterns sneak back in wearing new
clothes. Workaholism becomes &quot;building my legacy&quot;. Approval-seeking becomes
&quot;being of service&quot;. Avoidance becomes &quot;protecting my peace&quot;. Same machine, new
paint.</p>
<p>The test is always: is this coming from fear or from truth? You'll know. The
body knows before the mind does. Pay attention to the part of you that goes
quiet around certain people, certain projects, certain decisions. That's the
signal.</p>
<h2>Fearlessness is a side effect</h2>
<p>You don't get to fearless by trying. You get there by going through enough
endings that the bluff stops working.</p>
<p>Fear runs on a specific con: <em>if this thing happens, you won't survive it</em>. Not
literally die — but the you that exists now won't continue. You'll be broken,
finished, unrecognisable. The con works as long as it's untested. Then the thing
happens, and you go through it, and on the other side you notice you're still
here. Different, scarred, but continuous. The fear was lying about its hand.</p>
<p>After that, fear can still show up — it doesn't leave — but it can't run the
same con. You've seen the card it was holding. Next time it says <em>you won't
survive this</em>, some quiet part of you knows: I already did.</p>
<p>That's not the absence of fear. It's knowing you can act from what's true even
with the fear in the room.</p>
<h2>What's on the other side is ordinary</h2>
<p>Here's the bit that surprised me. Once the demolition is done and the rebuild
starts, what comes back isn't impressive. It's just real. Less reactive. Less
noise. Less performance. You stop needing to be seen a particular way, partly
because you've watched a few of those selves die and you don't trust the next
one enough to stake everything on it.</p>
<p>The goal of change management — the personal kind — isn't to become someone
admirable. It's to become someone who's the same alone as in public. Someone who
does the next true thing without announcing it. Most of the depth of this stuff
lives in the texture of regular days. How you handle a boring Tuesday. Whether
you rest when you're tired or push through to prove something to nobody.</p>
<p>Business change management has all this in it, and most people read it as a
project manager's manual. It's also a personal one. Endings, neutral zone, new
beginnings. Same shape, different blast radius.</p>
<p>The seeds that grow through the demolition are the ones worth tending. The rest
sorts itself out.</p>
]]>
      </content:encoded>
      <pubDate>Sat, 25 Apr 2026 00:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/change-management.png"/>
    </item>
    <item>
      <title>Three Ways to Look at Time</title>
      <link>https://jonno.nz/posts/three-ways-to-look-at-time/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/three-ways-to-look-at-time/</guid>
      <description>
        ST-ResNet decomposes crime patterns into three temporal scales and models each one separately. Clever architecture, but does it actually help with only four years of NZ data?
      </description>
      <content:encoded>
        <![CDATA[<p>ST-ResNet's core insight is that not all history is created equal.</p>
<p>When you're predicting crime in Auckland next month, three different kinds of
past information matter. What happened in the last couple of months: the recent
trend. What happened at the same time last year: the seasonal pattern. And
what's been happening over the longer term: whether crime is generally rising or
falling in an area.</p>
<p>ConvLSTM treats all of this as one continuous sequence and hopes the network
figures out which parts matter. <a href="https://arxiv.org/abs/1610.00081">ST-ResNet</a>
takes a more opinionated approach. It separates these three temporal scales
explicitly and gives each one its own dedicated neural network branch.</p>
<p>The original paper by Zhang et al. was about predicting crowd flows in Beijing.
People move through cities in patterns that look a lot like crime patterns:
daily rhythms, weekly cycles, long-term trends. The architecture
<a href="https://www.nature.com/articles/s41598-025-24559-7">translates well to crime data</a>,
with some modifications.</p>
<h2>Closeness, period, trend</h2>
<p>The three branches each look at different slices of history:</p>
<p><strong>Closeness</strong> captures what's been happening recently. For our monthly data,
this means the last 3 months. If South Auckland has been trending upward over
the last quarter, the closeness branch sees that momentum.</p>
<p><strong>Period</strong> captures seasonal patterns. It looks at the same month in previous
years. So to predict January 2026, it pulls in January 2025 and January 2024.
The assumption is that crime has an annual rhythm, and the same month tends to
look similar year to year.</p>
<p><strong>Trend</strong> captures longer-term shifts. It uses quarterly averages from further
back: broad strokes of whether an area is seeing more or less crime over time.
This is the slowest-moving signal.</p>
<p>Each branch independently processes its temporal slice through a stack of
residual convolutional blocks, then a learned fusion layer combines the three
outputs:</p>
<pre><code>prediction = W_c · closeness + W_p · period + W_t · trend + bias
</code></pre>
<p>Where <code>W_c</code>, <code>W_p</code>, and <code>W_t</code> are learned weights that vary by grid cell. This
is a nice touch. It means the model can decide that the CBD's crime is mostly
driven by recent trends (closeness), while a residential suburb might be more
seasonal (period). Different areas get different temporal recipes.</p>
<h2>Residual blocks</h2>
<p>Each branch uses residual convolutional units, the building blocks that made
<a href="https://arxiv.org/abs/1512.03385">ResNet</a> so successful in image recognition.</p>
<p>The key idea: instead of learning the full output at each layer, the network
learns the <em>residual</em>, the difference between input and output. The identity
shortcut connection means gradients flow cleanly through the network during
training, which lets you stack more layers without the signal degrading.</p>
<pre><code>ResUnit(X) = ReLU(Conv(ReLU(Conv(X))) + X)
</code></pre>
<p>That <code>+ X</code> at the end is the skip connection. If the layer has nothing useful to
add, it can learn weights near zero and just pass the input through. This makes
deeper networks stable, which matters when you're trying to learn spatial
features at multiple scales.</p>
<p>For our grid, I use 4 residual units per branch. Each unit has two 3×3
convolutional layers with 32 filters. That's deep enough to capture spatial
relationships across several kilometres without being so deep that the model
overfits on 36 months of training data.</p>
<h2>The NZ-specific problem</h2>
<p>Here's where theory meets reality, and it gets a bit awkward.</p>
<p>ST-ResNet was designed for dense, high-frequency data. The Beijing crowd flow
paper used 30-minute intervals over months of data: thousands of timesteps. The
crime papers that report strong results typically use daily data over several
years.</p>
<p>We have 48 monthly timesteps. Total. The period branch (which looks at the same
month in previous years) has at most 3 data points per month (2022, 2023, 2024
to predict 2025/2026). The trend branch is working with quarterly averages from
a four-year window. It's not a lot of temporal data for an architecture that's
specifically designed to decompose temporal patterns.</p>
<p>I had a feeling this would be the bottleneck, and it was.</p>
<h2>Implementation</h2>
<pre><code>Closeness branch:
  Input: last 3 months (3 × 6 channels = 18 input channels)
  → 4 ResUnits (32 filters, 3×3 kernels)
  → Output: 32 channels

Period branch:
  Input: same month from 2 prior years (2 × 6 = 12 input channels)
  → 4 ResUnits (32 filters, 3×3 kernels)
  → Output: 32 channels

Trend branch:
  Input: 2 quarterly averages (2 × 6 = 12 input channels)
  → 4 ResUnits (32 filters, 3×3 kernels)
  → Output: 32 channels

Fusion:
  → Learned weighted sum across branches
  → Conv2d(32, 6, 1×1) → 6 crime type predictions
</code></pre>
<p>Total parameters: roughly 180k. Slightly smaller than the ConvLSTM, which is
fine. ST-ResNet's power is supposed to come from the temporal decomposition, not
from model size.</p>
<p>Training uses the same setup as ConvLSTM: Adam optimiser, learning rate 1e-4,
MSE loss on <code>log1p</code>-transformed values, early stopping with patience of 15
epochs. On CPU, each run takes about 35 minutes, a bit faster than ConvLSTM
since there's no sequential recurrence to deal with.</p>
<h2>Results</h2>
<table>
<thead>
<tr>
<th>Crime Type</th>
<th>Hist. Avg MAE</th>
<th>ConvLSTM MAE</th>
<th>ST-ResNet MAE</th>
</tr>
</thead>
<tbody>
<tr>
<td>Theft</td>
<td>1.28</td>
<td>1.14</td>
<td>1.18</td>
</tr>
<tr>
<td>Burglary</td>
<td>0.35</td>
<td>0.32</td>
<td>0.33</td>
</tr>
<tr>
<td>Assault</td>
<td>0.20</td>
<td>0.19</td>
<td>0.19</td>
</tr>
<tr>
<td>Robbery</td>
<td>0.04</td>
<td>0.04</td>
<td>0.04</td>
</tr>
<tr>
<td>Sexual</td>
<td>0.03</td>
<td>0.03</td>
<td>0.03</td>
</tr>
<tr>
<td>Harm</td>
<td>0.01</td>
<td>0.01</td>
<td>0.01</td>
</tr>
<tr>
<td><strong>All types</strong></td>
<td><strong>0.39</strong></td>
<td><strong>0.35</strong></td>
<td><strong>0.36</strong></td>
</tr>
</tbody>
</table>
<p>ST-ResNet beats the historical average but doesn't quite match ConvLSTM. The
aggregate MAE of 0.36 is a 7.7% improvement over the baseline, compared to
ConvLSTM's 10.3%.</p>
<p>That's not a terrible result, but it's not what I was hoping for.</p>
<h2>Why ConvLSTM wins here</h2>
<p>When I dug into the learned fusion weights, the story became clear. The
closeness branch dominates. It gets 60–70% of the weight across most grid cells.
The period branch gets 20–25%, and the trend branch barely contributes at
10–15%.</p>
<p>The model is basically saying: &quot;Recent months matter most, seasonal patterns
help a bit, and long-term trends are mostly noise.&quot; That's not a failure of the
architecture. It's a fair assessment of what's in the data.</p>
<p>With only 2–3 examples of each calendar month, the period branch can't reliably
learn seasonal patterns. It's overfitting to individual years rather than
extracting a stable seasonal signal. ConvLSTM handles this better because it
processes the full sequence and implicitly learns seasonality from the
continuous flow of months, without needing to explicitly align calendar periods.</p>
<p>The trend branch suffers even more. Quarterly averages over a four-year window
don't give it much to work with. In the original crowd flow papers with years of
half-hourly data, the trend branch captures genuine long-term shifts in
population movement. Here, it's essentially learning a constant.</p>
<h2>Where ST-ResNet does shine</h2>
<p>Despite losing on aggregate, ST-ResNet has one clear advantage: it's better at
predicting seasonal transitions.</p>
<p>The months where crime shifts gears (the spring uptick in September/October and
the February dip) ST-ResNet handles more gracefully than ConvLSTM. The period
branch, sparse as its data is, does capture enough of the annual rhythm to
anticipate these transitions a bit earlier.</p>
<p>ConvLSTM tends to lag these transitions by about a month. It needs to &quot;see&quot; the
uptick starting before it predicts continuation. ST-ResNet, by explicitly
looking at last year's same month, can anticipate the shift before it fully
materialises in the recent sequence.</p>
<p>For an operational forecasting tool, that one-month lead time on seasonal
transitions could be valuable. But in our test set metrics, it's a small
advantage that doesn't overcome ST-ResNet's overall weaker performance on
month-to-month dynamics.</p>
<h2>Head to head</h2>
<table>
<thead>
<tr>
<th>Metric</th>
<th>Historical Avg</th>
<th>ConvLSTM</th>
<th>ST-ResNet</th>
</tr>
</thead>
<tbody>
<tr>
<td>Overall MAE</td>
<td>0.39</td>
<td>0.35</td>
<td>0.36</td>
</tr>
<tr>
<td>Theft MAE</td>
<td>1.28</td>
<td>1.14</td>
<td>1.18</td>
</tr>
<tr>
<td>Training time (CPU)</td>
<td>N/A</td>
<td>~40 min</td>
<td>~35 min</td>
</tr>
<tr>
<td>Parameters</td>
<td>0</td>
<td>~200k</td>
<td>~180k</td>
</tr>
<tr>
<td>Seasonal transitions</td>
<td>Poor</td>
<td>Lagging</td>
<td>Better</td>
</tr>
<tr>
<td>Spatial dynamics</td>
<td>None</td>
<td>Good</td>
<td>Good</td>
</tr>
</tbody>
</table>
<p>ConvLSTM is the better model for this specific dataset. Not by a lot. We're
talking about small differences on already-small error values. But consistently
better on the main crime types that have enough signal to matter.</p>
<p>Neither model is a revelation. A 7–10% improvement over &quot;just use the historical
average&quot; is real but modest. Deep learning's strengths (learning complex
nonlinear dynamics from huge datasets) are somewhat wasted on 48 monthly
timesteps over a relatively low-crime city.</p>
<p>If I had daily data instead of monthly, or ten years instead of four, I'd expect
ST-ResNet to close the gap or pull ahead. Its architecture is fundamentally
sound. The temporal decomposition is a genuinely good idea. It's just starved of
the data it needs to shine.</p>
<p>Both models meaningfully beat the baselines. Both learn spatial patterns that
simple averages can't capture. And both are honest about the sparse crime types:
they predict near-zero and move on, which is the right call.</p>
<p>Next up: we'll take these predictions and build something you can actually look
at. A 3D interactive dashboard where you can watch crime patterns evolve across
Auckland over time. The modelling was the hard bit. Making it visual is the fun
bit.</p>
]]>
      </content:encoded>
      <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/three-ways-to-look-at-time.png"/>
    </item>
    <item>
      <title>What an hour of your attention is worth</title>
      <link>https://jonno.nz/posts/what-an-hour-of-your-attention-is-worth/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/what-an-hour-of-your-attention-is-worth/</guid>
      <description>
        You pay Big Tech about $1,000 a year in attention. Here's how to read the meter — and why building your own is suddenly cheaper than opting out.
      </description>
      <content:encoded>
        <![CDATA[<p>I stood up a working social network for eight mates last weekend. Profile pages,
a shared feed, a photo wall, a jukebox bolted onto a spare domain. It took me a
Saturday, about forty bucks in Claude credits, and exactly zero
product-market-fit meetings.</p>
<p>The same weekend, Meta earned about six bucks off me. Google made ten. LinkedIn,
YouTube, TikTok, X — all quietly billing in the background, none of them sending
a receipt. If you add them all up for the average American, the annual total is
north of $1,000. You just never see it, because no money changes hands and no
invoice arrives.</p>
<p>The clever thing about &quot;free&quot; on the internet isn't that the trade doesn't
exist. It's that it's been designed so you can't see it. No money moves. No
invoice lands. No app shows you the meter ticking as you scroll. The exchange is
real — your attention and your data in, Instagram and Google and LinkedIn out —
but by the time the numbers get tallied, they live in a quarterly earnings
report you'll never read. So the trade feels weightless.</p>
<p>It isn't. You just can't see the price tag.</p>
<p>The strange thing is the price tag has been public the whole time. Every
platform listed on a stock exchange tells you, four times a year, exactly what
you're worth to them. You've just never been shown how to read it — and until
recently, the only practical alternative to reading it was &quot;live in a cabin.&quot;
That part has changed, and it's the part almost nobody is talking about.</p>
<p>The invisibility isn't an accident either. If Meta had to send you a cheque
every month for the money they made off you, you'd treat the relationship very
differently. You'd notice when the amount went up. You'd notice that the
teenager version of the payment looks nothing like the adult version. You'd
wonder why the Auckland cheque was ten times the Jakarta one for the exact same
hour of scrolling. The whole edifice of &quot;free&quot; rests on keeping the accounting
one-sided — they measure you in basis points to three decimal places, you
experience the trade as a vague sense of having lost your afternoon.</p>
<h2>The price tag they're legally required to print</h2>
<p>The number you want is called ARPU — average revenue per user. Every public
platform reports it, because investors demand it. The maths is blunt: take the
company's annual revenue, divide by monthly active users. What comes out is what
the platform earns off the average human who shows up, per year.</p>
<p>For Meta last year the global figure was about $52 per user. For YouTube's
ad-supported side, around $24. For
<a href="https://www.linkedin.com/posts/dshapero_earnings-update-to-close-out-our-2025-fiscal-activity-7361399679256858624-vVg7">LinkedIn it's $15 averaged across all 1.2B members</a>,
but much higher once you strip out the dormant accounts.</p>
<p>These aren't guesses from a watchdog group. They're from the companies
themselves, in the part of the earnings release where the whole purpose is to
convince shareholders each user is worth more than last quarter. The incentive
is to talk the number up, not down.</p>
<p>Whatever ARPU says, the reality on the ground probably isn't lower. If anything,
it's a floor.</p>
<h2>Your annual bill, itemised</h2>
<p>Rough figures, from the companies' own filings:</p>
<ul>
<li><strong>Meta</strong>: ~$52/yr global, ~$320 in the US</li>
<li><strong>Google (all products)</strong>: ~$100/yr globally, ~$500 US —
<a href="https://abc.xyz/investor/">$400B in revenue</a> across ~4B users spanning
Search, Android, YouTube, Cloud and Workspace combined</li>
<li><strong>YouTube ads alone</strong>: ~$24/yr global, ~$80 US</li>
<li><strong>LinkedIn</strong>: $15/yr averaged across all 1.2B members, but ~$57/yr across the
<a href="https://www.linkedin.com/posts/dshapero_earnings-update-to-close-out-our-2025-fiscal-activity-7361399679256858624-vVg7">310M monthly active ones</a></li>
<li><strong>TikTok</strong>: ~$16 global, ~$70 US — doubled in two years</li>
<li><strong>Snapchat, Reddit, Pinterest, X</strong>: all in the $10–30/user/yr range</li>
</ul>
<p>The geographic skew is the part most people miss. Meta's figure in the US is
roughly ten times what it is in Asia-Pacific. Europe sits in the middle at about
$92. Same product, same features, same algorithm — different rate card, because
ad buyers pay more to reach wealthier audiences. You are literally worth more in
Auckland than you are in Jakarta, and your feed is tuned accordingly.</p>
<p><img src="https://jonno.nz/img/posts/arpu-meta-by-region.svg" alt="Meta ARPU by region — US $320, Europe $92, global $52, Asia-Pacific $32"></p>
<p>The same skew shows up across every ad-funded platform. The US rate card is the
one the rest of the world gets compared to:</p>
<p><img src="https://jonno.nz/img/posts/arpu-us-vs-global.svg" alt="US vs global ARPU comparison — Google $500/$100, Meta $320/$52, YouTube $80/$24, TikTok $70/$16, LinkedIn $57/$15"></p>
<p>Marketplaces don't fit ARPU cleanly, but the extraction is still there if you
look for it. Uber and Lyft take around 20% of each fare. Airbnb combines host
and guest fees for about 14–16%. DoorDash and Uber Eats take closer to 25%.
Shopify's card take is 2.9% plus 30 cents per transaction. Different mechanism,
same game — a percentage of every transaction, quietly skimmed, never itemised.</p>
<h2>The meter, in dollars per hour</h2>
<p>ARPU is annual. Attention isn't spent in years though — it's spent in hours, in
the little windows between other things. So the honest conversion is to divide.</p>
<p>The average US Meta user burns about 200 hours a year across Facebook and
Instagram. $320 ÷ 200 = roughly $1.60 per hour of your attention. YouTube works
out to about $0.27/hour. TikTok $0.22. Snapchat cheaper still. Do the same sum
on global averages and Meta drops to around 26 cents an hour, YouTube to 8.</p>
<p>Those rates are only what the platform <em>earns</em> this year, mind. They aren't what
your data is ultimately <em>worth</em>. Everything you click and hover and pause on
feeds ad targeting across the wider web, plus — now — AI training corpora. ARPU
is the rent. The equity is bigger, and the equity compounds.</p>
<p>The AI-training bit is genuinely new and worth pausing on. For fifteen years the
data you generated on these platforms powered one thing: better ad targeting on
those same platforms. It was a closed loop. You scrolled, they learned, they
sold the targeting back to advertisers, the advertisers bought your attention
again. Bounded. Weird, but bounded.</p>
<p>That loop isn't bounded anymore. Your posts and comments and DMs are now
training data for models that will be sold, resold, and embedded into every
piece of software you touch for the next decade. The $320 Meta earned off you in
the US last year is a rounding error next to what the underlying corpus is worth
to the next generation of AI products. ARPU doesn't capture any of that. It's
literally last quarter's ad rent, with none of the capital gains on the asset.</p>
<p>Even the rent, laid out per hour, makes one thing obvious: you can see exactly
why every platform is obsessed with &quot;time spent&quot; as a north-star metric. If one
extra hour a week on Facebook is worth ~$83 a year per US user, multiplied
across three billion users, the maths for why the feed never stops scrolling is
not mysterious. The feed is a meter. Keeping it running is the business. Every
&quot;new feature&quot; that shows up in your settings — reels, shorts, a nudge to open
the app on your commute — is a hand on that meter.</p>
<p>Once you see it that way, a lot of product decisions stop looking like product
decisions.</p>
<h2>Run your own numbers</h2>
<p>The point of making the numbers this concrete is that you can plug in your own
usage and see what you personally throw into the machine each year. Drag the
sliders for how much time goes into each platform and watch the ledger tally up.
Rates are global averages.</p>
<section class="ledger" id="ledger" aria-label="The Ledger calculator"><style>
.ledger{--lb:#141f2e;--lb2:#1a2637;--li:#e4e9ee;--ld:#bfc8d2;--lm:#6a7d92;--la:#d4a853;--lbd:rgba(255,255,255,.08);--lbs:rgba(255,255,255,.16);max-width:38rem;background:var(--lb);border:1px solid var(--lbs);border-radius:.35rem;padding:1.15rem 1.25rem 1rem;margin:2rem auto;color:var(--ld);font-family:text,'Roboto',-apple-system,sans-serif;font-size:.88rem;line-height:1.5;text-align:left}
.ledger *,.ledger *::before,.ledger *::after{box-sizing:border-box}
.ledger h3,.ledger h4{font-family:inherit;margin:0;padding:0;color:inherit;font-weight:inherit;font-size:inherit;letter-spacing:0;line-height:1.2}
.ledger h3::before,.ledger h4::before{content:none}
.ledger p{margin:0;padding:0}
.ledger input{font:inherit;color:inherit;background:transparent;border:none;outline:none}
.l-head{display:flex;justify-content:space-between;align-items:baseline;gap:.75rem;padding-bottom:.75rem;margin-bottom:.9rem;border-bottom:1px solid var(--lbs)}
.l-title{font-family:serif,'Fraunces',Georgia,serif;font-weight:400;font-size:1.15rem;letter-spacing:-.015em;color:var(--li)}
.l-tag{font-family:code,'JetBrains Mono',monospace;font-size:.58rem;letter-spacing:.16em;text-transform:uppercase;color:var(--lm)}
.l-total{display:flex;align-items:baseline;justify-content:space-between;gap:1rem;padding:.85rem 1rem;background:#0e1623;border:1px solid var(--lbd);border-radius:.25rem;margin-bottom:1.1rem}
.l-total-amt{font-family:serif,'Fraunces',Georgia,serif;font-weight:400;font-size:1.9rem;line-height:1;color:var(--la);letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.l-total-lab{font-family:code,'JetBrains Mono',monospace;font-size:.58rem;letter-spacing:.18em;text-transform:uppercase;color:var(--lm);text-align:right}
.l-sec{margin-top:1.15rem}
.l-sec:first-of-type{margin-top:0}
.l-row{padding:.9rem 0;border-bottom:1px dashed var(--lbd)}
.l-row:last-child{border-bottom:none}
.l-row-top{display:flex;align-items:baseline;justify-content:space-between;gap:.75rem;margin-bottom:.55rem}
.l-row-n{color:var(--li);font-size:.94rem;line-height:1.25}
.l-row-meta{font-family:code,'JetBrains Mono',monospace;font-size:.6rem;letter-spacing:.05em;color:var(--lm);margin-top:.15rem;display:block}
.l-row-a{font-family:serif,'Fraunces',Georgia,serif;font-size:1.05rem;color:var(--la);text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap;flex-shrink:0}
.l-row-a.z{color:var(--lm)}
.l-row-c{display:flex;align-items:center;gap:.9rem}
.l-row-v{font-family:code,'JetBrains Mono',monospace;font-size:.66rem;letter-spacing:.05em;color:var(--ld);white-space:nowrap;min-width:6rem;text-align:right}
.l-sl{-webkit-appearance:none;appearance:none;flex:1;min-width:0;height:32px;background:transparent;cursor:pointer;padding:0;margin:0;touch-action:manipulation}
.l-sl::-webkit-slider-runnable-track{height:3px;background:var(--lbd);border-radius:2px}
.l-sl::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:22px;height:22px;border-radius:50%;background:var(--la);border:3px solid var(--lb);margin-top:-10px;box-shadow:0 0 0 1px var(--la),0 2px 6px rgba(0,0,0,.3);cursor:grab}
.l-sl:active::-webkit-slider-thumb{cursor:grabbing;box-shadow:0 0 0 1px var(--la),0 0 0 6px rgba(212,168,83,.22)}
.l-sl::-moz-range-track{height:3px;background:var(--lbd);border-radius:2px}
.l-sl::-moz-range-thumb{width:22px;height:22px;border-radius:50%;background:var(--la);border:3px solid var(--lb);box-shadow:0 0 0 1px var(--la)}
.l-sl:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px var(--la),0 0 0 6px rgba(212,168,83,.28)}
.l-notes{margin-top:1.25rem;padding-top:.9rem;border-top:1px solid var(--lbs)}
.l-notes h5{font-family:code,'JetBrains Mono',monospace;font-size:.56rem;letter-spacing:.18em;text-transform:uppercase;color:var(--lm);margin:0 0 .55rem;font-weight:400}
.l-notes p{font-family:serif,'Fraunces',Georgia,serif;font-size:.85rem;line-height:1.55;color:var(--ld);margin:0 0 .4rem;text-wrap:pretty}
.l-notes p:last-child{margin-bottom:0}
.l-notes strong{color:var(--li);font-weight:500}
@media (max-width:560px){
.ledger{padding:1rem .9rem;margin:1.5rem auto;font-size:.92rem}
.l-total{flex-direction:column;align-items:flex-start;gap:.25rem;padding:.75rem .9rem}
.l-total-lab{text-align:left}
.l-row{padding:1rem 0}
.l-row-c{gap:.75rem}
.l-row-v{min-width:5rem;font-size:.7rem}
.l-sl::-webkit-slider-thumb{width:26px;height:26px;margin-top:-12px}
.l-sl::-moz-range-thumb{width:26px;height:26px}
}
</style><div class="l-head"><h3 class="l-title">The Ledger</h3><span class="l-tag">global averages · per year</span></div><div class="l-total"><div class="l-total-amt" id="l-total">$0</div><div class="l-total-lab">extracted per year</div></div><div class="l-sec"><div id="l-attn-rows"></div></div><div class="l-notes"><h5>Notes on the method</h5><p><strong>ARPU is rent, not equity.</strong> What a platform earns this year isn't what the underlying data is worth across the wider web and AI training corpora.</p><p><strong>Averages hide heavy users.</strong> Freemium smears free and paying users into one figure. If you're all-in, you're worth more than average.</p><p><strong>Multi-product companies cheat the top line.</strong> Google's per-user number isn't all Search — it's Search plus Android plus YouTube plus Cloud.</p></div><script>(function(){var R={meta:.26,youtube:.08,tiktok:.05,x:.07,reddit:.12,snap:.07,pin:.15,li:.15,gq:.04};
var ATTN=[{id:'meta',name:'Meta (FB / IG / WhatsApp)',rate:'meta',unit:'hrs/day',mult:365,max:6,step:.25},{id:'youtube',name:'YouTube (ad-supported)',rate:'youtube',unit:'hrs/day',mult:365,max:6,step:.25},{id:'tiktok',name:'TikTok',rate:'tiktok',unit:'hrs/day',mult:365,max:6,step:.25},{id:'x',name:'X (Twitter)',rate:'x',unit:'hrs/day',mult:365,max:4,step:.25},{id:'reddit',name:'Reddit',rate:'reddit',unit:'hrs/day',mult:365,max:4,step:.25},{id:'snap',name:'Snapchat',rate:'snap',unit:'hrs/day',mult:365,max:4,step:.25},{id:'pin',name:'Pinterest',rate:'pin',unit:'hrs/day',mult:365,max:4,step:.25},{id:'li',name:'LinkedIn (free)',rate:'li',unit:'hrs/day',mult:365,max:2,step:.1},{id:'gq',name:'Google Search',rate:'gq',unit:'searches/day',mult:365,max:100,step:1}];
var state={attn:{}};
function fmt(n){n=Math.round(n);if(n===0)return'$0';if(n>=1000)return'$'+n.toLocaleString();return'$'+n;}
function recalc(){var tot=0;
ATTN.forEach(function(s){var v=state.attn[s.id]||0;var amt=v*R[s.rate]*s.mult;tot+=amt;var el=document.getElementById('l-a-'+s.id);if(el){el.textContent=fmt(amt);el.classList.toggle('z',amt<1);}var vl=document.getElementById('l-v-'+s.id);if(vl)vl.textContent=v+' '+s.unit;});
document.getElementById('l-total').textContent=fmt(tot);}
function renderAttn(){document.getElementById('l-attn-rows').innerHTML=ATTN.map(function(s){var meta='$'+R[s.rate].toFixed(2)+(s.unit==='searches/day'?' / search':' / hr');return '<div class="l-row"><div class="l-row-top"><div><span class="l-row-n">'+s.name+'</span><span class="l-row-meta">'+meta+'</span></div><span class="l-row-a z" id="l-a-'+s.id+'">$0</span></div><div class="l-row-c"><input type="range" class="l-sl" data-cat="attn" data-svc="'+s.id+'" min="0" max="'+s.max+'" step="'+s.step+'" value="0" aria-label="'+s.name+' '+s.unit+'"><span class="l-row-v" id="l-v-'+s.id+'">0 '+s.unit+'</span></div></div>';}).join('');}
function renderAll(){renderAttn();recalc();}
var root=document.getElementById('ledger');
root.addEventListener('input',function(e){var t=e.target;if(t.classList.contains('l-sl')){state[t.dataset.cat][t.dataset.svc]=parseFloat(t.value)||0;recalc();}});
renderAll();})();</script></section>
<p>The rates come from the earnings-report maths above — global ARPU divided by
average annual hours on the platform.</p>
<h2>The weekend social network</h2>
<p>Once the number has somewhere to sit, it's much harder to ignore.</p>
<p>Most people look at a total over $1,000/yr and go quiet for a second. Not
because any one platform is egregious — on a per-hour basis they really aren't —
but because the aggregate is real, and it's been invisible until now. That's the
first useful thing the exercise does. It makes a choice possible.</p>
<p>The obvious next move is to look at alternatives. Signal instead of WhatsApp.
Kagi or Brave Search instead of Google. Paid Spotify instead of ad-supported
Spotify. Bluesky or Mastodon instead of X. Fastmail instead of Gmail. None are
perfect, and some cost actual money — but once you can price what you're
currently &quot;not paying&quot;, the paid alternative often looks less expensive than it
did five minutes ago. Fastmail at
$5/month stops being a luxury when the honest comparison is &quot;$60/yr vs being the
product for an ad network that paid $500 for me last year.&quot;</p>
<p>That's the defensive move. It's the one everyone talks about, every time one of
these pieces gets written. You switch to the more honest vendor, you feel
slightly better, and the fundamental shape of the market doesn't move.</p>
<p>The more interesting move is what's happened on the <em>build</em> side, and it's the
part almost nobody has internalised yet.</p>
<p>Standing up a social app used to take a small team months. You needed a backend
engineer, a frontend engineer, a designer, probably a DevOps person, and a spare
three months. That was the real moat — not the network effects, not the
algorithm, but the sheer human-hours required to put a working thing on the
internet. That's why the only viable answer for twenty years was to build
something big enough to run ads against. Small social didn't exist because small
social couldn't pay the salaries.</p>
<p>With Claude Code, Cursor, v0, and Lovable, that equation has quietly inverted. A
profile page, a shared feed, a wall for photos, maybe a jukebox, a chat wall — a
MySpace-sized thing for you and a dozen friends, on a domain you own, with none
of it feeding anyone's ad platform — is a weekend. I know because I just did it.
Not as some Silicon Valley startup trying to replace Facebook. As a Saturday
project for eight mates, on a domain that cost twelve bucks, running on a box
that costs ten a month.</p>
<p>The bill of materials is embarrassingly short. A boring Postgres. A boring
Next.js app. Auth via magic link. Storage for photos. An LLM for the fiddly bits
nobody wants to write from scratch. All of it plumbed together in an afternoon
of prompting, an evening of cleanup, and a Sunday of adding the jukebox because
my mate Hamish wouldn't stop asking.</p>
<p>It is not good software. It is good <em>enough</em> software for eight humans who know
each other.</p>
<p>That qualifier is the whole thing. Facebook has to be good software at planet
scale because Facebook is selling ad impressions at planet scale. A group of
eight doesn't need p99 latency and a content moderation policy. A group of eight
needs a place to put photos from the weekend where the photos don't end up
training someone's image model in twelve months' time. Those are very different
engineering problems, and the second one is much, much easier than the first.</p>
<p>A lot of things genuinely don't work on the weekend version. There's no
recommendation algorithm. There's no real search. The feed is
reverse-chronological and that's it. When someone posts something at 3am nobody
sees it until the morning. There's no cleverness about which photos get surfaced
or which memories get resurrected. If you go on holiday for two weeks, you come
back to a feed that's exactly what your eight mates posted, in the order they
posted it.</p>
<p>That sounds like a limitation until you notice the thing it is not doing is
optimising for your engagement. Reverse-chronological across eight friends is
not a meter. It's a wall. You check it, you see what's there, you leave. There's
no reason for the software to try to keep you around because there's nobody
paying the software to keep you around. That inversion — from meter to wall — is
the entire point.</p>
<p>The thing that would have been a VC round in 2015 is now a side quest you finish
before the roast is in the oven. The tools genuinely got that much better in the
last eighteen months. We just haven't updated our intuitions yet about what that
means.</p>
<p>What it means, specifically, is that the ad-supported social network is no
longer the only technically viable answer. For twenty years it was. That was the
constraint the whole &quot;free web&quot; was built around. The constraint is gone, and
nobody has sent the memo.</p>
<p>The cheapest social network in 2026 is the one you and six mates build on a
Saturday afternoon. It doesn't scale. It doesn't need to. It costs less than a
month of Netflix, produces no ad revenue for anyone, and feeds no one's training
set. You own the domain. You own the data. You own the product decisions — which
in practice means there are no product decisions, because nobody is trying to
squeeze another hour out of anyone's week.</p>
<p>None of this replaces the platforms, to be clear. You still need Gmail for the
recruiter, LinkedIn for the job hunt, YouTube for the tutorial, WhatsApp for the
group chat your family refuses to leave. The ad-supported internet isn't going
anywhere and I'm not pretending it is. What's changed is that it's no longer the
only game in town. For the circle of people you actually care about — the eight
mates, the cousins, the old uni flat — you don't have to hand them over to the
ad machine anymore. You can build them a room of their own, and the tools to
build that room have become trivial in a way we haven't fully absorbed yet.</p>
<p>The meter's been running your whole life. You just got the tools to turn it off.</p>
]]>
      </content:encoded>
      <pubDate>Tue, 21 Apr 2026 12:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/what-an-hour-of-your-attention-is-worth.png"/>
    </item>
    <item>
      <title>Teaching a Neural Network to Watch Crime Like Video</title>
      <link>https://jonno.nz/posts/teaching-a-neural-network-to-watch-crime-like-video/</link>
      <guid isPermaLink="false">https://jonno.nz/posts/teaching-a-neural-network-to-watch-crime-like-video/</guid>
      <description>
        ConvLSTM was built for weather radar. Turns out predicting crime on a grid is basically the same problem. Here's how it works and what it learned.
      </description>
      <content:encoded>
        <![CDATA[<p>ConvLSTM was invented to predict rainstorms.</p>
<p>Specifically,
<a href="https://arxiv.org/abs/1506.04214">Shi et al. at HKUST, working with the Hong Kong Observatory</a>,
needed to forecast radar echo maps: 2D grids of rainfall intensity that evolve
over time. They had sequences of spatial images and wanted to predict the next
frames. Sound familiar?</p>
<p>That's exactly what we built in Part 3. Crime on a 500m grid, one frame per
month, six channels for crime types. The Auckland crime tensor is structurally
identical to a weather radar sequence. Same dimensionality, same prediction
task, just a very different domain.</p>
<h2>Why not regular LSTM?</h2>
<p>Standard LSTM networks are fantastic at learning sequences. They're the backbone
of a lot of time-series forecasting. But they have a fundamental problem with
spatial data: they need flat vectors as input.</p>
<p>To feed our 77×59 grid into a regular LSTM, we'd have to flatten it into a
vector of 4,543 values per crime type. That's 27,258 values per timestep across
all six channels. The network would process this as a sequence of big flat
vectors, with no concept that cell (10, 5) is <em>next to</em> cell (10, 6).</p>
<p>All the spatial structure (the fact that crime clusters, that hotspots have
neighbourhoods, that the CBD is a contiguous area) gets thrown away. The model
would have to rediscover spatial relationships from scratch, purely from
correlations in the flattened vector. With only 36 training months, that's not
happening.</p>
<h2>The convolutional trick</h2>
<p>ConvLSTM's insight is elegant. Take the standard LSTM equations (the input gate,
forget gate, output gate, cell state update) and replace every matrix
multiplication with a convolution operation.</p>
<p>In a regular LSTM:</p>
<pre><code>input_gate = sigmoid(W_xi * x_t + W_hi * h_{t-1} + b_i)
</code></pre>
<p>In ConvLSTM:</p>
<pre><code>input_gate = sigmoid(W_xi ∗ X_t + W_hi ∗ H_{t-1} + b_i)
</code></pre>
<p>That <code>∗</code> is a convolution instead of a matrix multiply. <code>X_t</code> is the full 2D
grid at time <code>t</code>, and <code>H_{t-1}</code> is the previous hidden state, also a 2D grid.
The convolution kernel slides across the spatial dimensions, so each cell's gate
values depend on its local neighbourhood.</p>
<p>This means the network naturally learns that a spike in cell (10, 5) might
affect predictions for cell (10, 6). Spatial proximity is baked into the
architecture. It doesn't need to learn it from data.</p>
<p>The kernel size controls how much spatial context each cell sees. A 3×3 kernel
means each cell looks at its immediate 8 neighbours. Stack multiple ConvLSTM
layers and the effective receptive field grows. Deeper layers can capture
relationships between cells that are several kilometres apart.</p>
<h2>Architecture choices</h2>
<p>Here's what I settled on after a fair bit of experimentation (which on CPU means
&quot;a lot of patient waiting&quot;):</p>
<pre><code>Input: (batch, 6, 6, 77, 59), 6 months, 6 crime types, 77×59 grid
  ↓
ConvLSTM2d(in=6, hidden=32, kernel=3×3, padding=1)
  ↓
BatchNorm2d
  ↓
ConvLSTM2d(in=32, hidden=32, kernel=3×3, padding=1)
  ↓
BatchNorm2d
  ↓
Conv2d(in=32, out=6, kernel=1×1), project to 6 crime type channels
  ↓
Output: (batch, 6, 77, 59), next month prediction
</code></pre>
<p>Two ConvLSTM layers with 32 hidden channels each. The 3×3 kernel gives each cell
a neighbourhood view, and stacking two layers means the effective receptive
field covers about 1–1.5 km. Enough to capture the spatial extent of most crime
hotspots.</p>
<p>Why only 32 hidden channels? This is where the CPU constraint actually helps. A
bigger model would be tempting with a GPU, but on a Ryzen 5 we need to keep it
tight. 32 channels gives us about 200k trainable parameters: small enough to
train in under an hour, large enough to learn meaningful spatial-temporal
patterns.</p>
<p>The 1×1 convolution at the end is a channel projection. It maps the 32 learned
features back to 6 crime type predictions.</p>
<h2>Sequence length: six months</h2>
<p>The lookback window is six months. The model sees January through June and
predicts July. Then February through July to predict August. And so on.</p>
<p>Six months captures one half of the seasonal cycle, which turned out to be the
sweet spot. Shorter sequences (3 months) missed seasonal context. Longer
sequences (12 months) didn't improve results, likely because the model doesn't
have enough data to learn year-long dependencies with only 36 training months
total.</p>
<p>The training set gives us 30 sequences (months 1–6 predict 7, months 2–7 predict
8, all the way to months 30–35 predict 36). That's not a lot. Every sequence
counts.</p>
<h2>Training details</h2>
<pre><code class="language-python">optimiser = Adam(lr=1e-4)
loss = MSE  # on log1p-transformed values
batch_size = 4  # small because sequences are large
epochs = 150 with early stopping (patience=15)
</code></pre>
<p>The <code>log1p</code> transformation from Part 3 is critical here. Raw crime counts range
from 0 to 50+. After <code>log1p</code>, the range compresses to 0–4. Without this, the
loss function would be dominated by the handful of high-count CBD cells, and the
model would essentially ignore the rest of the grid.</p>
<p>Training on CPU takes about 40 minutes per run. Not fast, but manageable. I
could typically fit in 3–4 experimental runs per evening, which meant progress
was slow but steady. Each run I'd tweak one thing (kernel size, hidden channels,
learning rate) and compare validation MAE.</p>
<p>Early stopping triggers around epoch 80–100 in most runs. The model converges
relatively quickly, which makes sense given the small dataset and architecture.</p>
<h2>Results</h2>
<p>So how does ConvLSTM stack up against the baselines from Part 5?</p>
<table>
<thead>
<tr>
<th>Crime Type</th>
<th>Hist. Avg MAE</th>
<th>ConvLSTM MAE</th>
<th>Improvement</th>
</tr>
</thead>
<tbody>
<tr>
<td>Theft</td>
<td>1.28</td>
<td>1.14</td>
<td>10.9%</td>
</tr>
<tr>
<td>Burglary</td>
<td>0.35</td>
<td>0.32</td>
<td>8.6%</td>
</tr>
<tr>
<td>Assault</td>
<td>0.20</td>
<td>0.19</td>
<td>5.0%</td>
</tr>
<tr>
<td>Robbery</td>
<td>0.04</td>
<td>0.04</td>
<td>2.5%</td>
</tr>
<tr>
<td>Sexual</td>
<td>0.03</td>
<td>0.03</td>
<td>~0%</td>
</tr>
<tr>
<td>Harm</td>
<td>0.01</td>
<td>0.01</td>
<td>~0%</td>
</tr>
<tr>
<td><strong>All types</strong></td>
<td><strong>0.39</strong></td>
<td><strong>0.35</strong></td>
<td><strong>10.3%</strong></td>
</tr>
</tbody>
</table>
<p>A 10% improvement on the aggregate MAE. Not earth-shattering, but real.</p>
<p>Theft gets the biggest lift because there's the most signal to work with. The
model genuinely learns spatial dynamics that the historical average can't
capture. When a cluster of cells in South Auckland trends upward over several
months, ConvLSTM picks up on that momentum and adjusts its predictions
accordingly.</p>
<p>Burglary sees a decent improvement too, likely driven by the spatial correlation
with theft that we spotted in the EDA.</p>
<p>For the sparse crime types (robbery, sexual offences, harm) ConvLSTM basically
learns to predict near-zero, same as the baseline. There simply isn't enough
signal at 500m monthly resolution for these types. The model is honest about
what it doesn't know, which I actually respect.</p>
<h2>Where it shines and where it doesn't</h2>
<p>The improvement isn't uniform across the grid. ConvLSTM does best in the
transition zones: cells on the edges of established hotspots where crime counts
fluctuate month to month. It learns that these boundary cells tend to follow the
trend of their neighbours, which is exactly the kind of spatial-temporal pattern
it was designed to capture.</p>
<p>In the stable hotspot cores (the CBD, Manukau) the model performs about the same
as the baseline. Those cells are consistently high, and the historical average
already captures that well.</p>
<p>Where it properly struggles is with sudden spikes in normally quiet areas. A
cell that's been near-zero for months and then gets 5 thefts in one month: the
model doesn't see that coming. Neither does any other model, to be fair. Those
events are closer to random noise than learnable signal.</p>
<h2>Putting it in perspective</h2>
<p>A 10% MAE improvement is meaningful but modest.
<a href="https://arxiv.org/abs/2509.20913">Recent ConvLSTM crime prediction research</a>
reports larger gains, but those models typically work with much more data:
years of daily records across cities with higher crime density. Our setup is
tougher. Monthly
resolution limits temporal signal, Auckland is relatively low-crime by global
standards, and we only have four years.</p>
<p>The model is also running on CPU with a deliberately small architecture. A
bigger model on a GPU might squeeze out more performance. But the point of this
project was always to see how far you can push it with modest resources, and a
10% beat over simple baselines feels like a real result.</p>
<p>The question now is whether ST-ResNet's different approach to temporal modelling
can do better. ConvLSTM processes time as one continuous sequence. ST-ResNet
breaks it into three separate temporal scales: closeness, period, and trend.
With a seasonal dataset like crime, that decomposition might be exactly what's
needed.</p>
]]>
      </content:encoded>
      <pubDate>Thu, 16 Apr 2026 00:00:00 GMT</pubDate>
      <meta property="og:image" content="https://jonno.nz/og/teaching-a-neural-network-to-watch-crime-like-video.png"/>
    </item>
  </channel>
</rss>