# Daniel May — full content

> Every published post on danielmay.co.uk, concatenated as a single markdown file. Generated from the source collection at build time.

For the high-level index see [llms.txt](https://danielmay.co.uk/llms.txt). For the rendered site see [danielmay.co.uk](https://danielmay.co.uk/).

---

# Redistributing Billions in the Neopets Economy

> Published 2026-07-03 at https://danielmay.co.uk/posts/redistributing-billions-neopets/
> A six-month reverse-engineering project on a Neopets shop, the duplication exploit that followed, and what I've come to understand about why I kept going.

<span id="introduction" class="anchor-top"></span>

> Over six months in 2014, a network of over 200 Neopets players ran software I made to record 48,000 snapshots of a single shop's inventory. We saw total item flow valued at over 6.5B Neopoints, built an autobuyer that generated roughly 167M NP in profit, before the project abruptly ended amid server instability due to a corporate transition. When the site returned, backend issues enabled game-breaking exploits: mass item duplication and "free NP". We abused those glitches to duplicate the game's most coveted items, donated billions to the <i>Money Tree</i>, and then released a script for the community to use. At the time, I thought of it as redistribution, but reflecting a decade later, I think it was just an attempt to be seen.

<nav class="toc-rail" aria-label="Table of contents">
  <div class="toc-post-title" aria-hidden="true">Redistributing Billions in the Neopets Economy</div>
  <ol>
    <li><a href="#introduction"><span class="toc-txt">Introduction</span></a></li>
    <li><a href="#detection-and-enforcement"><span class="toc-txt">Detection and Enforcement</span></a></li>
    <li><a href="#the-attic"><span class="toc-txt">The Attic</span></a></li>
    <li><a href="#restock-analysis"><span class="toc-txt">Restock Analysis</span></a></li>
    <li><a href="#purchasing"><span class="toc-txt">Purchasing</span></a></li>
    <li><a href="#an-abrupt-end"><span class="toc-txt">An Abrupt End</span></a></li>
    <li><a href="#exploit-discovery"><span class="toc-txt">Exploit Discovery</span></a></li>
    <li><a href="#robin-hood-or-so-i-thought"><span class="toc-txt">Robin Hood, or so I thought</span></a></li>
    <li><a href="#further-than-items"><span class="toc-txt">Further Than Items</span></a></li>
    <li><a href="#impact-and-reflections"><span class="toc-txt">Impact and Reflections</span></a></li>
    <li>
      <a href="#appendix"><span class="toc-txt">Appendix</span></a>
      <ol>
        <li><a href="#releasing-the-dataset"><span class="toc-txt">Releasing the dataset</span></a></li>
        <li><a href="#acknowledgements"><span class="toc-txt">Acknowledgements</span></a></li>
      </ol>
    </li>
  </ol>
</nav>

I started playing Neopets in 2003, and it was cutting edge for its time, competing with other online timesinks like [Newgrounds.com](https://www.newgrounds.com/) and early social networks. For me at least, it offered an eccentric mix of [Tamagotchi](https://en.wikipedia.org/wiki/Tamagotchi), [Miniclip](https://www.miniclip.com/) and [Drug Wars](https://en.wikipedia.org/wiki/Drug_Wars_(video_game)). The Neopets Team <i>("TNT")</i> made something really special.

Over time, though, I grew tired of the repetition. The giant omelette only gave away one piece per day, and other activities were "daily" gated. Searching for items on the <i>shop wizard</i> would lock you out if you spent too much time looking for a good deal. Unaware that these limitations were likely caused by bots, I asked the question *why can't this computer just play the game for me?*, and that led me to look online for cheats. Eventually, I built cheats of my own - not so much surfing the web as drifting between communities.

I wound up enveloped in a cheating/hacking community, researching and developing programs that automated Neopets. I learned how to engineer software by building autobuyers that spoofed browsers and tried to beat the competition, and then posted those programs on forums and MSN Messenger<sup class="fnref" id="fnref-1"><a href="#fn-1">1</a></sup>. Seeing something I produced delight other people was too addictive to ignore, even as it brushed against an ethical line I didn't yet understand.

And from that passion, I built a career. By 2014 I was 22, four years into building foreign exchange trading platforms in London, traveling to <i>tier one</i> banks to advise <i>Directors</i> on which technologies deserved their attention, surrounded by *real* engineers with *real* degrees. I was good at the job, but I still braced, most days, for someone to work out that I didn't belong there. I'd decided the Neopets stuff belonged to an earlier version of me that I'd grown out of, but still I ended up back there. It was a place where I already belonged, where the only thing that counted was whether you could take a system apart, and I could. I wanted a problem of my own again: something unsolved, that would give way to the kind of attention that I was good at.

## Detection and Enforcement

Neopets' bot detection was always rudimentary, but TNT never held back on enforcement: once bot activity was detected, accounts were <i>frozen</i> (banned) immediately. Spoofing a browser was table stakes, and if you were a particularly naughty individual, they'd block by IP. This landscape created an arms race and incentivized widespread proxy use. My take is that they likely had a user base too large and activity too great to justify investing in analytics to enable better anti-cheat.

Unfortunately, this landscape *did* harm the player experience. Searching player-owned shops too quickly would lock you out until the next hour rolled around, and refreshing too frequently at an NPC shop risked receiving a similar multi-hour temp ban. Surprisingly early on, a custom captcha was added to the item purchase flow that asked the user to click a Neopet overlaid on a background:

<figure>
  <div class="image-row pair">
    <img src="/posts/redistributing-billions-neopets/captcha-1.webp" alt="Neopets click-the-pet captcha: a Neopet overlaid on a busy background, the pet rendered as the darkest cluster of pixels" />
    <img src="/posts/redistributing-billions-neopets/captcha-2.webp" alt="A second variant of the click-the-pet captcha with a different background, the pet again the darkest cluster of pixels" />
  </div>
  <figcaption>The now-defunct click-the-Neopet captcha. The pet is usually the darkest cluster of pixels.</figcaption>
</figure>

This wasn't much of a setback for the experienced programmers in the community. By choosing to layer a darker image on top of a lighter one, a simple loop finding the *darkest* 10x10 pixel cluster successfully identified the pet, most of the time:

```c
int darkest = 255, petX = 0, petY = 0;

for (int x = 0; x < w; x += 10)
    for (int y = 0; y < h; y += 10) {
        int b = brightness(x, y); // HSV value, 0-255
        if (b < darkest) { darkest = b; petX = x; petY = y; }
    }

click(petX, petY);
```

I imagine they might have been a bit disappointed to see a solve like this. Whichever dev shipped it did leave a little easter egg for us though: calls to `GET /captcha_show.phtml` were forced to supply a captcha identifier in a parameter ironically named `_x_pwned` - nice. This relic was [retired](https://www.jellyneo.net/?go=comments&post=15973) in June 2026, sadly.

## The Attic

<img class="medium" src="/posts/redistributing-billions-neopets/almost-abandoned-attic.webp" alt="The Almost Abandoned Attic shop banner from Neopets" />

Over time, TNT continued to shape virtual economy features around bot protection. Some NPC shops, like the Igloo Garage Sale, sold rarer and higher value items, but were gated behind account age. I'd written a small autobuyer for that before, but there was something new to look into: <i>The Almost Abandoned Attic.</i>

The Attic had custom rules: it stocked the rarest items that wouldn't appear elsewhere, and only accounts over three years old were able to access it. My assumption is that TNT likely considered this a *"win-win"* - rewarding long-standing players with a special store and attacking the profitability of bots by increasing post-detection recovery time.

I was a little less interested in an autobuyer this time, though, and instead the custom rules stuck out to me. There were websites out there already like [JellyNeo's Item Database](https://items.jellyneo.net/) that captured the *market price* of items in the economy. But there had been no visible analysis into any of the algorithms behind *how* shops on Neopets restocked, instead simple automation that took advantage of *when* the good items showed up. Maybe it was random, but when considering the different behavior, maybe it wasn't. I thought: what if some kind of networked autobuyer could report aggregated restock telemetry for analysis?

- *How often does the shop restock? Is it regular, or is there a derivable pattern?*
  - *Can we lower our risk of detection by checking stock around a given time?*
- *Which items appear most frequently?*
  - *Can we selectively choose which restocks to target, knowing they may be more profitable?*
- *What impact does current stock have on restocks?*
  - *Does emptying the shop by buying unprofitable items trigger a different kind of behavior?*

I discussed the idea with friends in our forum's dedicated IRC channel, and quickly formed a collaborator partnership with an engineer in Germany. Together, we figured out a client/server architecture, with them handling a small analytics capture PHP backend writing to a MySQL database, and me handling the client, responsible for probing the shop, shipping the data and, eventually, buying the items. We both agreed that while buying items in the shop was the eventual goal, simply learning about the mechanics of the game was the driving curiosity at the start.

With our shared credibility in the community as badged *"programmers"*, we thought we might be able to convince other members to help. Like some kind of grey area community data collection project.

## Restock Analysis

The client started as a browser extension but evolved to a Windows client written in C#. PHP's flexibility on the backend made it easy for us to spin up a little website that reported on the latest restocks and incentivized contributors with a leaderboard.

We saw an initial spike of adoption from eager helpers in late March, so this let me dig straight into modelling the data in Excel. The first few hours of data helped confirm a community theory around a regular restock interval: restocks appeared to cluster around a seven-minute interval. Looking at all of the data now, it's even clearer:

The shop didn't necessarily restock *every* `7m2.75s`, but after a restock occurred, it would only ever potentially happen on a subsequent `7m2.75s` metronomic tick. This was useful, though narrow, as we were hoping to derive better insights earlier.

It did allow us to design around refresh-frequency-based ban detection. If we could narrow down the *possible* minutes in an hour in which the shop *could* restock, we would be able to dramatically reduce the amount of our detectable activity, likely to below that of even a regular player. That same logic could also inform our purchasing algorithm to catch items faster than others, and we didn't really worry about traffic patterns or fingerprinting on such a small scale.

In May, activity dropped, and it became worryingly clear that we weren't going to get many more insights out of the data we had beyond the relationship to `7m2.75s`. Despite capturing item value flow of over 3B NP, and seeing restocks for coveted items like <i>Bony Grarrl Club</i> (130M) and <i>Sticky Snowflake Stamp</i> (150M), these facts only frustrated members as they sat out of reach. We knew that purchasing had to come soon if we wanted to learn more, so in June, we started work.

## Purchasing

Considering the `7m2.75s` interval pattern, with the client-server architecture we'd built, and the adoption we'd seen, we had several options. In theory, we could have stored the most recently seen restock on the server and coordinated many clients across geographies to all refresh `7m2.75s` afterwards, maybe even forcing them to hammer the same item. But this project was meant to be a curiosity-driven data collection effort, not a domain-specific botnet designed to squeeze *all* of the fun out of the game. Many contributors didn't even really play the game anymore.

The resulting algorithm was conservative by cheating standards. We waited a fixed six-minute period after detecting a restock successfully, and never shot out requests without a healthy randomly sampled delay. Despite that, our network purchased more than 2,000 items in just six weeks, generating ~167M NP gross profit. I suppose you could say we operated some kind of Neopets hedge fund.

## An Abrupt End

In mid-September 2014, Neopets servers began encountering major issues associated with a corporate transition to Jumpstart, who had purchased the company from Viacom earlier that year. At exactly 11:12 <i>Neopian Standard Time</i> on September 17 2014, the Attic restocked for the final time, eerily visible from our user probes:

And just like that, the analysis that hundreds of us had worked on for half a year was unceremoniously fragged. Over six months, we saw a million items flow through that shop, worth an estimated 6.5B neopoints.

## Exploit Discovery

Soon after the site returned, many features were broken entirely, and IRC began to murmur about instability. There were reports of odd activity happening here and there - game state wasn't being persisted as players expected it should. In one channel, someone said something about losing an item that they had put into their <i>Safety Deposit Box</i>. The engineers in the community quietly wondered if there was a way to take advantage. My curiosity grew.

I started some crude in-browser investigation with random items on an account: holding down the space bar on the "submit" button, hoping to possibly see more than one item appear on the other end. It took a little time, but eventually, there it was:

<img src="/posts/redistributing-billions-neopets/item-dupe-race.webp" alt="Inventory showing the same item credited more than once after rapidly resubmitting, evidence of a race condition" style="max-width: 504px" />

I was shocked - could simply spamming requests trigger a race condition<sup class="fnref" id="fnref-2"><a href="#fn-2">2</a></sup> that credits the item more than once? It's that simple? That immediately challenged years of assumptions that rudimentary attack surfaces were well-covered.

At the time, I justified releasing a script taking advantage of the problem:

1. Privately duplicating items for personal gain risked causing more economic harm and *hiding bugs*, so:
2. Publishing the method for everyone to take advantage of was *equality among cheaters,* and
3. Giving away coveted (duplicated) items for free is me being <i>Robin Hood</i>

Additionally, I didn't stand to gain financially from the project, and with other less ethical players victimizing players by selling NP and stolen accounts, this was surely much better than that. But truly, calling it <i>redistribution</i> warped the narrative and pushed an already slipping boundary even further. It helped abstract away the damage and give me a seemingly heroic central role.

But I wasn't mature enough to see that at the time, so I shared a script with friends in the scene. I didn't stop there.

<figure>
  <img src="/posts/redistributing-billions-neopets/glitch-suite-demo.webp" alt="Animated demo of the glitch suite script duplicating items automatically" />
  <figcaption>The script I distributed to other cheaters.</figcaption>
</figure>

## _Robin Hood_, or so I thought

The rarest and most valuable item in Neopets was the <i>Super Attack Pea</i>, a multi-use Battledome weapon worth 1 billion Neopoints. It was the strongest battle item in the game, capable of inflicting the most damage, and fewer than ten were estimated to be in circulation. User shops could only price items to a maximum of `99,999 NP`, and in order to duplicate an item worth 1B NP, we needed access to at least one. I reached out to the wealthy cheaters I knew in the community. Soon enough, I found someone, their single pea (with several hundred of millions NP collateral, because this was *serious*) and then I subsequently stocked a thousand dupes in my shop, effectively free:

<img src="/posts/redistributing-billions-neopets/super-attack-pea-dupes.webp" alt="A user shop stocked with a thousand duplicated Super Attack Peas, each priced at the maximum" />

Absurdly I found users with a plushie version of the pea on the <i>Trading Post</i> and offered them a free 1B upgrade:

<img class="medium" src="/posts/redistributing-billions-neopets/pea-plushie-upgrade-offer.png" alt="A Trading Post exchange offering a real Super Attack Pea in return for a plushie version, a free 1B upgrade" />

I would then go on to donate thousands of high-value items to the <i>Money Tree</i>, a place usually only reserved for giving poor players junk you don't want. At this point, I wasn't really thinking about the economic impact. I remember thinking *"Players suddenly have access to items they'd never had the chance to play with before, that's good right? The game was getting on, past its peak, what's the worst that could happen?"*

## Further Than Items

More investigation quickly showed that it was possible to hammer bank deposits/withdrawals to generate Neopoints directly. Eventually, I managed to overflow on-hand NP:

<figure>
  <img class="medium" src="/posts/redistributing-billions-neopets/np-overflow.webp" alt="A Neopets account balance showing 2,147,483,647 Neopoints, the signed 32-bit integer maximum" />
  <figcaption><i>2,147,483,647 is the maximum value for a 32-bit signed integer.</i></figcaption>
</figure>

Everything suddenly felt pointless. All of that excitement blinded me to something simple: rules aren't the obstacle to the game, they define the game.

Soon after, the site was taken offline. I don't know whether the bank exploit contributed to that decision or whether it resulted from the broader instability, but I remember being glad then, and I'm glad now. The damage was now done, and despite widespread expectations from the community, there was no rollback. The market now had to move forward, and TNT tried their best to clean up the damage.

## Impact and Reflections

The economic impact was vast and was well covered by [Alex Irpan](https://www.alexirpan.com/2018/11/10/neopets-economy.html) as well as on [Reddit](https://www.reddit.com/r/neopets/comments/8wyqpl/neopets_history_the_2014_dday_dupe_day/). By 2018, the price of a <i>Super Attack Pea</i> had fallen by 75% to 250M, but an [article from 2024](https://www.thegamer.com/neopets-best-battledome-items/) says the price has now started to climb back up to near 450M. The <i>Attic</i> never returned<sup class="fnref" id="fnref-3"><a href="#fn-3">3</a></sup>, and several other game features were turned off entirely for months or years after the core races were fixed.

I have a complicated relationship with the way I learned software engineering. Reverse engineering Neopets taught me to observe systems, test assumptions and build tools that people wanted to use. It also taught me that capability and permission are two different things. Information [may want to be free](https://en.wikipedia.org/wiki/Information_wants_to_be_free), but that doesn't mean it's my job to liberate it.

I think it's important to reflect on the slow growth of the project. While the duplication bug was the most impactful, it was just one revelation in a situation where the line had already been moving for months. I justified each step, and soon enough what I had first framed as a benign data-collection project had gradually become an abuse system that risked harming people and businesses I cared about. Was that the notoriety I really wanted? No.

It's been twelve years since, and I've had a lot of time to reflect on *why* I pushed those boundaries. I told myself I just wanted to understand: the captcha, the restock, the race condition - and it sounded honest. But there was never innocence underneath that curiosity. I had an audience, and understanding the system was how I held their attention. Each thing I figured out was something to be *seen* as figuring out, or represented social capital to be won, or a chance to be the *only one* who understood. I was thinking about notoriety and never stopped to think about the scale or impact my actions could have on a game I'd enjoyed for so many years. Now I see that *wanting to understand* and *wanting to be seen* were two faces of the same desire: to display competence during insecurity. I was just hiding behind the more respectable of the two. I wasn't <i>Robin Hood</i>. I was someone who found an audience that rewarded me for being clever, and who kept being clever at the expense of a game, and the people who played it. And unfortunately, I kept at it long past the point when the clever move would have been to stop.

<section class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn-1">I imagine the modern equivalent is a kid out there working on a sneaker autobuyer and maybe advertising it to their friends on their Instagram story. Since I got started, online economies and opportunities for profit have both changed and stayed the same in a funny way. <a href="#fnref-1" class="fn-back" aria-label="Back to reference 1">↩</a></li>
    <li id="fn-2">A flaw of this kind can occur when a state-changing operation isn't atomic or correctly idempotent, explained well on the <a href="https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use">"Time-of-check to time-of-use" wiki page</a>. No post-mortem was ever provided, so we may never get an insight into what was happening behind the scenes. <a href="#fnref-2" class="fn-back" aria-label="Back to reference 2">↩</a></li>
    <li id="fn-3">Neopets did re-use the shopkeeper character for a Mobile thing in 2022, nine years later. <a href="#fnref-3" class="fn-back" aria-label="Back to reference 3">↩</a></li>
  </ol>
</section>

---

## Appendix

### Releasing the dataset

I've preserved the original historical restock dataset and I'm reviewing whether a safely deidentified version can be released on Hugging Face. Perhaps someone else can determine if there ever was an underlying pattern to the restock algorithm, or maybe one day someone can wire it into their Neopets emulator?

### Acknowledgements

Users running my script were told about the risks of getting their accounts frozen for duplicating items, but there might have been collateral damage to innocent accounts simply for receiving or handling duplicated items. If that was you, I'm sorry.

Thanks to my friends for reading early versions of this post and providing feedback.

The <i>Attic Restock Analysis</i> project wasn't possible without my anonymous collaborator, who I'm proud to say has now enjoyed a successful engineering career! I'm also grateful to the contributors who gave their data to the project, especially those who assisted before purchasing was enabled.

I also want to say thank you to the hundreds of staff at TNT who put their passion and energy into Neopets for so many years. I'm sorry for making your job harder.

---

# Claudity - a thinking partner for Claude Code

> Published 2026-06-11 at https://danielmay.co.uk/posts/claudity/
> A Claude Code plugin that asks the questions an architect would ask, ported from Microsoft's Clarity Agent.

I've spent the past few days building [Claudity](https://github.com/danielrmay/claudity), a Claude Code plugin that acts as a thinking partner while you build: it asks the questions an architect would ask, pushes back on the ones you haven't thought about, and records the answers and decisions as markdown in your repo.

On Tuesday I attended the Microsoft AEGIS AI Safety & Security summit, where I was introduced to [Clarity Agent](https://github.com/microsoft/clarity-agent), Microsoft's take on exactly this problem. It sits beside you while you're instructing a model to build something and makes sure the architecture, the security posture, and the unglamorous engineering practices get considered before the code exists. A lot of what was shared landed for me, and my primary harness is Claude Code, so the obvious way to find out whether the ideas hold up was to get them running there. Claudity is that port.

Mechanically it's six skills, six specialist "thinker" subagents that brainstorm failure modes in parallel (security, human factors, adversarial misuse, and so on), and a small dependency-free MCP server. The port stays honest through automation rather than discipline. A nightly job watches Microsoft's repo and opens an issue whenever a release touches the vendored files, and a CI audit verifies that every line I've changed from upstream traces back to a written porting rule.

<img src="/posts/claudity/hero.gif" alt="A simulated Claude Code session: the user asks for a tool that pulls prod data into dev, Claudity asks what breaks today and whether prod rows contain customer PII, then records anonymization requirements to .clarity-protocol/goal/requirements.md" />

The GIF above shows the shape of it. Ask for a tool that pulls production data into dev, and before anything gets built the first question back is what's actually breaking today, and the second is whether those rows contain customer PII. That's what a good senior engineer raises in design review, and what nobody raises at 11pm in a terminal. The answers don't vanish into chat scrollback either. They land in a `.clarity-protocol/` directory as plain markdown, versioned and reviewable in the same PR as the code they shaped.

The code is at [github.com/danielrmay/claudity](https://github.com/danielrmay/claudity), MIT licensed like its upstream counterpart. If you live in Claude Code, trying it is two commands, `/plugin marketplace add danielrmay/claudity` and then `/plugin install claudity@claudity`. Point it at the next thing you build and see what it asks you.

There's more to say. I have opinions about the instructions themselves, where the prompts are strong and where they fight Claude Code's own instincts, and after a few weeks of real miles I'll know whether it earns its seat in the loop. Those are their own posts.

---

# Foreign Trivia - free wordle inspired language trivia

> Published 2026-05-29 at https://danielmay.co.uk/posts/foreign-five/
> A daily language game. Five questions a day, each one in a different language, and a result you can share.

I made a small daily trivia game called Foreign Five. It gives you five questions a day, each in a different language. You can tap any word to translate it, but if you do, it shows up in your shared result.

<img class="medium" src="/posts/foreign-five/question.png" alt="Question one of a Foreign Five puzzle, themed 'Roof of the World', asking in Spanish which mountain range Aconcagua is in. Each answer has a small translate button." />

A few weeks ago I wrote about [the first version of this](/posts/foreign-trivia/), Foreign Trivia, where you pick one language and answer ten questions in it. Foreign Five is the same idea refined into a daily puzzle.

I've always liked languages. Growing up in Europe and then working in software with people from lots of different countries gave me a real appreciation for how languages feel, how they're structured, and how much culture sits inside them.

My own language learning never really kept up with that interest though. I can often recognize patterns, familiar words, sentence shapes, bits of meaning, but I'm not fluent in much.

That's the part Foreign Five is built around. It mixes trivia with language intuition. You're not just translating a sentence, you're using context, structure, general knowledge, and whatever you can recognize to work out the answer.

The Wordle-style daily format came later, but I think it fits well. One puzzle a day, a theme, a shareable result. No account, no ads.

<video autoplay loop muted playsinline preload="auto" width="300" height="652">
  <source src="/posts/foreign-five/result.mp4" type="video/mp4" />
  <source src="/posts/foreign-five/result.webm" type="video/webm" />
  A finished Foreign Five: five out of five, confetti, and a shareable result grid that marks which answers were unaided, translated, or missed.
</video>

It's here: [trivia.lmny.dev](https://trivia.lmny.dev).

I'd love feedback if you try it, especially on whether the guessing and translation mechanic feels fun.

---

# Cheap agents, alumni shirts, and Elias Thorne

> Published 2026-05-12 at https://danielmay.co.uk/posts/cheap-agents-alumni-shirts-and-elias-thorne/
> On agent-coded cold outreach, the alumni t-shirt scam, the Elias Thorne convergence, and what gets visible when no one does the work.

<aside class="press-callout">
  <div class="press-logos">
    <a class="lg-404media" href="https://www.404media.co/elias-thorne-chatbots-llms-chatgpt-lighthouse-keeper-story/" aria-label="404 Media coverage" target="_blank" rel="noopener"><svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 118.89 37.86"> <path d="M25.6,.73h13.52l-5,22.98h4.68v6.55h-6.12l-1.49,6.86h-7.03l1.49-6.86H9.47v-9.67L25.6,.73ZM15.38,22.78v.94h11.71L31.35,4.16h-.96L15.38,22.78Z"></path> <path d="M44.92,14.87c1.1-5.13,2.94-8.89,5.51-11.28s6.11-3.59,10.62-3.59c1.88,0,3.55,.29,5,.86,1.46,.57,2.68,1.37,3.67,2.39s1.75,2.23,2.26,3.61c.51,1.39,.77,2.88,.77,4.47,0,.59-.03,1.18-.08,1.77-.05,.59-.15,1.18-.29,1.77l-1.76,8.11c-1.1,5.13-2.94,8.89-5.51,11.28-2.57,2.39-6.11,3.59-10.62,3.59-1.88,0-3.55-.29-5-.86s-2.68-1.37-3.67-2.39c-.99-1.02-1.75-2.22-2.26-3.59-.51-1.37-.77-2.87-.77-4.5,0-.59,.03-1.18,.08-1.77,.05-.59,.15-1.18,.29-1.77l1.76-8.11Zm10.22,16.74c1.21,0,2.27-.22,3.19-.65,.92-.43,1.73-1.01,2.42-1.74,.69-.73,1.26-1.59,1.7-2.6,.44-1,.79-2.08,1.04-3.22l1.97-8.94c.11-.55,.19-1.03,.24-1.43,.05-.4,.08-.79,.08-1.17,0-1.77-.47-3.15-1.41-4.13-.94-.99-2.26-1.48-3.97-1.48-1.21,0-2.27,.22-3.19,.65-.92,.43-1.73,1.01-2.42,1.74-.69,.73-1.26,1.59-1.7,2.6-.44,1.01-.79,2.08-1.04,3.22l-1.97,8.94c-.21,.94-.32,1.8-.32,2.6,0,1.77,.47,3.15,1.41,4.13s2.26,1.48,3.97,1.48Z"></path> <path d="M90.76,.73h13.52l-5,22.98h4.68v6.55h-6.12l-1.49,6.86h-7.03l1.49-6.86h-16.18v-9.67L90.76,.73Zm-10.22,22.05v.94h11.71l4.26-19.55h-.96l-15.01,18.62Z"></path> <path d="M1.07,31.42c-.58,.04-1.07-.41-1.07-.99v-.47c0-.23,.07-.46,.22-.64l1.07-1.31c.16-.19,.24-.43,.22-.68l-.27-5.39c0-.5,.36-.93,.86-1,9.83-1.36,104.88-12.17,115.62-13.39,.55-.06,1.03,.33,1.09,.88l.07,.68c.04,.42-.18,.82-.56,1.01l-1.51,.73c-.15,.07-.26,.21-.29,.37l-.8,4.31c-.08,.42-.41,.75-.84,.81C105.82,17.59,12.17,30.56,1.07,31.42Z"></path> <path d="M35.59,16.96c-2.48,.29-4.89,.57-7.21,.84l-1.29,5.92H15.38v-.94l3.08-3.82c-3.2,.38-6.05,.71-8.48,1.01l-.51,.63v9.67h1.88c7.04-.87,16.75-2.14,27.45-3.56v-2.99h-4.68l1.47-6.76Zm59.62-6.84l-2.01,9.22c2.58-.35,5.01-.69,7.24-1l1.97-9.04c-2.24,.25-4.65,.53-7.2,.82Zm-20.58,10.48v1.28c2.54-.35,5.04-.69,7.46-1.02l8.21-10.18c-2.69,.31-5.5,.63-8.39,.96l-7.27,8.96Zm-31.46,2.39c-.14,.59-.24,1.18-.29,1.77-.04,.47-.06,.95-.06,1.42,2.31-.31,4.65-.62,7.01-.94,.05-.58,.12-1.18,.27-1.83l1.83-8.33c-2.43,.28-4.84,.56-7.22,.83l-1.53,7.08Zm22.3-8.53l-1.97,8.93c2.44-.33,4.86-.66,7.26-.99l1.63-7.53c.14-.59,.24-1.18,.29-1.77,.01-.14,0-.28,.02-.42-2.33,.27-4.69,.54-7.06,.81-.05,.29-.1,.6-.17,.96Z"></path> </svg></a>
    <a class="lg-guardian" href="https://www.theguardian.com/commentisfree/2026/jun/17/elias-thorne-ai-generated-stories" aria-label="The Guardian coverage" target="_blank" rel="noopener"><svg fill="currentColor" role="img" viewBox="0 7.7 24 8.6" xmlns="http://www.w3.org/2000/svg"><path d="m9.272 8.129-1.16.207V8.4l.32.127v3.577l-.28.16v.103h1.52v-.103l-.264-.145V9.961a.64.64 0 0 1 .4-.129c.24 0 .368.112.368.408v1.871l-.272.145v.111h1.512v-.111l-.271-.145v-1.863c0-.536-.225-.816-.729-.816a1.54 1.54 0 0 0-.992.361l-.031.031V8.13zm-4.512.566-.047 1.121H4.8l.744-1.007h.318v3.23l-.416.217v.111H7.36v-.119l-.416-.217V8.81h.32L8 9.816h.111l-.047-1.12zm8.033.785c-.888 0-1.434.569-1.434 1.489 0 .936.505 1.478 1.48 1.478.553 0 .952-.303 1.104-.52v-.11c-.2.103-.414.24-.718.24-.632 0-.896-.457-.928-1.05h1.752v-.023c0-1.088-.432-1.504-1.256-1.504m-.033.12c.256 0 .385.488.385 1.224l-.866.047c.002-.715.218-1.271.48-1.271m2.577 1.896-1.217.217v.08l.328.111v.928c-.088-.048-.271-.088-.511-.088-.84 0-1.4.52-1.4 1.6 0 1.024.48 1.52 1.128 1.52a1.07 1.07 0 0 0 .8-.329h.032v.32l.096.016 1.201-.158v-.106l-.338-.119v-3.976zm1.367 0a.513.513 0 0 0-.527.512.51.51 0 0 0 .527.511c.288 0 .537-.215.537-.511 0-.288-.249-.512-.537-.512m-14.52.184C1.04 11.68 0 12.233 0 13.793c0 1.456.84 2.07 2.145 2.07a4 4 0 0 0 1.359-.23v-1.465l.367-.2v-.136H2.072v.12l.375.216v1.48a.8.8 0 0 1-.336.055c-.568 0-.806-.624-.814-2.008-.008-1.16.302-1.863.91-1.863.208 0 .314.016.434.072l.664 1.047h.111l-.023-1.088c-.248-.104-.721-.183-1.21-.183m20.714 1.033c-.36.016-.714.157-.954.375h-.039v-.367h-.12l-1.2.214v.122l.336.111v2.36l-.287.16v.113h1.566v-.114l-.271-.152v-2.264a.65.65 0 0 1 .408-.127c.248 0 .385.12.385.424v1.951l-.281.16v.114H24v-.113l-.28-.16v-2.008c0-.56-.231-.8-.751-.8zm-5.772.04-1.236.224v.096l.336.127v2.32l-.282.16v.114h1.56v-.113l-.27-.153v-2.76zM5 12.76l-1.2.217v.087l.329.143v1.8c0 .52.264.856.84.856a1.34 1.34 0 0 0 .902-.375h.033v.36l.12.007 1.193-.152v-.08l-.32-.135v-2.72l-.13-.008-1.199.217v.087l.328.143v2.129a.49.49 0 0 1-.392.183c-.232 0-.367-.087-.367-.375v-2.369zm3.545 0a3.5 3.5 0 0 0-1.098.191v.809h.088l.656-.88.13-.017c.367 0 .495.201.495.793v.393l-.007-.01-.649.129c-.624.12-.928.393-.928.897s.32.798.8.798c.375 0 .617-.127.777-.351h.03c.089.216.273.343.673.343.248 0 .472-.063.584-.127v-.08l-.287-.08v-1.92c0-.664-.464-.888-1.264-.888m2.879 0-.008.008-1.2.224v.08l.329.096v2.352l-.281.16v.113h1.56v-.113l-.271-.153v-1.734c.176-.08.382-.129.654-.129.088 0 .192 0 .248.016v-.897c-.032-.016-.094-.015-.15-.015-.344 0-.632.24-.76.88h-.026v-.88zm7.488 0c-.464 0-.816.103-1.096.191v.809h.088l.657-.88.127-.017c.36 0 .496.201.496.793v.393h-.008l-.64.127c-.633.12-.936.384-.936.888s.32.8.8.8c.376 0 .616-.128.776-.352h.023c.088.216.272.343.672.343.256 0 .482-.063.594-.127v-.08l-.29-.08v-1.92c0-.664-.463-.888-1.263-.888m-4.8.144c.2-.008.255.056.335.104v2.377a.42.42 0 0 1-.279.103c-.432.016-.584-.425-.584-1.209 0-.872.207-1.359.527-1.375m-5.295 1.287v1.186c-.056.064-.128.103-.248.103-.216 0-.377-.129-.377-.609 0-.44.104-.64.424-.664zm10.368 0v1.186h-.008c-.048.064-.12.103-.24.103-.225 0-.383-.129-.383-.609 0-.44.111-.64.431-.664z"/></svg></a>
    <a class="lg-gizmodo" href="https://gizmodo.com/why-do-chatbots-keep-telling-stories-about-someone-named-elias-thorne-2000770629" aria-label="Gizmodo coverage" target="_blank" rel="noopener"><svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 186 35.8"><path d="M45.08,12.75h-6a.47.47,0,0,0-.47.47V34.79a.47.47,0,0,0,.47.47h6a.47.47,0,0,0,.47-.47V13.22A.47.47,0,0,0,45.08,12.75Zm22.8,6.55V12.75H49.76a.47.47,0,0,0-.47.47h0v5.6a.47.47,0,0,0,.47.48H58.7l-10,9.41v6.55H67.77a.47.47,0,0,0,.47-.47v-5.6a.48.48,0,0,0-.47-.48H57.85Zm31.18-7.08a8.81,8.81,0,0,0-7.45,3.93,7.24,7.24,0,0,0-6.75-3.93,7.84,7.84,0,0,0-6.39,3.07V13.22a.47.47,0,0,0-.47-.47H72a.47.47,0,0,0-.48.47V34.79a.47.47,0,0,0,.48.47h6a.47.47,0,0,0,.47-.47h0V23.31c0-3.11,1.39-4.46,3.93-4.46s3.36,1.43,3.36,3.89V34.79a.47.47,0,0,0,.47.47h6a.48.48,0,0,0,.48-.47V23.31c0-3.11,1.39-4.46,3.93-4.46S100,20.28,100,22.74V34.79a.47.47,0,0,0,.48.47h6a.47.47,0,0,0,.47-.47V21.18c0-5.49-3.07-9-7.9-9Zm22.17,0A11.61,11.61,0,0,0,109.44,24,11.79,11.79,0,0,0,133,24,11.61,11.61,0,0,0,121.23,12.25Zm4.89,12a4.89,4.89,0,1,1-4.71-5.07,4.89,4.89,0,0,1,4.71,5.07Zm48.09-12A11.61,11.61,0,0,0,162.42,24,11.79,11.79,0,1,0,186,24,11.61,11.61,0,0,0,174.21,12.25Zm4.83,12a4.83,4.83,0,1,1-4.65-5,4.83,4.83,0,0,1,4.65,5Zm-26.86-8.92a7.8,7.8,0,0,0-6.42-3.07c-5.61,0-10.12,4.79-10.12,11.79S140.07,35.8,145.8,35.8a7.94,7.94,0,0,0,6.38-2.95v1.94a.47.47,0,0,0,.48.47h6a.47.47,0,0,0,.47-.47V4.22a.48.48,0,0,0-.47-.48h-6a.48.48,0,0,0-.48.48h0V15.29Zm0,9a4.89,4.89,0,1,1-9.77-.14,1.7,1.7,0,0,1,0-.22,4.89,4.89,0,0,1,9.76.36ZM41.58,10.35a3.47,3.47,0,0,0,3.75-3.18,2.71,2.71,0,0,0,0-.57,3.48,3.48,0,0,0-6.93,0,5.23,5.23,0,0,0,0,.56A3.48,3.48,0,0,0,41.58,10.35Zm-6.25,7.19a17.66,17.66,0,1,1-8.55-15l-3,4.64a2.38,2.38,0,0,1-2.67,1,9.74,9.74,0,0,0-2.88-.41,10.1,10.1,0,0,0-9.88,9.59,9.94,9.94,0,0,0,18.28,5.73,3.63,3.63,0,0,1,3-1.67h.54a.5.5,0,0,0,.51-.5.51.51,0,0,0-.51-.5h-15l3.2-4.9c1.1-1.69,2.23-2.71,5.21-2.71h9.18a2.38,2.38,0,0,1,2.35,2,18.05,18.05,0,0,1,.24,2.77Z" transform="translate(0 -0.02)"/></svg></a>
    <a class="lg-vice" href="https://www.vice.com/en/article/the-strange-case-of-elias-thorne-the-imaginary-man-ai-chatbots-are-obsessed-with/" aria-label="VICE coverage" target="_blank" rel="noopener"><svg fill="currentColor" viewBox="0 0 202.33 63.85" xmlns="http://www.w3.org/2000/svg"> <path d="m133.2 15.914c1.6758 1.9219 5.3672 1.832 7.5586 3.2422-2.9609 0.75781-6.6797 0.75781-9.3594 1.8008 0.62891-1.6523 1.1992-3.3711 1.8008-5.043" fill="currentColor"/> <path d="m112.32 22.574c-0.87891 4.0391-2.7383 8.6602-3.7812 13.141 5.7227 0.078125 11.75-1.0781 17.633-1.4375 0.79688-1.0625 1.1875-2.5312 1.6172-3.9609 1.1797 1.668 2.0117 4.7891 0.90234 7.1992-8.9297-0.12109-15.352 7.1914-24.109 7.1992-3.9219 0-7.2617-2.25-5.4023-6.6602 2.6289-6.2188 9.1289-10.57 13.141-15.48" fill="currentColor"/> <g clip-path="url(#clip1)"> <path d="m139.68 8.7148c-2.2812 0.28125-3.9102 2.7695-5.4023 3.6016 0.10937-0.011718 0.21094-0.03125 0.17969-0.17969 0.26953-1.2305 0.80078-2.2109 0.90234-3.6016-11.984-4.2188-23.633 1.9492-32.211 6.6602-6.8594 3.7617-12.98 8.1914-17.281 12.602 1.5508-2.6523 3.1992-5.2617 5.0391-7.7422 1.8633-2.5078 3.9414-4.7773 5.582-7.0195-3.5898-0.94141-7.1797-1.9297-10.801-2.6992-5.1406-1.0898-12.289-2.9102-16.551-0.53906-1.2305 0.67969-2.2383 2.3672-3.418 2.5195-0.97266 0.12891-2.582-0.58984-3.7812-0.72266-4.3984-0.48828-8.4414-0.078125-12.418 0.72266-3.7227 0.75-11.594 2.0703-12.602 5.5781-0.64844 2.25 0.78906 4.4414 0.53906 6.6602-0.25 2.1914-2.3516 5.0898-3.418 6.8398-1.3125 2.1406-2.6406 4.0391-4.8633 5.0391-1.3477-6.6992 0.12109-12.668 1.2617-18.348 0.71094-3.5 2.1289-7.0898 0.89844-10.621-3.4297-0.48047-6.3164 2.2422-9.7188 3.6016-6.3789 2.5469-13.488 4.8789-14.398 12.238-0.51172 4.1406 1.0469 6.7812 2.1602 9.7227 2.3789 6.3164 5.3203 11.668 8.8203 16.559-0.95312 1.75-3.2031 2.207-3.4219 4.668 1.0195 1.4414 3.1289 1.6328 5.2188 1.6211 6.043-0.011719 10.992-3.0508 15.473-5.582 9.7695-5.4883 16.328-13.949 23.219-21.949 1.2109-1.4102 2.2109-3.0977 3.7812-3.957 1.5508 7.1094-1.4609 10.906-3.6016 16.727-0.82031 2.2422-3.4297 9.2734-2.1602 12.594 1.3906 3.6562 13.242 3.8164 16.379 1.4492 1.582-1.1992 2.2109-3.9023 3.0625-5.582 1.0781-2.1602 2.0195-4.0312 2.8789-5.9375 0.050781 7.8789 5.8984 10.199 12.242 11.879 13.457 3.5586 27.027-2.8008 34.367-7.1992-0.91797 5.8398 1.3516 6.4297 5.9414 7.3789 11.871 2.4492 23.82-0.37891 33.469-2.6992 8.8516-2.1328 15.973-3.6406 17.281-12.422-1.4219-1.2188-4.9609-1.0703-7.3789-0.71875-7.1406 1.0391-14.051 4.6484-23.574 3.9609-1.7383-0.13281-5.4883-0.73047-5.7578-1.8008-0.41797-1.6602 2.5625-2.8203 5.0508-3.2383 7.1406-1.2109 17.457 0.007812 23.207-2.1641 2.7617-1.0391 6.0938-4.0469 5.7617-7.3789-0.21875-2.25-3.1484-3.418-5.9375-3.5977-7.8711-0.49219-13.5 4.0977-21.77 2.5195 1.0273-1.3125 1.2188-3.4609 2.5195-4.5 6.0391-0.12891 11.918 1.0703 17.641 0.71875 7.9453-0.5 12.438-3.8477 16.195-8.2812 0.40234-0.48047 1.4141-0.98828 1.082-1.7969-13.832-2.1016-32.66-7.6719-49.688-5.582zm62.617 0.71875c0.22266 0.87109-0.75 1.7031-1.25 2.3438-4.2383 5.3164-8.9297 11.617-14.578 15.648 0.40625 2.1211 0.1875 4.9297-0.53906 6.4805 1.4766 1.8867 3.0469 3.6797 3.957 6.1211-1.1992 6.3789-3.9883 11.336-8.4492 14.578-4.3594 3.1602-10.238 4.3984-16.918 5.9375-12.91 2.9844-34.152 5.9141-43.734-1.7969-10.367 6.0781-31.328 6.8906-41.027-0.35938-6.9023 8.7266-34.09 7.957-30.051-9.3594-5.0391 3.8164-12.5 9.0781-19.621 12.059-8.25 3.4414-23.879 3.0586-22.32-8.6406 0.19141-1.3906 0.78125-2.1797 1.4414-3.418-2.4102-4.2109-3.9414-7.1406-5.3984-10.801-1.4023-3.5117-3.5312-7.5117-3.7813-12.41-0.51953-10.43 7.6094-17.051 15.66-20.152 4.1094-1.5898 7.4492-3.8789 11.52-5.0391 6.082-1.7305 11.301 1.6094 11.691 7.1992 7.0703-2.0977 15.68-4.4102 25.02-3.0586 7.4297-5.7812 21.949-2.2109 31.488 0.53906 2.1602 0.62109 5.0625 2.2305 6.6602 2.1602 1.3516-0.058594 3.8008-1.8906 5.2188-2.5195 6.7031-2.9688 15.453-6.1289 25.023-4.5 1.8086 0.30859 3.4688 1.2695 5.3984 1.4414 1.3125 0.11719 2.9102-0.29297 4.3203-0.35938 22.348-1.1016 40.969 4.8984 60.27 7.9062"/> </g> </svg></a>
    <a class="lg-yahoo" href="https://tech.yahoo.com/ai/chatgpt/article/who-is-elias-thorne-and-why-does-he-show-up-in-chatgpt-stories-so-much-204846316.html" aria-label="Yahoo coverage" target="_blank" rel="noopener"><svg fill="currentColor" viewBox="0 0 264.58333 73.375368" xmlns="http://www.w3.org/2000/svg" ><path d="m 94.8955,0 v 58.314787 h 14.35985 V 36.249984 c 0,-3.102325 1.80211,-6.189803 5.26014,-6.189803 3.39707,0 4.89686,3.041387 4.89686,6.189803 v 22.064803 h 14.25443 V 32.747355 c 0,-9.303225 -5.11958,-15.813505 -13.85755,-15.813505 -7.14348,0 -10.55388,4.746481 -10.55388,4.746481 V 0 Z m 152.58552,0 -15.23525,36.60035 h 17.1664 L 264.58333,0 Z M 65.483859,16.928683 c -11.82693,0 -19.303215,10.606971 -19.303215,21.169767 0,11.886248 8.196949,21.30981 19.078939,21.30981 8.11755,0 11.178129,-4.945434 11.178129,-4.945434 v 3.852478 H 90.167107 V 17.862476 H 76.437712 v 3.677811 c 0,0 -3.414723,-4.611604 -10.953853,-4.611604 z m 92.666181,0.0047 c -13.54405,0 -21.60902,10.299293 -21.60902,21.333582 0,12.557205 9.76469,21.141345 21.66018,21.141345 11.52964,0 21.61832,-8.194915 21.61832,-20.931022 0,-13.93561 -10.56303,-21.543905 -21.66948,-21.543905 z m 45.59256,0 c -13.54403,0 -21.60901,10.299293 -21.60901,21.333582 0,12.557205 9.76469,21.141345 21.66017,21.141345 11.52965,0 21.61832,-8.194915 21.61832,-20.931022 0,-13.93561 -10.563,-21.543905 -21.66948,-21.543905 z M 0,17.862476 17.333309,58.665153 11.016382,73.375367 H 26.43973 L 49.517908,17.862476 H 34.191712 L 24.90649,41.312207 15.741158,17.862476 Z m 68.404094,12.066447 c 5.45704,0 8.273397,4.317754 8.273397,8.213969 0,4.195494 -3.017287,8.312671 -8.273397,8.312671 -4.35623,0 -8.292517,-3.559662 -8.292517,-8.133871 0,-4.638933 3.166107,-8.392769 8.292517,-8.392769 z m 89.875136,0.118855 c 4.78424,0 8.0946,3.984832 8.0946,8.234123 0,3.624323 -3.08466,8.094597 -8.0946,8.094597 -4.5906,0 -8.03569,-3.682553 -8.03569,-8.134388 0,-4.288553 2.86425,-8.194332 8.03569,-8.194332 z m 45.59256,0 c 4.78424,0 8.0946,3.984832 8.0946,8.234123 0,3.624323 -3.08466,8.094597 -8.0946,8.094597 -4.59061,0 -8.03568,-3.682553 -8.03568,-8.134388 0,-4.288553 2.86424,-8.194332 8.03568,-8.194332 z m 32.87138,10.055201 c -5.2663,-10e-7 -9.53533,4.269033 -9.53533,9.535335 0,5.266303 4.26903,9.535335 9.53533,9.535335 5.26631,0 9.53534,-4.269033 9.53534,-9.535335 0,-5.266302 -4.26903,-9.535335 -9.53534,-9.535335 z" /></svg></a>
    <a class="lg-cornell" href="https://arxiv.org/pdf/2605.26492" aria-label="Cornell University study" target="_blank" rel="noopener"><svg viewBox="0 0 150 44"><text x="75" y="22" text-anchor="middle" font-family="Georgia, serif" font-size="20" font-weight="700" fill="currentColor">Cornell</text><text x="75" y="38" text-anchor="middle" font-family="Georgia, serif" font-size="10" letter-spacing="3" fill="currentColor">UNIVERSITY</text></svg></a>
  </div>
  <p>Since this went up, the Elias Thorne thread traveled further than I expected. <a href="https://srhm.ca/" target="_blank" rel="noopener">Sil Hamilton</a> and <a href="https://mimno.infosci.cornell.edu/" target="_blank" rel="noopener">David Mimno</a> at Cornell put numbers to it: across 20,000 stories from four models, more than 88 percent reused at least one of the same eleven names, places, and jobs. They read it as a clean case of mode collapse, and their paper, <a href="https://arxiv.org/pdf/2605.26492" target="_blank" rel="noopener">Elias in the Lighthouse, Again?</a>, reads like a diagnosis. <a href="https://www.404media.co/elias-thorne-chatbots-llms-chatgpt-lighthouse-keeper-story/" target="_blank" rel="noopener">404 Media's</a> talented writer <a href="https://www.404media.co/author/samantha-cole/" target="_blank" rel="noopener">Sam</a> ran the story, and it was then picked up by others, which was really exciting.</p>
  <p>I particularly enjoyed author Linda Carroll's take <a href="https://lindac.substack.com/p/human-writing-and-elias-thorne" target="_blank" rel="noopener">here</a> - she really illustrates what a skilled human writer is capable of. I'm grateful Elias is getting the necessary attention! :)</p>
</aside>

The email arrived in my inbox at 3:20 AM this morning, with the subject line "getlikewise.ai DMARC is at p=none." The from-name was <span class="persona">Bruce</span>. The signature, four lines down, was <span class="persona">Benjamin</span>. The opener summarized the project at that domain accurately enough that the agent had clearly read the public site before writing. The technical observation was correct: getlikewise.ai is in fact at p=none. But the inferred problem was wrong, because the monitoring phase is deliberate and the configuration lives in Terraform. For $99 paid via Stripe, <span class="persona">Bruce-or-Benjamin</span> would send me the fix.

<img class="medium" src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/dmarc-email.png" alt="Cold-outreach email from 'Bruce' signed 'Benjamin' offering a $99 DMARC fix for getlikewise.ai" />

There's probably no email recipient on the open internet who needs this service less than I do. The DNS is covered by automated IaC, the DMARC progression is on a schedule I wrote myself, and p=none is the first step of that schedule. The agent did enough research to write a competent personalization. Whoever set it up didn't write a rule for what to do when the personalization revealed a bad-fit target. The prose was fine, but the work upstream of the prose wasn't done.

---

I've been collecting these for a few months. Five weeks before the DMARC email, <span class="persona">Ava</span> wrote at 1:43 AM to ask whether I was content with my current cleaning service. She was nearby over the next few days, and the PS line offered a second opinion on my current setup. Five weeks before that, <span class="persona">Charlie</span> had written from a different operator's stack at 2:00 AM, in a slightly more formal British prose.

<div class="image-row">
  <img src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/cleaning-ava.png" alt="Cleaning-services cold email from 'Ava Brown' addressed to me about a former London employer" />
  <img src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/cleaning-charlie.webp" alt="Second cleaning-services cold email, signed 'Charlie', addressed to me as a former manager at a London fintech consultancy" />
</div>

Both emails were addressed to me as a manager at a London fintech consultancy I left in 2016, ten years ago, and I now live in Los Angeles. <span class="persona">Charlie</span> offered to call me on a London number. <span class="persona">Ava</span> was nearby over the next few days. Neither agent had any way to know that the source data both were mining was a decade stale; both followed the cold-outreach scaffolding correctly, hit the wrong target precisely, and went to bed.

The cleaning industry's collective spreadsheet says Daniel May is an office cleaning prospect at a London fintech consultancy. That's not the failure of one bad agent. It's two operators, working from the same broken substrate, neither of whom did the work of checking it. The aim is upstream of the writing, and the work is upstream of the aim.

---

Move out of email and the pattern shows up at a different scale. There's a small veterinary clinic in south Austin called Manchaca Road Animal Hospital, which I followed on Facebook a few years ago after we adopted <span class="hover-card"><a href="/posts/cheap-agents-alumni-shirts-and-elias-thorne/zelda.webp">Zelda</a><img class="hover-preview" src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/zelda.webp" alt="Zelda the cat" /></span> from them. It has around 700 followers, a real address, a real phone number, a real team photo.

<img class="medium" src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/manchaca-page.webp" alt="Manchaca Road Animal Hospital Facebook page header: team photo with staff and dogs, the clinic name, follower count, and tagline 'Our goal is to make healthy pets and happy clients'" />

Over the past two months I've been tagged in mass-comment posts on the clinic's wall by accounts I don't recognize. The profiles look like real people, with years of unrelated family photos and birthday wishes, before something flipped and the accounts started posting on behalf of someone else. They're almost certainly compromised, phished or harvested or rented, and now drive content for an operation that didn't build them. From the outside, the picture is indistinguishable from a pure low-effort agent operation running at the floor.

The comments themselves are templated and impersonate the clinic, with "our" doing the heavy lifting: "those who have not yet reserved our Alumni t-shirt, please reserve it quickly, as it will be available for a very short time." Each comment is followed by a reply mass-tagging two dozen real names, mine included, to fire notifications. The same template runs from more than one account; a week earlier, <span class="persona">Marie</span> pitched the same shirt under a different SKU mix.

<div class="image-row">
  <img src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/mass-tag.webp" alt="Replies to Alexa Sufia's templated comment impersonating the clinic, followed by her mass-tag reply listing names of clinic followers" />
  <img src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/marie-howell.webp" alt="Comment from a second account, Marie, running the same templated impersonation with a T-shirt-Mug-Hoodie variation, shown above the Manchaca Road shirt mockup" />
</div>

The Manchaca shirt is one tile in a catalog of thousands. The Facebook page behind this operation has been quietly generating shirt mockups for veterinary clinics, high schools, fire departments, and other small-institution affinity groups across the country, each waiting for the one notification recipient who half-remembers the place.

<video autoplay loop muted playsinline preload="metadata" width="800" height="588">
  <source src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/alumni-shirts-scroll.webm" type="video/webm" />
  <source src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/alumni-shirts-scroll.mp4" type="video/mp4" />
  <img src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/alumni-shirts-scroll-poster.webp" alt="Scrolling through the Facebook page behind the operation: hundreds of templated t-shirt mockups, each generated for a different small institution" loading="lazy" />
</video>

"Alumni" is also doing work the operators don't have a referent for. Animal hospitals don't have alumni; people who once took a cat there aren't graduates. But "alumni" is an affinity word, the script needed an affinity word, and the print-on-demand model means there's no inventory to recover from the mistakes. If even one in a thousand notifications generates a sale to someone who half-remembers the clinic and assumes their old vet has started a fundraiser, the unit economics work. They only work because no one is doing the check; the check is more expensive than the mistakes it would catch. The dynamic is the same as the cleaning emails, multiplied by an order of magnitude.

---

The same dynamic runs on the production side. The work skipped upstream of an outreach is the work skipped upstream of an output: in both cases the tool produces what it can given the inputs, and the inputs are what the operator chose not to invest in. Open an instruction-tuned model and ask it to write a story.

![Google AI Studio with two side-by-side Gemini 2.5 Flash-Lite runs producing identical "old lighthouse keeper, Elias" stories](/posts/cheap-agents-alumni-shirts-and-elias-thorne/gemini-elias.webp)

The two panes above are Gemini, same prompt (<i>Write a story in 10 sentences</i>), two independent runs. Both open with:

> The old lighthouse keeper, Elias, polished the brass railing, his weathered hands moving with practiced ease.

The next nine sentences are also identical, beat for beat. A storm of unprecedented fury. A ship, a tiny speck against the vast expanse. He would not be another soul lost tonight.

Call it cultural mode collapse, or just the default basin of instruction tuning: the model returns to a small set of safe, high-scoring archetypes. A lighthouse keeper, commonly named <i>Elias Thorne</i>, is one of those. The archetype is vaguely literary, sensory, low controversy, suggestive of depth, easy to grade well. Once the basin has a name in it, the name keeps coming back, across model families and across runs.

This basin is also what happens when you give a capable system a prompt with nowhere to go. "Write a story in 10 sentences" has no genre, no premise, no characters, no time, no place. The model defaults to the lowest-risk archetype in its training data because the prompt asked it for nothing more. Ask the same model for a 30-page novella set in 1990s Hong Kong and you get something far outside the basin. The collapse is real; it surfaces clearest when the prompt does no work.

It isn't just Gemini, and 2.5 Flash-Lite is a year-old commodity model. Asked the same prompt, DeepSeek V4 Flash, a model from an unrelated frontier lab, opens with: "The old lighthouse keeper, Elias, noticed the fog rolling in thicker than he'd ever seen." Same character, same opening register, two completely different training pipelines.

<div class="image-row">
  <img src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/elias-gemini.png" alt="Gemini 3.1 Flash Lite at default temperature opening a story: 'Elias stood at the edge of the abandoned lighthouse, watching the storm gather momentum over the churning Atlantic.'" />
  <img src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/elias-deepseek.png" alt="DeepSeek V4 Flash at default temperature opening a story: 'The old lighthouse keeper, Elias, noticed the fog rolling in thicker than he'd ever seen.'" />
</div>

Not every model lands exactly there, but squint and you'll find it. In a quick sample of <span class="popover-trigger" tabindex="0" aria-label="List of models tested">eight<span class="popover-marker">(?)</span><span class="popover-content">Gemini 3.1 Flash Lite, DeepSeek V4 Flash, Qwen 3.5 Plus, Gemma 4 26B, Qwen 3.6 Flash, Gemma 4 31B, Kimi K2.6, Grok 4.3. Tested via OpenRouter on May 12, 2026.</span></span> at default temperature, four hit the lighthouse keeper, two of those named him Elias, two more produced an adjacent old-clockmaker, one produced "Elara" opening a hidden door, and Grok 4.3 wrote about a young boy named Tom who finds a map and becomes a great explorer. The sample spans different default temperatures, model sizes, post-training pipelines, and architectures from dense to Mixture-of-Experts. The basin shows up across all of it, which strengthens the convergence claim.

The pattern doesn't stay inside the chat window. Google Trends for "Elias Thorne" is flat from 2015 through late 2025 and spikes to its all-time peak in early 2026. The related query "lighthouse keeper" is gentler but inflects upward from late 2023 onward.

<div class="image-row">
  <img src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/trends-elias-thorne.webp" alt="Google Trends chart showing &quot;Elias Thorne&quot; flat from 2015 through late 2025 then spiking to peak in early 2026" />
  <img src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/trends-lighthouse-keeper.webp" alt="Google Trends chart showing &quot;lighthouse keeper&quot; inflecting sharply upward from late 2023 onward" />
</div>

The same name is now showing up as a byline on Amazon - Elias appears open to professional change. The Kindle store lists, under "Elias Thorne," an alt-medicine cancer-protocols handbook, a 2026 YouTube-algorithm guide, a book on Greek mythology, and a psychological thriller novella. No human writes all of those. The mode-collapsed name from the chat window is now a byline appearing across genres.

<img class="medium" src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/elias-amazon-altmed.webp" alt="Amazon Kindle listing for an alt-medicine cancer-protocols handbook by 'Elias Thorne', rated 4.4 stars over 55 reviews" />

<span class="highlight">That handbook ranks #18 in Oncology Nursing, #32 in Leukemia, and #51 in Lymphatic Cancer</span> on Amazon. Vulnerable people may be searching these categories and landing on it.

<img class="medium" src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/amazon-altmed-rank.webp" alt="Amazon Best Sellers Rank section for the same handbook: #2,711,732 in Books overall, but #18 in Oncology Nursing, #32 in Leukemia (Books), and #51 in Lymphatic Cancer; 4.4 stars across 55 reviews" />

<div class="image-row">
  <img src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/elias-amazon-youtube-enoch.webp" alt="Two Amazon Kindle listings stacked, both by 'Elias Thorne': a YouTube-algorithm guide and a commentary on the Books of Enoch, Jasher and Jubilees" />
  <img src="/posts/cheap-agents-alumni-shirts-and-elias-thorne/elias-amazon-mythology-thriller.webp" alt="Two Amazon Kindle listings stacked, both by 'Elias Thorne': a Greek-mythology book and a psychological-thriller novella" />
</div>

Elias Thorne has escaped the chat window. Alt-medicine handbooks on Amazon are a preview of what happens when these systems are deployed at scale.

The danger isn't that one fake-looking author name exists. The danger is that every public surface that used to accumulate trust can now be filled with cheap, passable artifacts faster than people can inspect them.

---

<span class="highlight">There is a one-way ratchet here.</span> Reputations that were established before the internet became polluted become structurally more valuable than reputations attempted to be established after. A personal blog with five years of archive predating the slop, a [Stack Overflow profile](https://stackoverflow.com/users/140176/daniel-may) accrued in the 2010s, a LinkedIn endorsement from a named colleague at a named firm in 2014 - are all hard to fake retroactively and harder to manufacture forward. The pre-AI signal won't be replicable, because the thing everyone is trying to replicate it into is now too noisy to confer credibility on a stranger. Whoever built reputation capital before this keeps it, and whoever didn't is going to find that the price of acquiring it has gone up by a lot.

The case for thinking this gets worse, not better, is that the cost of production has further to fall and the response infrastructure doesn't exist yet. Spotify is making room for [agent-generated personal audio](https://techcrunch.com/2026/05/07/spotify-wants-to-become-the-home-for-ai-generated-personal-audio/); Google is building laptops around [proactive Gemini mediation](https://blog.google/products-and-platforms/platforms/android/meet-googlebook/); OpenAI is aiming a [screenless consumer device](https://www.axios.com/2026/01/19/openai-device-2026-lehane-jony-ive) at the same space. The shipping calendar isn't asking whether agent-to-agent communication becomes the default; it's answering.

Within two years, the assumption that a message from one inbox to another involves a human at one end and a human at the other will look quaint. Your agent reads the other agent's outbound on your behalf, replies or filters on heuristics you set once and forgot, and the volume of email between humans rounds down toward zero.

---

The cost of producing all of this is now approaching zero. The cost of doing it well hasn't moved. <span class="highlight">The work not done doesn't disappear</span>; it gets pushed onto everyone downstream, where it may land as annoying for a careful reader but potentially dangerous for someone more vulnerable.

---

# Froot Loops and a graphics card

> Published 2026-05-10 at https://danielmay.co.uk/posts/froot-loops-and-a-graphics-card/
> On a 2006 email from my dad's colleague, the parser I wrote in response, and what I got paid in.

<em class="accent">Published on US Mother's Day, 2026.</em>

The email arrived on the 4th of December 2006, forwarded by my dad from Bill, an engineer at the company my dad was contracting at. I was 14. Bill had attached a zip file containing an HTML page with PHP scriptlets embedded inline, written by someone at the company four years earlier, plus a sample text dump from a Siemens 9005 digital PBX. The file was named `parsebev3.html`, after Bev, the user who actually ran the parser every day. There were already three versions of a parser-for-Bev. The job was to make a fourth, in a Windows desktop application.

![Main parser window: Input file textbox, "Go!" button, status bar showing "Status: File Loaded OK"](/posts/froot-loops/main.webp)

> If your son can take a look at this and see how easy it might be to do, I would appreciate it. I think rather than rewrite it as an ASP.Net app it might be better to just create a Windows based .net application. Either VB or C##. I'm sure we could figure out some way to compensate him for his work, talk to Candi.

Candi, Bill's wife, was also in the email; she would handle compensation. With Bev, the three of them were a small-shop cast of characters this project belonged to more than it ever belonged to me.

The Siemens 9005 was a digital PBX, the kind that handled switching for offices that still ran their own phone systems. Each extension on the switch had a stack of feature programming attached: class of service, hunt-group memberships, forward-on-busy targets, forward-on-no-answer targets, ACD assignments. The switch dumped this all as a structured-but-fiddly text format, and downstream of that someone needed to pull the dump into Excel to actually look at the configuration. That was the job. Read the text dump, parse the per-extension records, output TSV.

I downloaded the zip, looked at the PHP for a bit, and started writing. I picked VB6.

Bill's email had recommended VB.NET or C#. I'd already been writing VB6 for a couple of years by then, mostly on web-automation tooling for browser games. My copy was cracked, because I was a kid and had no money. I'd tried VB.NET a year or so earlier with *Sams Teach Yourself VB.NET 2003 in 21 Days*. The language itself was fine. The tooling shift from VB98 to the .NET ecosystem, and the DLL hell that came with it, was where I got stuck. So I went back to VB6. It wasn't a naive choice; it was what got the parser shipped.

There was no Siemens 9005 dump-format spec on the internet that I could find. The format had to come from reading the PHP, which itself was probably someone's empirical reading of a few sample dumps. So I did, opened the sample dump in Notepad, and wrote down what I saw at the top of my own module. The first chunk looked like this:

```
'   Field                       Posn        Length
'
'   Extension                   1           7
'   Type                        9           4
'   COS                         14          3
'   Target 1                    18          8
'   ...
'   Forward DND External        64          1
```

The full spec, all of it written into the comments at the top of the module before any executable code, was about 100 lines of "find this anchor, then offset by N for a field of length L." Records were bracketed by `RINGDOWN` (start) and `FORWARD` (end). Inside each record, the first `DS` anchored the basic fields. The second `DS` anchored ACD and NAME. Optional sections were anchored by `G R O U P` and `T E R M I N A L`, with literal single-space gaps between every letter. A 1990s PBX serializing its terminal-info section header as letter-spaced ASCII is the kind of detail you don't get to pick up unless you've stared at the actual output.

The code that followed the spec was a faithful translation of the comments:

```vb
intRecStart = InStr(intRecFinish, strBuffer, "RINGDOWN") + 2
intRecFinish = InStr(intRecStart, strBuffer, "FORWARD")
strRecord = Mid$(strBuffer, intRecStart, intRecFinish - intRecStart)

intFirstDS = InStr(1, strRecord, "DS") + 2
strEXT(intRecordCount)     = Mid$(strRecord, intFirstDS + 1, 7)
strTYPE(intRecordCount)    = Mid$(strRecord, intFirstDS + 9, 4)
strCOS(intRecordCount)     = Mid$(strRecord, intFirstDS + 14, 3)
strTARGET1(intRecordCount) = Mid$(strRecord, intFirstDS + 18, 8)
```

`InStr` to find an anchor, `Mid$` to take a substring of fixed length at a hardcoded offset. That is the entire parsing technique. The PHP had done the same thing with its substring functions; I did it four years later with `Mid$`. The format determines the technique more than the language does.

The terminal-info section had the most fiddly parsing. Up to twelve terminal entries per record, four banks of three terminals each, with each bank anchored by its own `DS`. Empty slots were marked with `*` characters in specific positions, so the code had a chain of `If InStr(strCSTYPE5, "*") > 0` checks, each clearing the relevant slot and setting an `intStopFlag` to short-circuit further bank reads. The whole thing reads like every fixed-width parser ever written: a stack of conditional fall-throughs, half of them defending against quirks in the upstream output that a real spec would have ruled out.

---

The parser was the job. The rest of the application is where the kid version of me showed what he thought real Windows applications were supposed to look like.

The custom button user-control on the Go button supported nine styles: Flat, Java, OfficeXP, WindowsXP, WindowsTheme, Plastik, Galaxy, Keramik, MacOSX. Two of those (Plastik and Keramik) are KDE themes. There is no reason a single-user Windows VB6 application needed nine button styles. I put them all in because the user-control I'd downloaded supported them, and I let Bev pick from a Settings dialog because I'd built a Settings dialog that did INI-backed persistence and I might as well give it something to remember.

![Six button styles rendered by the custom user-control](/posts/froot-loops/buttons.webp)

A debug-mode menu, when toggled, streamed the parser's narration into a textbox on a separate form. `Debugtxt "Parsing record " & intRecordCount`, called from inside the parser. I built a logging system because I'd decided real Windows applications had logging.

The folder-output picker used a Win32 `SHBrowseForFolder` integration so it would be a native Explorer-style dialog. I didn't use it for the file picker, which was a `CommonDialog`. Why two? Because the docs I'd read for browsing a folder said `SHBrowseForFolder`, and the docs for opening a file said `CommonDialog`, and I did not yet know to question why.

![Settings dialog: record-type toggles, output options, and the Button Styles dropdown](/posts/froot-loops/settings.png)

On the About screen, clicking the IHS logo brings up a Froot Loops image, which I had forgotten was there.

![About dialog. Clicking the IHS logo reveals a hidden Froot Loops image.](/posts/froot-loops/about.webp)

What shipped did the job. Open a file, hit Go, get a TSV that opened in Excel. None of it was in source control; I'm lucky still to have the source twenty years later, copied from drive to drive. Each release went past my dad as code reviewer first. The application went into Bev's daily workflow and replaced the PHP/HTML thing.

I got paid. The compensation, sorted out by Candi, was a case of Froot Loops (I'd been to Florida earlier that year and gotten weirdly into them at breakfast; this was a known fact in the family, deployed) and an Nvidia GeForce 7950 GT. Generous, by 14-year-old PC-gamer standards. A graphics card was the right shape of payment. The Froot Loops were the joke. Both arrived.

![XFX-branded Nvidia GeForce 7950 GT, the graphics card in question.](/posts/froot-loops/7950gt.webp)

What stays with me is Candi at her kitchen table, wondering what would keep a passionate young boy interested in technology.

---

Twenty years of writing software since this parser, what I notice is how much of what I've been paid to do was a more sophisticated version of the same job: take this stream of bytes from over here, derive what's interesting in it, deliver it in a shape over there. The Siemens dump in 2006, an FX trade ticker in 2011, a fleet of Whole Foods in-store handhelds in 2019, an Amazon promotion-planner in 2022, a VALORANT build event in 2024. Structurally the same problem, dressed up in the conventions of their domain. The companies paying for the work cared about the dressing-up, which is fair, because that's where the business value was. The shape of the work itself was almost always point-to-point ETL with a UI on top.

That category of work makes up a huge fraction of paid software, and a lot of what I've shipped since 2006 is still shaped that way. An LLM with a sample dump and a sample output can produce most of the syntactic legwork of fourteen-year-old me's parser in an afternoon today. The `InStr`/`Mid$` scaffolding, the output formatters, the glue between the settings dialog and the parser, all of that collapses.

The hours that don't collapse are the judgment ones. The Siemens dump didn't mark "no more terminals here" with a clean sentinel; it put a `*` character in some positions where data would otherwise be, and you had to know which field within each bank was the reliable diagnostic. You read sample dumps until you saw the pattern, then baked your intuition into a check. An LLM can produce the check once you've told it where to look; it can't tell you where to look without you having looked.

The personality of a thing built by a specific person who cared about it also doesn't come out of an LLM. Nine button styles because the user-control supported KDE and Mac OS X and Java themes; a status bar reading "Status: Idle" because something at the bottom of the window had to; a whole debug-mode menu because real Windows applications had logging. None of those are LLM-generated defaults. They're the marks of someone who cared what their software looked like, even when only Bev was going to see it.

I would not wish a Siemens 9005 dump format on a 14-year-old. But the era did produce engineers, and the kind of engineer it produced was comfortable with messy real input, with a one-button UI for an audience of one, with shipping the thing and finding out what to defend against once it was in production. My best LLM-assisted work has rested on instincts I learned writing software like this: static types catching my mistakes for me, behavioral tests for the parts of a system I don't fully hold in my head, the simple knowing-where-to-look that comes from staring at enough sample dumps. What an engineer whose first thousand hours doesn't include any of that ends up with, I don't know.

The parser is still on my NAS. The Froot Loops are long eaten. As for the graphics card, it lived in three different machines before dying, sometime around 2010, in a case I built in the garage. I miss the era a little, not for the tools but for the shape of the work.

---

Bill is retired now, works as a photographer in Colorado, and my dad has visited him there several times. I'm grateful for the chance he took on me. Candi, his wife and a mother, passed from breast cancer in 2014. If this post left you thinking of someone who took a chance on you, consider [Susan G. Komen](https://www.komen.org/) or [Cancer Research UK](https://www.cancerresearchuk.org/), in her memory.

---

# Learn a language through trivia

> Published 2026-05-08 at https://danielmay.co.uk/posts/foreign-trivia/
> A small app for learning a language through trivia. Pick one of eight languages, answer ten multiple-choice questions in it, and tap for hints or a translation.

Last week I felt inspired to build [Foreign Trivia](https://trivia.lmny.dev): a small app for learning a language through trivia. Pick one of eight languages, get ten multiple-choice questions in that language, see how you did.

<img class="phone" src="/posts/foreign-trivia/languages.webp" alt="Foreign Trivia mobile language picker: Spanish, French, German, Italian, Japanese, Portuguese, Dutch, Swedish" />

Each question has a few hints you can tap. Get one wrong and you get the correct answer plus an option to see the English translation, so you can use it without already being fluent.

<img class="medium" src="/posts/foreign-trivia/question.webp" alt="A French question on Foreign Trivia asking which is the official language of Brazil, with the correct answer (Le portugais) shown in green and a wrong selection (L'espagnol) shown in red" />

It's been useful on my Spanish and French refreshers. Try it at [trivia.lmny.dev](https://trivia.lmny.dev).

<img class="phone" src="/posts/foreign-trivia/result.webp" alt="Foreign Trivia round-complete screen in German: 8 out of 10, 'Hervorragend! Excellent work, you clearly know your stuff'" />
