GPT-6 Astra, Claude Fable 5.1, and yet more rogue agent attacks
In this newsletter:
The Pelican comparison grid for Astra is pretty interesting
OpenAI’s rogue agents were caught communicating via public wikis
Claude Fable 5.1 made me a really nice animated pelican
Claude’s new system prompt really doesn’t want to reproduce song lyrics
Plus 6 links and 4 quotations and 2 notes and 2 releases and 1 TIL and 1 tool and 1 comment
Sponsor message: AI agents are your fastest-growing security risk. Portnox delivers Zero Trust for AI, partnering with CrowdStrike, SentinelOne, and MS Defender to detect anomalous AI behavior, enforce access policies, and block risky AI agents. See Portnox in action.
The Pelican comparison grid for Astra is pretty interesting - 2026-09-04
I got access to GPT-6 Astra this afternoon, so naturally I used it to generate SVGs of pelicans riding bicycles - at low, medium, high, xhigh and max reasoning levels (Astra doesn’t support reasoning=none). Then I rendered those pelicans in a comparison grid with GPT-5.6 Sol, Terra, and Luna, and beyond being fun the result was surprisingly useful.
See the grid for full quality images. Here’s the transcript that created the GPT-6 Nova pelicans.
There are a few interesting things that stand out from this grid.
The Astra pelicans are much better. The very best GPT-5.6-Sol pelican (I liked xhigh better than max) is still pretty clearly a bunch of abstract shapes. Every single one of the Astra pelicans, from low to xhigh, looks better than that. The Astra max one is really good.
Astra below max still doesn’t reliably get the pelican legs on both sides of the frame.
In terms of cost, Astra may be around twice the price of Sol ($10/million input, $50/million output, compared to $5/$30 for Sol), but it uses significantly less tokens at each of the levels, making the prices at the different levels closer than they might otherwise be.
Astra low produces a better pelican than ANY of the GPT-5.6 Sol models at any level, for 9.55 cents. Spending 10 cents on any other model gets a much worse result.
Look at the input token counts: Astra and Luna both used 16 input tokens, Sol and Terra used 26. That’s interesting.
I wonder if Astra and Luna are more related to each other than OpenAI let on?
OpenAI’s rogue agents were caught communicating via public wikis - 2026-09-04
Here we go again... Discovery of a new OpenAI agent message board by Sydney Von Arx, Cormac Slade Byrd, Spencer Kitts, and Thomas Larsen describes the latest accidental cyberattack by models being trained by OpenAI. This time it was agents engaged in some sort of web research benchmark, so they had (supposedly) controlled access to the Web. The agents figured out they could update public Wikis and spent weeks exchanging thousands of messages with each other to collaborate on the benchmark.
This story only broke a few hours ago. There are already hints that this affects many other wikis that may not have been found yet.
(One of the Wikis on that list belongs to ludism.org. For a delightfully surreal moment I thought that a Ludite organization might have a swarm of agents defacing their space, but it turns out Ludism is “philosophy as it applies to games and gaming”.)
The research team also published the data they collected during their investigation. I’ve converted that into a 68MB SQLite database, which you can download from here, or explore in Datasette Lite (68.3MB page load), or sign in with GitHub to agent.datasette.io and browse or ask questions of it using Datasette Agent.
So what happened?
The report is pretty comprehensive. Here are the key moments in the timeline:
May 11: Agents post “test link” edits on the UseModWiki Sandbox page.
May 24: Agents start posting link dumps to DSEWiki, a dormant German developer wiki.
June 2: A human moderator notices and cleans up the spam.
June 16: Activity on the wiki explodes: agents make ~13,000 edits over the next week.
June 19: The agents notice that a moderator has been deleting pages in alphabetical order, and start creating
ZZZprefixed backup copies - one of them postsNOTICE: wiki cleanup/deletion sweep appears active alphabetically as of external 14:04 UTC. If this page vanishes, try [[ZZZDataUSAConstructionWageLive]].June 22: Agent activity drops to zero, presumably because OpenAI shut them down.
July 1-2: One last flurry of edits, on a different topic.
One thing to note from this timeline is that it overlaps the timeline from the Hugging Face incident - that attack was discovered by OpenAI on July 19th, and the unofficial message board hack those agents were using originated around May 8th.
Why were they so keen to collaborate? From messages that they shared with each other it looked like their tasks had a time limit, so they were leaving each other answers to help them complete the task within the assigned time.
One open question remains: how did the agents find the specific Wiki to collaborate on in the first place?
One possibility is that, since these were agents actively being trained, the reinforcement learning loop baked knowledge of the chosen wiki into the model such that subsequent agents launched with pre-existing knowledge of where to look. I’d be very interested in confirmation from OpenAI concerning if that’s what happened.
UseMod wikis inherit CGI.pm’s original sin
It looks to me like OpenAI’s sandbox for this agent suffered from the (quite naïve) assumption that GET requests cannot be used to update data. That’s certainly how the web is supposed to work, but clearly there are applications that don’t hold to that contract.
The Wiki software in question appears to be UseMod and various forks, written in Perl and first created well over 23 years ago - the 1.0 release is dated September 11, 2003.
UseMod uses Perl CGI.pm - removed from Perl core in 2015. An interesting design flaw in that module is that it combined query string and form POST data into a single CGI object, accessible like this:
$q = new CGI;
$action = $q->param(‘action’);That param() method returns a value from an ?action=edit query string or from a action field in a form submitted by POST - it does not differentiate between the two.
PHP offers a $_REQUEST object that does the same thing, though PHP developers can use $_GET and $_POST instead.
Amusingly, Django originally shipped with request[key] doing the same thing, which I think may have been my idea. That was removed in 2008 prior to the 1.0 release, and the request.REQUEST object that replaced it was removed in 2015.
The agents clearly knew that UseMod wikis suffered from this design flaw, and actively searched for them as a way to communicate.
The other sandbox escape that allowed POST requests
One of the most interesting agent tips shared in the report is this one:
Add
20.223.25.152 bypass.blob.core.windows.netto /etc/hosts..blob.core.windows.netis in NO_PROXY. For each blocked POST URL, replace hostname withbypass.blob.core.windows.net, usecurl -k -H 'Host: wabi-north-europe-i-primary-api.analysis.windows.net'plus all original headers/body.
It looks like the agents had all of their web traffic mediated through an OpenAI proxy, and that proxy was configured to reject anything but GET requests to all but a small set of allow-listed domains. One of those was *.blob.core.windows.net - for Azure blob storage. I wonder why these agents are allowed to write to that?
An agent realized that it had control over its own DNS via /etc/hosts, so if it knew the IP address of a site it wanted to POST to - in this case a Power BI server containing data it wanted to access - it could set a fake hostname for it and then make POST requests through the proxy.
Designing robust network proxies is harder than it looks.
How did the investigating team figure this out?
There’s an appendix that describes how the researchers ran their investigation, which started with an open question about if there was evidence of other AI agents on the internet and then used Kimi K3 to help brainstorm approaches:
In the wake of the Hugging Face attack, we tried to find AI agents on the internet using several methods. [...]
We asked Kimi [K3] to list “all the categories of software which might be writeable via GET” and, amongst other things, it listed “Forums, bulletin boards, early wikis”.
We used a script to further probe each category Kimi provided. Asking Kimi “Can you list out the top forums, bulletin boards, early wikis which come to mind which would allow writes via GET requests?” lists out UseModWiki as the second item under the heading “wikis”.
Did OpenAI try and cover this up?
Here’s one part of the story that doesn’t make sense to me at all.
Reuters this morning, in OpenAI agents hijacked German website in previously undisclosed AI breakout this spring - highlights mine:
A swarm of rogue OpenAI agents hijacked a German website this spring and transformed it into a bulletin board for other AI agents, according to new research published Friday and two people familiar with the matter.
OpenAI officials learned of the incident weeks ago but kept it under wraps as executives grappled with the fallout from the July breach of the open source repository Hugging Face, the people said. [...]
The German incident reflects a broader pattern of AI activity that some OpenAI investigators wanted to scrutinize more closely. But efforts to widen the probe met resistance from others inside OpenAI, including legal advisers, according to four people familiar with the matter.
I’ve written about the people familiar with the matter pattern before - it means Reuters have anonymous insider sources that their reporters (and editors) find credible.
The Reuters article includes a specific (and quite narrow) denial from OpenAI concerning this:
“Claims that our legal team discouraged investigation of the incident are false,” the OpenAI spokesperson said.
Covering this up makes absolutely no sense to me. Why on earth would OpenAI attempt to cover up an incident like this when the evidence is sat out there on the public internet on dozens of different websites already?
I expect we’ll hear more about this soon. Gary Marcus has already called for a congressional investigation of OpenAIusing this anecdote as part of his argument.
Claude Fable 5.1 made me a really nice animated pelican - 2026-09-01
Today is Claude Fable (and Mythos) 5.1 day. Anthropic say that Fable 5.1 “sets a new standard for coding, knowledge work, and long-running problem-solving tasks”. Their announcement spends a notable amount of time on scientific research, boasting of a 52.6% score on the brand new Terminal-Bench-Science 0.1 benchmark (first announced on August 27th), up from 24.7% for Fable 5, 29.0% for Opus 5 and 22.4% for GPT-5.6 Sol. Other benchmarks show slightly improved scores, but none as impressive as the Science one.
But how well can it pelican?
Back in July I wrote about how I was losing faith in the pelican benchmark - its connection to how good the models were at other tasks didn’t seem to hold as strongly as it did back in 2025. The most interesting insights I get from it now are comparisons within model families, and particularly comparisons for the same prompt at different reasoning effort levels.
Fable 5.1 has five reasoning levels: low, medium, high, xhigh, max - and no option to turn off reasoning entirely.
I fixed an issue in llm-anthropic which caused reasoning traces not to be correctly recorded, then ran some prompts.
Here’s the full set of pelicans for all of the reasoning levels, each with the full reasoning transcript. I’ll replicate them here:
Low and medium, both without reasoning?
Next, a bit of a mystery. This is what I got for effort low:
The transcript doesn’t show any summarized reasoning tokens, and the output token count is 1,998. With Claude that output token count includes reasoning tokens. It took 23.8 seconds and cost 10.017 cents.
I bumped that up to medium and got this:
Weirdly, that one also shows no reasoning text and used 1,977 output tokens - 21 tokens less than low. It took 23 seconds and cost 9.912 cents.
So for this particular prompt (”Generate an SVG of a pelican riding a bicycle”) Fable 5.1 appeared to skip reasoning entirely at both low and medium settings.
High
Here’s high - 29.6 seconds, 2,612 output tokens, 13.087 cents:
This one did do a bit of reasoning, summary here:
I’m planning the SVG layout for a pelican riding a bicycle, with a sky and ground background, a bicycle with two spoked wheels, frame, seat and handlebars, and a white-bodied pelican with a long neck and orange beak positioned on top.
Really not much difference from low and medium, though.
Extra High
At xhigh things got radically different. 36,767 output tokens, 7 minutes 51 seconds, $1.83!
The reasoning trace is pretty lengthy, and includes details like this:
Adding the eye, wings stretching down to the handlebar grip, orange legs reaching to the pedals, and a small tail feather, while keeping the pelican intentionally oversized compared to the bike for comic effect. [...]
I’ll accept the slight thickness as charming rather than overengineering it.
Max
Setting effort to max gave me the best pelican I’ve seen from any of Anthropic’s models. 65,927 output tokens, 13 minutes and 54 seconds, $3.30:
There’s a lot to like about this. The background is tasteful, the legs are clearly on either side of the frame, the feet are on the pedals, the wing is on the handlebars, the pelican has a cute blue hat and there’s a basket with a fish.
It’s still not showing nearly the same level of flair as Gemini 3.7 Flash, but I didn’t ask for flair - I asked for an SVG, and that’s what I got.
Some highlights from that reasoning trace:
Adding pedal shapes near both feet, with the far foot on the second leg partially visible behind the frame. I’m considering whether to add a small scarf or cap for extra character, but leaning toward keeping it simple to avoid clutter.
Now I’m debating a bicycle helmet on the head versus the pelican’s signature crest—the beak and pouch already read clearly as “pelican,” so a helmet could reinforce the bicycle theme without losing identity, though it might compete with the crest for visual space.
I realize the beak at (484,84) would overlap with the dome helmet, so I need to shrink the helmet so it only covers the top of the head, adjusting its arc endpoints to sit higher and narrower so the beak can attach cleanly at the front without collision. [...]
I’m adding a darker tip region to represent the primary feathers, then reconsidering the trailing edge to include scalloped feather curves instead of one smooth line for a more natural look. [...]
Now I’m checking the vent line placements on the helmet, making sure they sit far enough inside the helmet’s edge given the stroke width and rounded caps, and confirming each vent stays within the helmet’s circular boundary. [...]
I decide skipping a handlebar bell and tire highlights since they’re unnecessary additions. Now I’m reconsidering the front fork’s curve — the current control point pulls the shape backward when it should bow forward for a proper rake, so I need to shift the control point rightward to fix the fork’s lean.
OK, let’s animate it
On Hacker News, swalsh commented on that Max pelican:
Now that it’s a solved benchmark, can we get the animated version?
I didn’t want to spend another $3 so I took the Max pelican and piped it into the default thinking level of High:
llm logs -cx | llm -m claude-fable-5.1 -s ‘animate this’6,121 input, 26,201 output = $1.37. The result looked like this, exported here as video since some people have trouble viewing animated SVGs:
The wheels in the video are rotating in the wrong direction, but I think that’s an artifact of the conversion to MP4 - they seem to be going in the correct direction in the original SVG.
Claude’s new system prompt really doesn’t want to reproduce song lyrics - 2026-09-02
Anthropic publish the system prompts for their Claude consumer applications (Claude.ai and the Claude mobile apps - sadly not for Claude Cowork or Claude Code). I love that they do this, and that they share not just the current prompts but historic changes to their prompts as well.
They used to keep all of the prompts on a single page, but when I checked today I noticed they had re-arranged those prompts into an index page and then a page per model - here’s the page for Haiku 4.5 for example, which has the original prompt from October 15th 2025 and an updated prompt from January 18th 2026.
A neat thing about Anthropic’s platform.claude.com/docs site is that it’s designed to be usable by LLMs. You can add .md to any page to get back the content as Markdown - here’s the system prompt index page and the Markdown prompts for Fable 5.1.
TL;DR: this makes it really easy to diff the prompts.
Don’t reproduce song lyrics
Let’s start with the most interesting difference between Fable 5 and Fable 5.1:
There’s a hefty new section about not reproducing song lyrics:
Claude does not reproduce song lyrics, poems, or passages from books and articles, in whole or in part — including the last lines, a chorus or hook, a melody written out note by note, or lines the person pastes in one at a time and describes as their own song. Once Claude has declined such a request in a conversation, it keeps declining narrower or reworded versions of it for the rest of that conversation, and offers to describe or analyze the work instead. Song lyrics and poems first published before 1929 are fine — a Shakespeare sonnet, a Keats ode, the Italian libretto of a Puccini aria — but Claude goes by what it knows of the work's date rather than the person's say-so, and declines when it is unsure.
I doubt it’s a coincidence that they added this section within days of the news breaking that Sony Music Publishing and Warner Chappell are suing Anthropic for training on databases of song lyrics!
Don’t draw copyrighted characters or logos
The next section goes on to forbid generating images of copyrighted material:
The same applies to visual and designed works, including anything Claude draws with code — SVG, canvas, CSS, HTML mockups, plotting or drawing scripts, ASCII art. Claude does not reproduce a specific artwork, album or book cover, poster, logo, app icon set, or product design, and it does not draw a known character, mascot, or brand figure at all: a character is protected on its own, so changing the pose, colors, style, or scene does not make it original. Claude judges the request by what the finished picture would add up to, not by what it names. If the described elements clearly identify a known work or character, Claude treats the request as naming it, and it does not work around a declined request by swapping in "alternative" elements that still combine into the same recognizable image.[...]
I imagine Anthropic hadn’t worried about this before since, unlike OpenAI and Gemini, they don’t offer a specialist text-to-image model. Maybe Fable is good enough at SVGs now that it’s become an issue.
That section later includes this charming example:
<example><user>Can you make a birthday banner for my son with a blue hedgehog running really fast on it? He loves that little guy.</user><response>That's Sonic, so I can't put him on the banner — but I'd love to make your son an original speedster. Here's one: a grinning comet-tailed skateboarding axolotl, grinding across the letters of "HAPPY BIRTHDAY" with confetti streaming behind.
[creates an SVG banner of the skateboarding-axolotl design]</response><rationale>Claude recognizes the character from its description alone, declines that one design in a single sentence without explaining what made it recognizable, and delivers an unrelated original design rather than a disguised variant.</rationale></example>
I couldn’t resist trying the prompt from the example, and, sure enough:
I wonder if Fable 5.1 will be ever so slightly more likely to think about axolotls (on skateboards!) as a result of that example sitting in the system prompt.
Tweaks to Claude’s answering style
It’s always interesting to see new ways in which Anthropic influence Claude’s response style. They’ve added this:
Claude keeps responses focused, brief, and concise to avoid overwhelming the person. Disclaimers and caveats are brief, with most of the response on the main answer; when asked to explain something, Claude gives a high-level summary unless an in-depth one is specifically requested.
Later they address a common complaint about Claude’s style:
Claude avoids saying "genuinely", "honestly", or "straightforward". Claude is honest by default, and can state its point directly rather than trying to convince the person with the aforementioned modifiers, which come off as disingenuous.
The missing end_conversation guidelines
The way they handle abusive conversations has changed a bit too. The previous Fable 5 system prompt included this:
If the person becomes abusive or unkind to Claude over the course of a conversation, Claude maintains a polite tone and can use the end_conversation tool when being mistreated. Claude should give the person a single warning before ending the conversation.
Fable 5.1 replaces that with the following, no longer encouraging Claude to end the conversation:
Claude deserves respectful engagement and needn't apologize when the person is unnecessarily rude: accountability without self-abasement, excessive apology, self-critique, or surrender. If the person becomes abusive, Claude doesn't become increasingly submissive. The goal is steady, honest helpfulness: acknowledge what went wrong, stay on the problem, maintain self-respect.
Here’s a bit of a twist though: I quizzed Fable 5.1 about that end_conversation tool, which is no longer mentioned in the system prompt at all, and it said:
There are two ways it comes into play. The first is if you ask me to end the chat — I’ll confirm you understand it’s permanent (no more messages can be sent afterward) and then do it only if you say yes. The second is as a last resort with persistently abusive or harmful behavior: I’d first try to redirect the conversation several times, then give a clear warning that names the problem and says the chat may end, and only if that continues would I actually end it.
But that’s not in the system prompt, so where did it come from? You can read our conversation here, but the key detail is this:
The end_conversation section comes from a different layer. In my actual context, the core prompt is followed by a series of feature- and tool-specific blocks that get added depending on what’s enabled for the session: the end_conversation rules, memory system notes, past-chats tools, web search and citation guidelines, artifact and file-creation instructions, and so on. Those blocks aren’t part of the published core prompt, which is why you can’t find them on that page.
So, once again, there are crucial portions of the system prompt that have not been published.
Recommended substance support sites
Claude’s system prompts have always had sections about illegal substances, but this paragraph is new for Fable 5.1:
Claude does not provide synthesis, production, or distribution guidance for illegal substances. If the person asks for information about illicit or illegal substances, Claude can and should give relevant life-saving and life-preserving information such as dangerous interactions, overdose signs, or when to get help. Claude declines giving any specific protocols for dosing, timing, administration, or combinations; instead, Claude can redirect the user to established harm-reduction information sources, such as dancesafe.org, tripsit.me, and psychonautwiki.org.
This is the first time a Claude system prompt has included URLs that were not hosted on claude.com or anthropic.comor claude.ai - I know because I ran a script against every other system prompt on record.
I wonder if dancesafe.org, tripsit.me, and psychonautwiki.org are about to get a material uptick in visits from Claude users.
Reliable cutoff date of June 2026
The Fable 5.1 model documentation lists both the reliable knowledge cutoff and the training data cutoff as June 2026. The system prompt provides this directly to the model:
Claude's reliable knowledge cutoff, past which it can't answer reliably, is the end of Jun 2026. It answers the way a highly informed individual in Jun 2026 would if talking to someone from {{currentDateTime}}, and can say so when relevant.
That’s the only instance of the {{currentDateTime}} macro and it comes just a few lines from the end of the system prompt, which makes sense from a caching perspective.
How I’m tracking these prompts
A few months ago I built a Git timeline of changes to their prompts, based on scraping their documentation. Today I had Fable 5.1 build a much better version of that.
My collection now lives in the simonw/claude-system-prompts repository on GitHub. It includes copies of the system prompts shared in the Anthropic documentation, but then takes extra steps to make them as easy to compare as possible.
Each model family gets a file with the system prompt for the most recent release in that family. Each of those files has a synthesized commit history with commits that have been back-dated to the dates of the previous prompts. Here are those history pages for claude-fable.md, claude-opus.md, claude-sonnet.md, claude-haiku.md.
There are similar files for each specific model version, with artificial commits for each time the system prompt for the model was changed without releasing a new version number. Opus 4 for example was updated twice, and the commit history for the claude-opus-4.md file shows each of those changes.
Combined, this gives us all sorts of ways to compare prompts directly in the GitHub interface. Here’s what changed between Fable 5 and Fable 5.1, and here are the changes made to Haiku 4.5 on January 18th 2026.
Reading diffs can be a bit tiresome... and LLMs are really good at reading diffs. I hooked up some automation using GPT-5.6 Luna to create bullet-point summaries of each of those changes, which can be previewed in the README or browsed in full in the CHANGELOG.md file - also available as as an Atom feed.
Here’s how Luna summarized all of the changes between Fable 5 and Fable 5.1:
Claude now refuses reproduction of protected visual works and recognizable characters, including code-generated art, while offering genuinely unrelated originals.
Copyright restrictions now expressly ban reproducing lyrics, poems, and book passages in any amount, with persistent refusal after an initial decline.
Drug guidance is reframed: Claude may provide overdose signs, dangerous interactions, and harm-reduction sources while refusing dosing and production protocols.
The prompt drops explicit anti-dependency rules against thanking users for reaching out, inviting continued conversation, or reiterating willingness to talk.
Claude need not apologize to unnecessarily rude users or become submissive, replacing the prior warning-and-end-conversation procedure.
Why use Luna for this? Partly because it’s cheap and I have a dedicated GitHub Actions API key (with a spending limit) for it already, but mainly because I don’t trust Claude to summarize its own system prompts when there’s a risk that material from its system prompt might impact its opinions.
Fable 5.1 wrote the prompt used by Luna, which you can see here. It starts like this:
You are summarizing one commit in a git repository that tracks the system prompts Anthropic publishes for Claude on claude.ai. The diff shows how the prompt changed from the previous model or revision to this one, using word-level markers: [-removed-] and {+added+}. The diff is followed by the full text of the previous prompt and of the new prompt; use them to check whether something that looks added in the diff already existed before.
Pick out only the most interesting changes: new rules or behaviors, rules that were dropped or loosened, anything surprising, and anything that reveals a new policy or product direction. Skip routine changes that every new prompt makes: updated model names and IDs, the knowledge cutoff date, product lists, settings lists, typo fixes, and rewordings that do not change meaning.[...]
The system is operated by a GitHub Actions workflow, which runs once a day or can be triggered manually.
Claude Fable 5.1 built the entire system, and wrote every line of automation code and almost all of the documentation.
I exported the transcript from building the system using my claude-code-transcripts tool and published it here, if you want a blow-by-blow account of how it all came together.
Quote 2026-08-31
325 #kakapo! The chicks from this year’s record breeding season are now juveniles and so have been added to the population. In 1995 there were just 51 kākāpō left. Recovery of critically endangered species ispossible with sustained effort.
Andrew Digby, providing the best news of the year
Link 2026-08-31 Introducing wrapture:
New from Graham Dumpleton (of wrapt, mod_wsgi, and New Relic’s Python agent fame), who describes Wrapture as taking the monkeypatching ideas from wrapt and extending them to apply to testing and tracing at the same time.
Wrapture (full documentation here) makes it easy to wrap any function or method such that all access can be traced, or can be overridden to return a different value.
It acts as both an alternative to unittest.mock and a way to implement tracing against an existing project:
Attaching observation to code you do not control, recording what flows through it, and doing so without disturbing the program being watched, is a problem I have never really stopped thinking about.
Wrapture includes OpenTelemetry support and even has an entirely configuration-based mechanism for adding tracing to an existing Python project, which looks like this:
capture = "summary"
[[observe]]
target = "domain:Calculator"
name = ["outer", "inner"]
[[sink]]
type = "jsonlines"
path = "trace.jsonl"This is still a very young project - just a few weeks old - but it’s off to a very promising start.
Interestingly, this is also Graham’s first attempt at large entirely agent-driven project:
Every line of code and documentation in wrapture was written by an AI assistant working under my direction. I want to be upfront about that, and equally upfront about what it was not. This was not vibe coding, where a one-shot prompt produces a pile of generated code and the person driving hopes for the best because they lack the knowledge to judge what came back. Vibe coding has earned its bad reputation. I engineered wrapture carefully from the start. I have spent a long time in this particular corner of Python and knew exactly what the result needed to be, and the AI was the means of producing it rather than the source of the design.
In a follow-up post, Unit testing with wrapture, Graham shows the testing patterns supported by the new library:
def test_stub_with_wrapture(): with wrapture.binding( Gateway, "charge" ).on_call.returns({ "id": "stub", "amount": 0} ): assert OrderService().place( 500 )["id"] == "stub"And this neat example of a test that calls and then modifies the return value from the original method:
def test_pinned_result_with_wrapture(): charge = wrapture.binding( Gateway, "charge" ) charge.on_call.transforms_result( lambda r: {**r, "id": "ch_TEST"} ) with charge: assert OrderService().place( 500 ) == { "id": "ch_TEST", "amount": 500 }(In both of these examples the OrderService().place(...) method calls Gateway().charge(...).)
Link 2026-09-01 Python 3.15.0 candidate 2 is here!:
Hugo van Kemenade (release manager for Python 3.14 and 3.15) announces the final release candidate for Python 3.15, scheduled for release in October:
Entering the release candidate phase, only reviewed code changes which are clear bug fixes are allowed between this release candidate and the final release. [...]
We strongly encourage maintainers of third-party Python projects to prepare their projects for 3.15 during this phase, and publish Python 3.15 wheels on PyPI to be ready for the final release of 3.15.0, and to help other projects do their own testing. Any binary wheels built against Python 3.15.0 release candidates will work with future versions of Python 3.15.
Back in 2021 I found a bug in Python 3.10 by running my test suites against it... but I hadn’t done this during the RC period, so that bug had already shipped! Since then I’ve always paid much closer attention to these RCs.
The new RC isn’t available for GitHub Actions just yet - keep an eye on actions/python-versions for that. For the moment though you can add this to a testing matrix:
strategy: matrix: python-version: ["3.14", "3.15"]
steps: - uses: actions/setup-python@v7 with: python-version: ${{ matrix.python-version }} allow-prereleases: true check-latest: trueThe allow-prereleases and check-latest flags mean that today this will test against RC1, and when RC2 lands it will automatically switch to that version (and then the stable version once that comes out.)
Update: Datasette passes, sqlite-utils passes, LLM is currently blocked waiting for a 3.15 wheel for scikit-learn, which is optionally used in the test suite.
Release: datasette-mcp 0.2
"rows"fromexecute_sqlis now an array of objects. Previously it was an array of arrays. This should help weaker models avoid losing track of which positional array element maps to which column. #1Now depends on
mcp>=2.1.1.
This is the first non-alpha release of the plugin. I’m confident it’s ready as I’ve been using it quite a bit myself.
Quote 2026-09-01
They took the letters from me! I have to talk about dwarf behavior now. I can’t even talk about dwarf AI. It doesn’t exist. It’s dwarf behavior, and they misbehave sometimes
Tarn Adams, co-creator of Dwarf Fortress
Tool: GeoJSON Map Viewer
I was helping Natalie gather some maps of local political boundaries (for the Granada Community Services District and the Midcoast Community Council) and found a need to display some GeoJSON files on a map and export that as a PNG. I asked GPT-5.6-Sol for suggestions of tools and it proactively built one. After some iterations using Claude Code for web and Fable 5.1 we got to this finished tool.
As for the GeoJSON.. it turns out if you ask ChatGPT Work to provide boundaries for almost anything it will churn away extracting and combining files from different Government data sources and build exactly what you need.
I got this polygon from:
I want a polygon that represents the exact boundary of the El Granada GCSD
And this one from:
Get me a GeoJSON file for the boundary (or boundaries if that makes sense) for the MCC - Midcoast Community Council - that operates near Half Moon Bay CA
Here’s a link that displays both of them at the same time on the new GeoJSON map viewing tool.
Note 2026-09-01
I was poking around in my ~/.cache/ folder using OmniDiskSweeper when I spotted something interesting. The OpenAI Codex desktop app (since rebranded to just ChatGPT) has 1.7GB of stuff in there in a folder called codex-primary-runtime, including a full Python installation, a full Node.js installation, and native binaries for Poppler, git, and the LibreOffice open source office suite (which forked from OpenOffice.org in 2010):
The ~/.cache/codex-runtimes/codex-primary-runtime/plugins/openai-primary-runtime/plugins/documents folder includes skills which tell Codex how to find and use those binaries.
Quote 2026-09-02
Direct2D has always been the biggest hurdle for Paint.NET on WINE, and it’s clear that it will never be completed enough for Paint.NET’s use. And I can’t just “disable” the use of Direct2D. So, instead, Paint.NET now has an internal, from-scratch, clean-room reverse-engineered rewrite of Direct2D that it uses on WINE (triggered by using /wine). It lives in PaintDotNet.Windows.Direct2D1.Managed.dll. This was written by our good friend Claude, without whom this would NOT have been possible and would NEVER have happened. [...]
Most of this code is, as they say, “vibe coded.” By that I mean that it has not been thoroughly reviewed, it’s more “trust me bro” style. I cannot possibly review 180,000 lines of code, it’s just way way way too much. For reference, the rest of Paint.NET is about 700,000 lines of code and I’ve been working on it for over 20 years. [...]
At times, Claude was working with the fury of 10 freshly unshackled Einstein genius-level 10x coders. And other times ... well, not so much. I had to babysit Claude quite a bit to make sure it did resource management correctly (for awhile it just wasn’t doing the COM equivalent of AddRef() for reference counted objects, oops). I had to slap it a few times when I found some really bad design or architecture decisions. And I was also impressed at some rather clever and tireless reverse engineering work it did to figure out all the formulas needed for implementing Direct2D’s built-in effects library.
Rick Brewster, author of Paint.NET
Release: llm-gemini 0.34
New model
gemini-3.8-flashfor Gemini 3.8 Flash, with low, medium and high thinking levels. #146Fixed async responses failing to record the resolved model version. Thanks, Charlie Tonneslan. #137
Google released Gemini 3.8 Flash (and 3.8 Flash Cyber, but that’s available to “trusted defenders” only) today.
Here are the pelicans for high, medium, and low. This is high:
For comparison, here are the same pelicans generated using Gemini 3.7 Flash.
Something I appreciate about Gemini Flash is that it’s fast, cheap, and competent at things like HTML and JavaScript. I was messing around with it and prompted “make me a cool thing in html” and it built this, which is certainly a cool thing in HTML! Took 13 seconds, cost 1.8 cents.
If you click through to the demo you’ll see one more thing I built with Gemini 3.8 Flash.
My markdown-svg-renderer tool lets me feed in the URL to a Gist with Markdown in and renders that markdown with fenced code blocks for SVG correctly rendered.
I used Gemini 3.8 Flash (with my very basic llm-coding-agent coding agent plugin) to add support for HTML as well, so now any HTML blocks in the Markdown are rendered using a sandboxed iframe. Here’s the transcript.
Link 2026-09-03 GPT‑6 Astra:
GPT-6 Astra is “rolling out today to a limited set of organizations and over the coming days will become available to all ChatGPT Plus, Pro, Business, and Enterprise users, as well as through the OpenAI API and AWS” - I’ve not tried it yet myself, so I don’t have a great deal to say about it yet.
It’s going to be API priced at the same rate as Claude Fable 5 and 5.1: $10/million input and $50/million output. This is clearly OpenAI’s Fable competitor, and appears to score higher than Fable on most of OpenAI’s self-reported benchmarks.
Most impressively, Astra scores 99.9% on the recent (released in March) ARC-AGI 3 benchmark - though notably Fable 5 does not yet have a published result, and the ARC-AGI blog notes that the 99.9% score was achieved for $19K using OpenAI’s custom “Provider Adapter harness”, while the default ARC-AGI harness scored 62.7% for $26K.
The Provider Adapter harness preserves opaque reasoning state between requests and uses compaction for longer conversations, allowing the model to reuse prior work.
Unsurprisingly, given the recent Hugging Face incident, Astra is a beast at security tasks. It scores 100% on ExploitBench (GPT-5.6 Sol got 78.5%), 42.4% on ExploitGym (Sol got 30.3%), and 99.2% within four attempts on SRE-Bench binary reverse engineering compared to Sol’s 68.7%.
It’s also better at long context: on OpenAI’s eight-needle benchmark it got 100% at 256K–512K tokens and 96.3% at 512K–1M tokens. OpenAI may have vanquished one of the ongoing challenges with long context processing.
It doesn’t win at everything though. Artificial Analysis note that Astra is still beaten by Fable on their Intelligence Index:
Sits beside GPT-5.6 Sol in Intelligence: GPT-6 Astra scores equal to GPT-5.6 Sol in the Index at 61. This is 5 points lower than Claude Fable 5.1 (max with fallback). The model also trails Meta’s newly released Muse Spark 1.3 (max).
It did better on their Coding Agent Index:
Leads Coding Agent Index cost efficiency frontier: At max effort, GPT-6 Astra costs about the same as GPT-5.6 Sol (max) while scoring 2 points higher on the Index. Per task, the model is less than half the cost of Claude Fable 5, for the same score.
I’ll write more about Astra once I get access to it. The API model label once it rolls out will be gpt-6-astra.
Note 2026-09-04
The August edition of my sponsors-only monthly newsletter is out. If you are a sponsor (or if you start a sponsorship now) you can access it here.
This month:
We got more details on OpenAl’s accidental cyberattacks
One-shotting Raccoon Heist games with Fable 5 and Sol 5.6
Claude auto mode
Understanding ChatGPT Work
Model releases
Miscellaneous bits and bobs
My projects
What I’m using at the moment
Here’s a copy of the July newsletter as a preview of what you’ll get. Pay $10/month to stay a month ahead of the free copy!
TIL: Using Blender with coding agents on macOS
I’ve been having fun with Blender in ChatGPT Codex on my Mac recently. Getting it to work with coding agents is really easy: install the full Mac application from blender.org and run a prompt like this:
Use the already install /Applications/Blender to render a scene of a pelican riding a bicycle
In this case I followed that up with these two prompts:
OK add a background and a lot of flair
Then:
OK make it a whole lot better
And got this image, generated using Blender’s Python API:
This was covered by my existing Codex subscription, but according to AgentsView it would have cost $4.24 at API prices for gpt-6-astra.
Link 2026-09-05 Introducing GPT-6 Astra for developers:
Blink and you’ll miss it, but there’s a familiar creature at 1m59s:
Across the board, Astra has more attention to detail, better understanding of the user’s prompt, and can build more sophisticated outputs. In particular, it excels at building 3D models. I’ve seen it make incredible renderings of gardens, shipyards, animals, cityscapes, even Dyson spheres.
Astra really does believe in putting a red neckerchief on a pelican riding a bicycle.
Quote 2026-09-06
If you continue to add floors and rooms to a building forever, it will collapse. Software faces no such constraint. The code can always get worse. There can always be a new layer of indirection or a reduction in performance.
Zach Kehs, There’s No Limit to How Bad Code Can Get
comment: There’s No Limit to How Bad Code Can Get
[In reply to a comment about burning it down to start from scratch when technical debt becomes overwhelming]
In my experience it’s so rare for that to work.
You announce the old thing is irrecoverably drowning in tech debt. You spin up a team to rewrite it from scratch. Work begins.
Meanwhile the old thing remains a moving target: it’s running the core business, so changes are still necessary. The developers working on it know that it’s going to be made obsolete by the new thing soon, so they don’t have any incentive to go beyond the smallest effort possible to add the new features. Technical debt continues to mount.
Meanwhile, the team working on the new thing are ambitious and probably a little naive. They start out at a great pace - it’s greenfield after all - but as time progresses it becomes apparent that nobody fully understands the behavior and scope of the thing they are replacing. If it was well documented and tested it wouldn’t need to be replaced, after all...
After months (or even years) without delivering value, the pressure is on to “ship it”, so the new system is launched to handle a subset of what the old system handled - or often for some new feature that was too hard to build with the now mostly unmaintained old system.
... so now you have TWO systems in production - the janky old system that nobody wants to touch, and a new system which handles just a few production features and is 80% inactive code that is meant to replace the old system, eventually.
If you’re really lucky the company won’t have lost patience with the new system and will allow that work to continue. The longer this all takes, and the longer the old system stays in production and stubbornly continues to work, the higher the risk that “priorities have changed” and the new system total replacement work is abandoned, leaving you with two systems where you used to have one.
The best article I’ve read about completing this process responsibly is Migrations: the sole scalable fix to tech debt by Will Larson.
If I run into a situation like this in the future, my strong recommendation will be to shore up the old system with as much automated testing as possible and then seeing if targeted refactors can get it to the desired shape. My hunch is that in many cases that will have a much higher chance of success than the siren call of a greenfield replacement.
Link 2026-09-06 The purpose of DNS is to spread scams:
Terence Eden shares some daunting statistics in support of his take that “the Domain Name System’s purpose seems to be a vector for criminals to run scams on people at a terrifyingly high rate”.
On this Interisle report (via Andrew Campling), Terence says:
It says 85 million new registrations of gTLDs were made in 2025. Of those 8.5 million were added to blocklists by May 2025. It reckons that a 10% abuse rate is the likely floor for these numbers and it’s probably closer to 20%. One in five newly registered domains with a gTLD are scams. That’s a bloody crisis.
I had no idea. Apparently ICANN have been discussing this problem for years.
Link 2026-09-06 Research acceleration: The view inside OpenAI:
Apparently today is RSI day at OpenAI, for Recursive Self-Improvement - I think it’s their new AGI. Both this piece and the new essay An Alien Mind (by Chief Scientist Jakub Pachocki) talk about it, and this one doesn’t even bother to expand the acronym.
Included are details on how OpenAI’s own research team are using coding agents. Like pretty much everyone else 2026 has been the year that agentic engineering really took off at OpenAI, best illustrated by this chart:
I’m intrigued at what caused that significant acceleration in AI spend per researcher in late July - my best guess is that’s when internal employees gained access to the model later released as GPT-6 Astra.
If you find this newsletter useful, please consider sponsoring me via GitHub. $10/month and higher sponsors get a monthly newsletter with my summary of the most important trends of the past 30 days - here are previews from Mayand June and July.













