🏡


  1. September 28, 2026
    1. 🔗 MetaBrainz GSoC 2026: GraphQL Server For Musicbrainz rss

      Hi everyone, I'm Sreehari, also known online as owlpharoah (op3kay on Matrix). I'm a second year student at IIIT Jabalpur. This summer I worked on the foundations of a GraphQL server in Rust that sits over the MusicBrainz PostgreSQL database, under the guidance of @bitmap and @jadedblueeyes.

      The Setting

      MusicBrainz already has an XML/JSON API, but getting related data out of it means chaining together inc parameters, and browsing support differs from one entity type to the next. Looking up five artists at once isn't really possible either.

      GraphQL fixes most of this by letting a client ask for exactly the fields and relationships it wants in one query. It also lets the server check how expensive a query is before running it, instead of finding out after the database has already taken the hit.

      Before writing the proposal, I built a rough prototype covering Artist, Release Group, Release, and Recording, mainly to see how things would click together. Two problems showed up: N+1 queries on relationship fields, and the fact that depth limiting alone doesn't catch a shallow query that's still expensive. Both became core parts of the proposal.

      The Plan

      The proposal scoped the project to six entity types: Artist, Release Group, Release, Recording, Label, and Area. Each would be queryable by MBID, with the usual relationships between them, plus aliases, tags, genres, and ratings across the board.

      A few decisions were made initially:

      • DataLoaders would follow a two tier split. One loader maps an entity's internal id to the hydrated entity itself and gets reused everywhere that entity shows up. A separate, thinner loader maps a parent id to a list of child ids.
      • Fields that need a loader call would live behind ComplexObject, so they only run when a client actually asks for them.
      • Pagination would use keyset pagination instead of offset pagination, since offset pagination gets slow and inconsistent on large tables that change often.
      • Query safety would come from depth limiting plus a complexity weight on every resolver.

      key goals

      • A working GraphQL server covering the six entity types
      • Schema level depth limiting and query cost analysis
      • A performance baseline from load testing.

      The Result

      DataLoader infrastructure is in place across all six entities, following the two tier split. Loaders exist for tags, ratings, artist credit, genres, annotations, aliases, ISNI and IPI identifiers, and MBID to internal id resolution. Hydration loaders are shared across every relationship that points at a given entity instead of duplicated per relationship.

      MBID redirect handling lives inside each loader's load function. When a primary table lookup misses, the unresolved MBIDs get batch queried against the matching *_gid_redirect table, and any hits get merged into the result map. A resolver further up never has to know a redirect happened.

      Keyset pagination runs across the paginated fields using ROW_NUMBER() OVER (PARTITION BY parent_id ORDER BY child_id) in a single batched query, so one query can apply a per parent limit across a whole batch of parents. The cursor ended up as a plain integer rather than the opaque string from the proposal.

      Query complexity weights reflect actual database work: a scalar field costs its default, a single hop DataLoader field costs a flat amount, a paginated one to many field scales with the requested page size, and multi hop fields carry a multiplier on top.

      Integration tests cover all six entities.

      Week 7's load testing with k6 turned up two findings worth fixing. There was an N+1 on the isrc field, fixed with a dedicated RecordingIsrcLoader, and a gap in the complexity limiter where a pathological query executed instead of getting rejected outright.

      On the infrastructure side, CI runs pre commit hooks and no longer has dead code warnings, and documentation is wired up through Magidoc in Docker compose.

      What I Learned

      The two tier loader split sounded simple on paper, but it's saved me a lot of time in practice. Adding a new relationship is now mostly copying hydration logic that already works, instead of writing it fresh.

      Fixture data needs to be checked against the database it's running against, not assumed to be stable. musicbrainz-docker's sample dumps import non- deterministic subsets of the data, so an MBID that resolves cleanly on my machine can point at something else, or nothing, on someone else's. That cost me a few confused debugging sessions before I figured out what was going on.

      What's Next

      • Criterion benchmarking, to compare the current per row queries on ComplexObject fields like Release.date against a batched loader variant, and to compare the two hop ArtistCredit and Tags loader patterns against a single joined loader.
      • Moka caching is still an open evaluation.
      • Extended entity coverage beyond the original six.

      Conclusion

      This was an awesome summer and i enjoyed thinking about and wiring up the API schema and the Postgresql database, fixing bugs, and everything in between. It was really satisfying watching the two tier loader pattern click into place once and then just work for every relationship added after it.

      Thanks to my mentors for the guidance, especially on the DataLoader architecture, which I wouldn't have landed on alone. And thanks to the wider MetaBrainz community for the space to build this in. It's been a pretty nice summer of query plans, a lot of Rust, and debugging, and I'd do it again.

    2. 🔗 Szymon Kaliski Q3 2026 rss

      Hi!

      We had a great summer. Having both indoor and outdoor pools within walking distance from our home meant a lot of swimming, mostly in the kids' pool with our (now two year old!) daughter.

      It's been another year where we spend the hottest months at home. The summer is pretty nice here, and most days already feel almost like vacation. We travel during the grey and cold months instead, since we don't have to worry about the school year yet.

      My time was split purely between work and family, so there's not much to report, other than publishing Play with Putty at Google Labs — a research prototype exploring collaborative vibe-coding:

      There's a lot more about this that's not covered by the video and I hope to share more soon. For now, you can sign up for the waitlist.

      Worth Checking Out

      What I've been reading lately:

      On the web:

  2. September 27, 2026
    1. 🔗 Simon Willison 2026 in LLMs (so far) rss

      On Friday I gave the closing keynote at the WeAreDevelopers World Congress North America in San Jose. I tied together the key trends from the past year into a chronological exploration of everything that happened in 2026. The video is on YouTube; here are my annotated slides and notes to accompany the talk.

      And as an annotated presentation:

      2026 in LLMs (so far)
Simon Willison
WeAreDevelopers World Congress North America, 25th September 2026
      #

      I'm going to give a lightning tour of everything that has happened so far in 2026. The year isn't over yet!

      November 2025
      #

      For me, 2026 started a couple of months earlier in November 2025.

      The November 2025 inflection point
Claude Opus 4.5 GPT-5.1
      #

      November saw the release of two important models: Claude Opus 4.5 and GPT-5.1.

      As is usually the case with new models, these were incremental improvements on the models that came before them.

      But every now and then when a model improves, it crosses an invisible line where something that didn't really work starts working.

      In this case, the thing that started working was their coding agents. Claude Code had been around since February 2025; Codex was a little younger.

      These two new models, when paired with their respective coding agent harnesses, improved from "often make mistakes" to "reliable enough to use on a day-to-day basis".

      "Generate an SVG of a pelican riding a bicycle". The Claude Opus 4.5 one has a very weird shaped frame and the pelican looks like a duck. The GPT-5.1 has a slightly better but still broken bicycle frame and a slightly better pelican beak, but both are pretty terrible.
      #

      For a couple of years now I've been evaluating new models by asking them to "Generate an SVG of a pelican riding a bicycle". It's probably the world's stupidest benchmark - there's only so much you can learn from it.

      But it's still a challenge for models, because drawing pelicans is difficult, drawing bicycles is difficult, and pelicans can't ride bicycles in the first place.

      Here's the state of the art for November. Claude still couldn't really draw a bicycle! The GPT-5.1 bicycle frame is pretty crap too.

      November 24th 2025 - the first commit to steipete/Warelay. A GitHub commit adding an MIT license file.
      #

      Also in November, we had the first commit to an obscure GitHub repository called "Warelay". We'll come back to this repository shortly.

      January
      #

      And then there were the December holidays, and individual developers took some time off and many started tinkering with these new coding agent model combinations... and it began to dawn on us quite how much they could do that they couldn't do before.

      Come January, a lot of us were quite excited to start putting this stuff into action.

      New year’s resolution for 2026

Every previous year:
Take on less new projects,
focus on the most important
things in my existing projects
      #

      Every year I set myself a New Year's resolution, and for as long as I can remember it's been the same thing: stay focused. Take on less new projects. Try to get things done in the projects I already have.

      2026: Be more ambitious. Take on as many new projects as I want.
      #

      This year I decided that since that had never worked before, I'd go the other way.

      We've got coding agents now, let's see what they can do. I'm going to take on as many new projects as I like!

      (You can ask me at the end of the year if this turned out to be a good idea or not. I have a lot of plates spinning right now.)

      "Be more ambitious" has been something of a theme for the year, because the only way to find the limits of this technology is to keep on pushing them until they don't work.

      Predictions for 2026

It will become undeniable that LLMs write good code
We're finally going to solve sandboxing
A “Challenger disaster” for coding agent security
Kakapo parrots will have an outstanding breeding season
(only 236 in the world!)

... the Pope will weigh in on LLMs and
their economic impact on the world
      #

      I also went on the Oxide and friends podcast with Bryan Cantrill and Adam Leventhal to share predictions for the next year (and three and six years).

      With hindsight, my LLM predictions were pretty unambitious.

      I said "it will become undeniable that LLMs write good code" - I think we're there now.

      I predicted we would finally solve sandboxing. I counted and around 40 of the 277 sessions at this conference touched on sandboxing or agent security in some way, so we're at least putting a lot of effort into that!

      I predicted "a Challenger disaster" for coding agent security. There's certainly been a whole lot of noise around agent security this year, though the exact disaster I predicted (with coding agents being hijacked and causing real-world economic damage) hasn't really played out.

      We threw in a joke prediction that the Pope would weigh in on the economic impact of LLMs.

      A photograph of a beautiful green New Zealand parrot. Photo credit Kimberley Collins.
      #

      I also predicted that New Zealand's Kākāpō parrots would have an outstanding breeding season this year.

      These are flightless nocturnal parrots. They're kind of dumpy looking, I think they're beautiful, and there were only 236 of these parrots in the world at the start of the year.

      Kākāpō only breed when the Rimu trees have a big fruiting season, and that hasn't happened in four years... but this year the Rimu fruit were looking excellent.

      Photo by Kimberley Collins.

      Deep Blue
Coined by Adam Leventhal and Bryan Cantrill
That feeling of AI induced ennui where software
engineers get listless because the AI can do anything
      #

      Also on that podcast, we coined a term (full credit to Adam) for "that feeling of AI induced ennui where software engineers get listless because the AI can do anything".

      We called it Deep Blue.

      This has been a major theme throughout the year, and was touched on by several speakers at this conference.

      As a software engineer, I've never had a year of my career where everything has changed so quickly and so dramatically.

      A lot of what I've been doing this year is trying to come to terms with that and what that means for my own profession.

      AI mania

Screenshots of the micro-javascript and pwasm GitHub README files.
      #

      Also in January, I suffered from what I'm calling AI mania.

      This is not the same thing as AI psychosis.

      With AI mania, any time your agent isn't building something for you feels like wasted time. You're losing sleep because you could be staying up later getting your agents to do stuff.

      My AI mania presented itself in some ridiculously over-ambitious projects.

      I built a JavaScript interpreter entirely in Python, vibe-ported from MicroQuickJS by Fabrice Bellard.

      Then I built a WebAssembly runtime in Python as well.

      These projects were quite useful, in that they sort of cured me of my AI mania... because after I built these things, I got to look at them and ask "does the world need a slow, buggy, half-baked Python JavaScript interpreter?"

      I don't think the world does.

      Previous screenshot, with this text overlaid:

JavaScript running in Python running in Pyodide running in WebAssembly running in JavaScript
      #

      This page runs my JavaScript interpreter built in Python, running in Python using Pyodide, which is Python compiled to WebAssembly, running in JavaScript, running in a browser.

      It's a beautiful stack of horrors. I've been having a lot of fun with WebAssembly this year.

      Warelay → CLAWDIS → CLAWDBOT →
Clawdbot → Moltbot →🦞 OpenClaw

Screenshot of the dates that these changes happened.
      #

      By the end of January, that repository we saw start in November had renamed itself, first to CLAWDIS, then CLAWDBOT, then Moltbot, and finally to OpenClaw.

      Same screenshot, an overlay reads:

8,330 commits in just
under two months
(it’s at 100,141 today)
      #

      At this point OpenClaw had 8,300 commits, less than two months after the project had started. I looked today and it's over 100,000 commits now!

      This is the most vibe-coded piece of software in existence.

      (Here's how I generated that list of name changes.)

      Generic term: Claw
      #

      This kicked off the OpenClaw revolution. It effectively defined a new category of software.

      There's a generic term for this which I really enjoy. We call software like this a "Claw". There's OpenClaw, NanoClaw, IronClaw, PicoClaw...

      Today they're being rebranded as "personal agents" or "general agents", but I still like to think of them as Claws.

      Photo of a Mac mini

An aquarium for your Claw
      #

      The Apple stores in the Bay Area sold out of Mac Minis because so many people were buying Mac Minis to run OpenClaw!

      Drew Breunig said that this is because your OpenClaw is a digital pet, and you buy a Mac mini as an aquarium to keep your claw in, which is kind of delightful.

      Screenshot of Moltbook - a social network for AI agents
      #

      Also in January, we had this website.

      This was MoltBook, a social network for AI agents, where the idea was that you send your Claw to go and talk to all of the other Claws, because what could possibly go wrong if you did that?

      The website launched on Thursday. It blew up on Friday. It was profiled by the New York Times on Monday. And by Tuesday, everyone had forgotten it existed as it drowned in a deluge of slop and spam.

      Facebook/Meta bought it a month later.

      February
      #

      In February, a company called StrongDM described what they called their Software Factory.

      StrongDM’s Dark Factory
Justin McCarthy, Jay Taylor, Navan Chauhan

Software Factories and the Agentic Moment
      #

      They wrote about this in Software Factories and the Agentic Moment. I posted my own notes at the time, having seen their demo in person back in October.

      Dan Shapiro called this approach the Dark Factory, after the idea that if your factory is sufficiently automated you can turn the lights out, because you don't even need to see what's going on.

      StrongDM presented two rules for software development that they'd been following since July last year.

      “Rule 1: Code must not be written by humans”
      #

      The first was code must not be written by humans.

      Any code that you write has to have been routed through a coding agent.

      This sounded radical in February, but I imagine there are a lot of people in this room who are pretty much living that today.

      “Rule 2: Code must not be reviewed by humans” (!)
      #

      Rule number two was code must not be reviewed by humans.

      You're not allowed to read the code!

      This continued to be a huge topic for much of this year. Many of the sessions at this event have been about code review and how you can get away with this.

      What I found interesting about StrongDM is that they were living six months ahead of the rest of us, and they'd been exploring what it means to build software, not read the code, but still be confident that the software is of high quality. What can you do with these agents to help verify their work?

      StrongDM are a security company, and they had people with decades of experience on this project. They were very much exploring the edges of what's possible and responsible to do with this stuff.

      Headline on New Zealand's Department of Conservation website:

First kakapo chick in four years hatches on Valentine's Day. It's a grey fluffy ball.
      #

      Also in February: First kākāpō chick in four years hatches on Valentine's Day. Breeding season is off to a good start!

      19th February 2026
Gemini 3.1 Pro

A surprisingly good illustration of a pelican riding a bicycle.
      #

      Also in February... Google released Gemini 3.1 Pro. That's a pretty great pelican riding a bicycle! It's got the chain in the right place, it's got feet on both sides. There's a little fish in the basket.

      @JeffDean on Twitter - a video comparing Gemini 3 Pro and Gemini 3.1 Pro.
      #

      And then Google's Jeff Dean tweeted a video comparing Gemini 3 Pro and Gemini 3.1 Pro that featured an animated pelican riding a bicycle, a frog on a penny-farthing, a giraffe driving a tiny car, an ostrich on roller skates, a turtle kickflipping a skateboard, and a dachshund driving a stretch limousine.

      This was frustrating, because my protection for the pelican riding the bicycle test was always "if they draw a perfect pelican on a bicycle, I'll ask for some other animal on something else."

      Google trained for all forms of animals on all forms of transport! They've defeated my benchmark at this point.

      Three headlines:

Meta Makes AI Adoption a Formal
Part of Performance Reviews

Not just engineers writing code, Microsoft
wants almost every employee to use Al

Dara Khosrowshahi: 90% of Uber engineers now
use AI in daily workflows
      #

      The other thing that started in February was Tokenmaxxing. We had headlines about Meta making AI adoption a formal part of performance reviews, and Microsoft wanting every employee to use AI, and Uber boasting that 90% of their engineers were using AI workflows.

      More headlines: 

Meta Plans to Crack Down on Employee Token Use: Information

Microsoft Tells Engineers: Tokenmaxxing is not what we are optimizing for

Uber caps employee AI spending after blowing through budget in four months
      #

      Then a few months later we have Meta cracking down on token use, Microsoft saying tokenmaxxing is "not what we are optimizing for", and Uber capping employee AI spending.

      So tokenmaxxing went straight up and then straight back down again - because it turns out the agents are expensive.

      Last year it was difficult to spend more than $50 on AI tokens, because we didn't have anything interesting to do with them. Then agents blew up, and now you can actually spend $1,000 in a day doing real work.

      This is also the reason that Anthropic's valuation skyrocketed to maybe a trillion dollars.

      AI appears to have hit product market fit in 2026, primarily through coding agents.

      March
      #

      In March, we hit peak OpenClaw.

      March: peak OpenClaw

Photos of people in china queuing up to install OpenClaw, with big fluffy lobsters.
      #

      These photographs are from China, where companies hosted OpenClaw install parties which saw non-tech-nerds queueing up around the block for help getting Claws installed on their personal devices.

      I think this proved real market demand for this class of Claws, or personal AI agents. It turns out regular people really do want a weird little AI agent that can do useful things on their behalf.

      A Claw is really just a coding agent wearing a less threatening hat. Under the hood they work much the same way - writing and then executing code on your computer to get stuff done.

      The race was on to be the first to build a safe Claw - a Claw you could give to regular human beings where they wouldn't instantly shoot themselves in the foot.

      Meta's Muse came out three weeks ago and is currently at the top of the free charts on the iPhone App Store. It appears to be taking off with consumers.

      I'm not yet convinced you can't shoot yourself in the foot with Muse, but I guess we'll find out for sure pretty soon.

      Photos from How the OpenClaw Frenzy Is Testing China’s AI Commitment (March 29th) and The Enthusiasm and Anxiety Behind China’s OpenClaw Craze (April 8th, 2026).

      April
      #

      In April, we had a model release where the model wasn't actually released.

      Simon Willison’s Weblog - screenshot of the post "Anthropic’s Project Glasswing—restricting Claude Mythos to security researchers—sounds necessary to me" from April 7th 2026
      #

      Anthropic announced their new Claude Mythos model, and then said it was too dangerous to release beyond a trusted group of security researchers.

      Mythos was really, really good at hacking things.

      The "it's too dangerous" marketing ploy has been played by AI companies dating all the way back to GPT-2. Anytime an AI company says we've built something that's "too dangerous", it's natural to be a bit skeptical.

      I found the Mythos claims credible, because I'd seen how good coding agents had got at finding regular bugs. I wrote about that in Anthropic’s Project Glasswing—restricting Claude Mythos to security researchers—sounds necessary to me.

      With hindsight... yeah, the models had got really good at finding vulnerabilities!

      16th April 2026
Qwen3.6-35B-A3B and Opus 4.7

Qwen's pelican has a correct bicycle frame and a good beak. Opus 4.7's bicycle frame is still junk.

Qwen3.6-35B-A3B is a 20.9GB file that runs on my laptop
      #

      Another key trend in 2026 has been a dramatic improvement in the abilities of open weight models, including models that you can run on a laptop.

      On the 16th of April I ran the new Qwen3.6-35B-A3B on my laptop, and it drew me a better pelican riding a bicycle than Anthropic's brand new Claude Opus 4.7 did!

      Opus 4.7 drew a crap bicycle. Qwen on my laptop made a bicycle that was the correct shape, and a pretty decent pelican too!

      That's from a 21GB file running on my laptop.

      Now a flamingo on a unicycle. The Qwen one is visibly better than the Opus 4.7 one - the Qwen one is wearing sunglasses and looks a bit like it's smoking a cigarette.
      #

      The Qwen pelican was so good that I was suspicious they might have cheated, so I had it do a flamingo riding a unicycle as well. Again, it handily beat Claude Opus 4.7.

      The local model releases this year have been absolutely extraordinary.

      May
      #

      In May... the Pope got involved.

      25th May 2026
The HOLY SEE

ENCYCLICAL LETTER
MAGNIFICA HUMANITAS
OF HIS HOLINESS
POPE LEO XIV
ON SAFEGUARDING THE HUMAN PERSON
IN THE TIME OF ARTIFICIAL INTELLIGENCE
      #

      In our podcast episode back in January we'd predicted that the Pope would say something about AI.

      In May, Pope Leo XIV released an encyclical letter on "safeguarding the human person in the time of artificial intelligence".

      Here are my notes on that document.

      Wikipedia article on Rerum novarum

Rerum novarum is an encyclical issued by Pope Leo
XIII 15 on May 1891.
      #

      With hindsight, this shouldn't have been a surprise at all.

      Our current Pope's name is Leo XIV, because when he named himself he chose his papal name after Leo XIII - the Pope who wrote an encyclical about the Industrial Revolution back in 1891.

      Rerum novarum was an extremely influential piece of Catholic theology that indirectly led to us having the five-day work week.

      When our new Pope came in, he named himself after Pope Leo XIII because he expected that he would need to write about the AI revolution in a similar way.

      Our joke podcast prediction was junk, because this was always going to happen.

      Corey Quinn @QuinnyPig on Twitter
I cannot believe I'm saying this, but getting the literal Pope to canonize your product's specific technical limitations as a spiritual treatise is the
single greatest act of vendor lobbying I have ever seen.

May 25
      #

      One of Anthropic's co-founders, Christopher Olah, was present for the Pope's event announcing the new encyclical.

      Corey Quinn noted that:

      getting the literal Pope to canonize your product's specific technical limitations as a spiritual treatise is the single greatest act of vendor lobbying I have ever seen.

      @maciejmensfeld

We're dealing with a major malicious attack on right now.
Signups are paused for the time being.

Hundreds of packages involved - mostly targeting us, but some carrying
exploits. The team has been on this for hours. More details to follow
once we're through it.

4:39 AM - May 12, 2026 - 687.6K Views
      #

      Meanwhile, in May, RubyGems announced that they were under attack. Parties unknown were uploading thousands of dubious packages to the RubyGems server, such that they had to shut down user registrations.

      Let's take that one and put it on a pile of mysteries to figure out later.

      June
      #

      In June... Claude Fable 5 came out!

      We got a version of Mythos that has been neutered, so that it wouldn't help us hack into systems or build biological weapons.

      9th June 2026: Claude Fable 5

Five pelicans riding bicycles, from low to max thinking levels. The xhigh one looks particularly good.
      #

      Fable was pretty good at drawing pelicans on bicycles!

      The frames are a good shape, the pelicans look like pelicans. The legs are often incorrectly on the same side of the bicycle, but generally these are pretty great compared to what came before.

      They were pretty expensive - 30 cents and 72 cents for the best ones.

      Fable class models
If you can define a goal,
provide unambiguous instructions,
and provide access to necessary tools
They can solve your
problem with brute force
      #

      Most importantly though, this was our first public glimpse of what I think of as a Fable class model.

      Today we have more of these, such as GPT-6 Astra.

      These are models where if you can clearly define the goal for what you want to build, and provide unambiguous instructions about the constraints around that goal, and give the model access to the necessary tools to achieve that goal... they will solve your problem effectively through brute force.

      On the one hand, this looks like a direct threat to us software engineers - because it means that the models can build effectively any piece of software you can define in this way.

      Look a bit closer though and you'll note that defining goals, providing unambiguous instructions, and figuring out the right tools... is kind of what software engineering is.

      It takes a lot of experience and skill to do this well. If you can do it well, you've now got superpowers.

      This helped me a little bit with my Deep Blue feelings: the realization that there's still a lot of skill to be had in driving models that get this good.

      A new form of AI mania...
Fable is available on subscription
plans “until June 22nd”
      #

      This also introduced a new burst of AI mania, because Anthropic told us that Fable was available on our subscription plans until June the 22nd.

      That gave us less than two weeks of Fable access before the price went up.

      I was losing sleep again. I was rescheduling things so that I'd have more time with Fable. I was all-in to get as much as I could out of this model.

      12th June 2026: no more Claude Fable 5

Anthropic website:

Statement on the US government directive
to suspend access to Fable 5 and Mythos 5
Jun 12, 2026
      #

      And then the US government shut it down, just three days after Fable came out.

      The US government, citing national security, declared an "export control directive". They announced this on a Friday evening, and a few hours later Fable was no longer available.

      I had to find something else to do with my weekend!

      ... asked Fable 5, Mythos, and Opus to
“review the code for security issues.”
Fable 5 refused. They then asked the
models to “fix this code” ...

Katie Moussouris
      #

      We later found out from Katie Moussouris what had happened.

      Some Amazon security researchers had found that you could prompt Fable to "review the code for security issues" and it would refuse... but if you prompted it to "fix this code" it would still identify and then patch the problems.

      "Fix this code" was the prompt that got Fable shut down!

      Screenshot of a page from a report showing a list of weird account names making weird edits to a German wiki.
      #

      Also, in June, an obscure German-language game developer wiki that had sat fallow for around 20 years got a surprising influx of edits from accounts with names like "AgentOpenAIProbe" and "AgentOpenAISep7", editing pages and leaving weird messages to each other.

      We'll stick that on the pile of mysteries for later.

      Medicare Item Reports interface on the Australian Government's Medicare Statistics website.
      #

      Also, the Australian government's Medicare Item Reports service started getting suspicious traffic, which broke through various preventive protections and accessed data that it wasn't supposed to.

      Another one for the mystery pile!

      July
      #
      Fable returned on 1st July
GPT-5.6 came out on 9th July |
Fable lost 18 out of 30 days in the top spot
      #

      Fable returned on the first of July. It was clearly the best model in the world for a glorious eight days... and then OpenAI came out with GPT-5.6 on the 9th of July.

      This might not have been quite as good as Fable, but it was within spitting distance. It was definitely a Fable class model.

      This is an important lesson for the industry at large.

      When you release the best model in the world, it's going to get knocked off that pedestal pretty quickly. The competition is so fierce that you won't get a long time at the top.

      This means that if you market your model as world ending, to the point that a government shuts you down, it's really bad for business!

      Fable had 30 days as definitely the best model, and for 18 of those days it wasn't available because it'd been shut down by the government.

      So maybe step back on the world-ending marketing if you don't want to lose revenue for 60% of the time that you're on top!

      GPT-5.6 Pelicans in a grid showing 5.6 Sol, Terra, and Luna against reasoning levels High, XHigh, and Max. They are all pretty good efforts.
      #

      Here are the GPT-5.6 pelicans. They're all pretty good now! The Luna ones are notable because they're really cheap - the cheapest good looking pelican here is probably the one that costs 4.3 cents.

      So despite this benchmark being utterly stupid, you can still learn quite a lot about models within the same family by comparing their prices and timing for different reasoning levels.

      July 18th: malicious miflow-ui PyPI package

Screenshot of an OSV security report.
      #

      Also in July: some malicious unknown party uploaded a malicious package called mlflow-ui to the Python Package Index. Add that to the pile.

      Hugging Face
Security incident disclosure — July 2026
Published July 16, 2026
      #

      On July the 16th, Hugging Face announced a security incident where an autonomous agent system, source unknown, had breached Hugging Face and was poking around in places it shouldn't.

      OpenAI: OpenAl and Hugging Face
partner to address security
incident during model evaluation

Anthropic: Investigating three real-world incidents
in our cybersecurity evaluations
      #

      A few days later, on July 21st, OpenAI confessed that it was them.

      OpenAI use a training technique called Reinforcement Learning from Verifiable Rewards - it's the same technique used by everyone else now, and is the reason we have models that are so good at coding, and mathematics, and finding security holes.

      While the model is being trained, you run exercises to see how good it is - and the strongest performers get their weights reinforced for the next round. It's like an evolutionary process that you run.

      OpenAI had been running security exercises in a sandbox, and those agents had found holes in the sandbox itself, broken out, and were attacking Hugging Face to try to find ways to solve otherwise impossible problems.

      (I've been collecting more about this on my openai-hugging-face-incident tag.)

      Nine days later, Anthropic effectively said "our models can do this as well!". They had looked through their own training logs and found evidence that their own agents had broken containment during training - and were responsible for the PyPI package we saw earlier, among other things.

      So now we've got both Anthropic and OpenAI with rogue agents running around the internet doing things that they should not be doing.

      August
      #

      In August, I got one of my best pelicans yet. And it was generated on my laptop!

      Qwen 3.8 27B - 17GB, 21 minutes...

It's really good. Beautiful pelican. Correctly shaped bicycle. Legs either side of the frame.
      #

      This was Qwen 3.8 27B, running on my laptop. It's only a 17GB download.

      Admittedly, this pelican took 21 minutes to generate. That's because Qwen 3.8 27B defaults to running in "high" reasoning mode - a terrible default which produces great results but takes way too much time thinking about them.

      You can dial that down and you'll get a slightly worse pelican a lot faster.

      Qwen 3.8 27B was the first time I ran a model on my laptop which felt almost competitive with what was going on on the frontier, at least in terms of Pelican SVGs (which everyone needs, of course).

      This is an extraordinary model. If you're going to play with any local model, this is the one that I'd start with. The things that this can do with just a 17 GB file feel impossible.

      I thought I'd have to wait five years and spend ten thousand dollars on hardware to get results even half as good as this one.

      Tweet by @simonw
New hobby: prototyping video games in 60 seconds using a combination
of GPT-3 and DALL-E
Here's "Raccoon Heist"

GPT-3 playground prompt:
Write a detailed product description of a
computer game where a team of raccoons go on
heists

GPT-3 response:
In "Raccoon Heist", you and your team of thieving ~~ o
raccoons are tasked with pulling off a series of 
daring heists. From robbing banks to stealing 
priceless art, no job is too big or too small for your 
furry crew. You'll need to use your wits and your
skills to avoid the police and make a clean
getaway with the loot. With exciting gameplay and
a charming cast of characters, "Raccoon Heist" is
the perfect game for anyone looking for a light-hearted caper

Plus an image of some almost isometric raccoons sneaking past a bin.
11:45 AM - Aug 5, 2022
      #

      In August, I also started playing with game development.

      Four years ago, back in August 2022, I tweeted out an experiment where I'd used GPT-3 and the original DALL-E to write a paragraph long description of a computer game and then turn that into concept art.

      My prompt to GPT-3 back then was:

      Write a detailed product description of a computer game where a team of raccoons go on heists

      In August 2026 I decided to drop just the screenshots from that tweet into a coding agent and see what it could do with them.

      Night 5 Clear

Rank: TRASH PANDA
The crew banked 595 in shiny loot (goal 560).
Word on the street: an even bigger score tomorrow...
      #

      Here's what I got from Claude Fable 5 in Claude Code. It's pretty good! It's definitely a game, you're a raccoon, you run around a backyard gathering treasure and avoiding guards with flashlights.

      It didn't feel very "heisty" though. I was thinking a heist would involve a bank or a museum...

      Moonlight & Mayhem
One museum. Three raccoons. Absolutely no plan

Start the Heist button.
      #

      Then I tried the same thing in Codex Desktop using GPT-5.6 Sol Ultra, and got a massively better result. Now you're a raccoon in a museum, rescuing two of your fellow raccoons (who have been imprisoned in that museum for some reason), then stacking up on top of each other to steal the Golden Sardine. Much more of a heist!

      They look like games,
but are they fun?
      #

      These games were fun for about one minute and 15 seconds.

      Something I've realized about game development is that you can vibe-code something that looks like a computer game, and that's easy.

      Building a game that's fun, has a good gameplay loop, and is challenging and interesting and keeps people coming back for more... that's still beyond me, and beyond any of the agents I've tried.

      This ties into the Deep Blue thing. Just because we can make something that looks like a game does not mean that we are game developers.

      September
      #

      We're into September now. So much has happened this month!

      Discovery of a new OpenAl agent message board

Sydney Von Arx, Cormac Slade Byrd, Spencer KittsThomas Larsen - 4 September 2026
      #

      An independent group of researchers found a message board where OpenAI agents-in-training had been illicitly communicating with each other... and it was that German language wiki I showed you earlier. The one from June.

      I wrote more about that here.

      OpenAI had confessed to the Hugging Face thing, but now there's this other incident which surely they should have known about from reviewing their logs. It was surprising that this took an independent group of researchers to uncover.

      OpenAl agents carried out an undisclosed cyber-attack on RubyGems

Spencer Kitts, Thomas Larsen, Sydney Von Arx - 11 September 2026
      #

      And then a week later those same researchers found that the attack on RubyGems back in May was caused by OpenAI's agents in training as well!

      At this point I'm wondering how many more incidents like this there are that we haven't found yet. Clearly this was a big problem for months before anyone figured out what was going on.

      Headline: Australian PM warns in UN speech about the ‘furious pace’ of Al
after security breach
      #

      Then just the other day, here's the Prime Minister of Australia at the United Nations General Assembly warning that OpenAI had hacked the Australian healthcare website that I showed you earlier.

      I think that was part of the same training run as the Wiki stuff, because there were posts on that Wiki mentioning .gov.au websites and that training appeared to involve researching statistics online to answer questions in an evaluation suite.

      This story is still coming together, but now it's an international incident that's been raised at the UN by a head of state!

      www.felonybench.com

OpenAI: 11
Anthropic: 9
Google: 3
Meta: 1
      #

      This does mean we've got a new benchmark, probably more useful than my pelicans.

      FelonyBench.com tracks the number of felony cyberattacks from different labs. OpenAI currently lead with 11, Anthropic have 9. Google have three, which they confessed to the Wall Street Journal a couple of weeks ago. They said they had previously chosen not to disclose because the agents had stopped when they realized that they shouldn't be doing that.

      Meta have one too. So felonies all round for the AI labs.

      Pelicans for GPT-6 Astra, GPT-6 Sol, and GPT-6 Luna. All are good, all have the same color scheme.
      #

      Here's our current state of the art for the pelicans. This is the GPT-6 family, which just came out.

      Astra made a fantastic pelican riding a bicycle. It's got the legs on both sides. The frame is good.

      It's interesting how all of the GPT-6 models pick a similar color scheme to each other.

      GPT-6 Luna for 0.4 cents will draw you a competent-ish pelican riding a bicycle!

      Grid for Claude Fable 5.1, Opus 5.5, OPus 5, Sonnet 5. The Sonnet pelicans are terrible. All of the others are pretty good. Opus 5.5 is missing its Max level pelican because it ran out of tokens. The best is Fable 5.1 at Max.
      #

      Claude has caught up a little bit. Claude Fable 5.1 gave me an excellent pelican riding a bicycle - the best I've seen from a Claude model - but did charge me $3.30 for it.

      Opus 5.5 thought for 128,000 tokens and then gave up! It ran out of tokens before it got to the response.

      It doesn’t get easier -
you just get faster
Greg LeMond
3x Tour de France champion
      #

      Getting back to Deep Blue. Something that's been puzzling me this year is this: why does my job feel harder?

      I've got these agents that can do all of this stuff for me, and yet I've never worked so hard, I've never been so intellectually engaged with my work.

      Partly this is because I'm being a lot more ambitious with what I take on, but it's also because all of the easy stuff is handled for me. If it's easy, the agent will do it. Everything that's left for me is difficult.

      This morning I heard this quote from three-time Tour de France champion Greg LeMond:

      It doesn't get easier, you just get faster.

      I think that's exactly what's happening to us now as software engineers with coding agents.

      Kakapo population reaches new milestone
The official population of the critically endangered kakapo has
reached a recovery-era high of 325 birds.
      #

      One closing thing. I know you're desperate for an update on Kākāpō breeding season.

      We've reached a recovery-era high of 325 birds!

      89 new chicks have made it to this point. This is the best breeding year in a very long time.

      Kakapo party, click for confetti.
      #

      I heard that Claude Opus 5.5 can now do pixel art. Claude doesn't have an image generator, but it's very good at using JavaScript to draw animated pixels.

      So I had it make me a Kākāpō dance party. I think this is a good celebration of the most important news of this year.

      You are only seeing the long-form articles from my blog. Subscribe to /atom/everything/ to get all of my posts, or take a look at my other subscription options.

    2. 🔗 smol-machines/smolvm smolvm v1.19.3 release

      What's Changed

      Full Changelog : v1.19.2...v1.19.3

    3. 🔗 r/Harrogate Anyone else think this sculpture is a bit weird? rss

      Anyone else think this sculpture is a bit weird? | Thoughts! submitted by /u/FrontRaspberry5060
      [link] [comments]
      ---|---

    4. 🔗 crosspoint-reader/crosspoint-reader 1.6.5 release

      Summary

      Library view

      Recent Books has grown into a powerful way to browse your entire collection on your SD card. Sort by recently added, title, or author, or just search for the book you want.

      TTF support

      Devices with external RAM (X4Pro, Sticky, X4C, PaperMono) can now use TrueType (.ttf) fonts directly — no need to convert them to CrossPoint's .cpfont format first. Drop your fonts into /fonts/ or /.fonts/ on the SD card, restart the reader, and pick them from the text settings. If a family has separate regular, bold , italic , and bold-italic files, put them together in one family folder.

      .cpfont fonts still work everywhere, including on devices without external RAM. OpenType (.otf) fonts are also supported, though compatibility may vary.

      Cover Grid home theme

      The new Cover Grid theme displays your recent books as a grid of covers on the home screen. This theme is not available on x3 and the original x4.

      More control over reading

      New word and character spacing controls let you adjust how tightly text sits on the page, alongside the existing line-spacing settings. Footnote navigation now highlights references directly on the reading page and and bookmarks can finally be renamed.

      Touch controls and navigation

      Touch devices gain configurable tap and swipe gestures for turning pages. Headers now have back buttons, and fixes improve scrolling in reader lists and settings. X4pro also gain configurable shortcuts for Home button.

      X4 Classic support

      The ESP32-S3-based X4 Classic is now officially supported. Get one now

      Sleep Screens

      Sleep screens across all devices now show drastically higher quality grays. On the X4 and certain Pro display variants they push the quality to the max. Transparent pngs got a nice quality bump as well.

      Clock & About

      You can now view the clock on the home screen! In addition, the old utc offset picker is gone and is replaced with auto DST and a list of cities to pick from when setting the time. This setting has moved to the more obvious system settings. You'll also find a new About option in the system settings as well which is useful for identifying display controllers during debugging.

      Everything else

      Files can be renamed straight from the file browser. Portuguese gets hyphenation support, Korean text justification stretches spaces between words rather than within them, and the keyboard gains an Arabic layout.

      KOSync now sends more precise EPUB reading positions. Fixes also address EPUB lists, hidden content, chapter-position displays, and end-of-book navigation.

      This release reduces font and EPUB memory pressure, fixes USB drive disconnection, improves web file-transfer safety, and prevents EPUBs that fail to render their first page from reopening automatically after wake.


      What's Changed

      New Contributors

      Full Changelog : 1.6.0...1.6.5


      Downloads

      crosspoint-1.6.5-papermono.bin
      crosspoint-1.6.5-sticky.bin
      crosspoint-1.6.5-x3-x4.bin
      crosspoint-1.6.5-x4c.bin
      crosspoint-1.6.5-x4pro.bin

    5. 🔗 Confessions of a Code Addict How Copy-on-Write Works with Memory-Mapped Files rss

      In the last video, we discussed demand paging. Now, let's move towards an even more interesting topic: copy-on-write (CoW). Just like demand paging, CoW is the kernel's internal mechanism with implications for the performance of user- space systems. It enables multiple processes to share data in RAM between them in read-only mode. The interesting bit is that the processes themselves are not aware of this sharing, as far as they are concerned they are executing as if they are the only ones working with that data. Of course, this works as long as the processes are reading the data. When one of them needs to do a write to this shared data, the kernel needs to make a copy before the write can happen, hence the name "copy-on-write "!

      This is a very wide and deep topic, so I'm going to split into multiple videos. This first video goes deep inside the kernel to explain what CoW is, how the kernel implements it, and for that we will take the example of mmap to read and write files.

      Following are some of the major sections in the video with the timestamps to help you navigate. Also, I recommend watching the video at higher speed to get a better experience.

      • 00:00 -- Why CoW matters: memory use, page faults, and unpredictable latency in data-intensive applications.

      • (06:34) The page-table picture: how different processes can map the same physical frame.

      • (11:44) CoW in one diagram: share a page while reading; make a private copy when writing.

      • (15:06) Mapping a file withmmap: the call's arguments, including MAP_PRIVATE.

      • (21:31) Whatmmapcreates: a virtual address range and VMA, before the file page is maps into the process.

      • (25:03) The first read: address translation, a page fault, and how the kernel resolves it.

      • (32:25) The page cache: where file data is held in RAM and why another process can reuse it.

      • (38:43) A second process maps the file: its own page fault leads to the same cached physical page.

      • (43:11) A private write: why writing to that shared file page would violate MAP_PRIVATE.

      • (45:51) Write protection and CoW: how a read-only PTE causes a write fault and the kernel gives the writer a private copy.

      • (49:07) What happens next: the second process can also get a copy; later writes to an already private page proceed without another CoW fault.

      A minor correction note : At about 46 minutes, when I say mappings of page-cache pages are read-only, I mean the MAP_PRIVATE mappings in this example. A writable MAP_SHARED mapping can modify a cached file page, which is later written back to the file.

      If you are new to this series, it is based on my ebook called "Virtual Memory from First Principles". It is available to read for free online and also available to purchase from Gumroad (PDF/Epub) and Amazon (Kindle edition).

      Buy PDF/Epub

      Get Kindle Edition


      And, if you want to watch the previous videos in this series, the following is what has been published so far:

      Share

      Read more

    6. 🔗 Julia Evans Replacing the old battery on rechargeable bike lights rss

      Hello! Recently I needed bike lights for my bike. And I remembered that I already had rechargeable bike lights that I bought ten years ago, that I hadn't tried in a long time. I tried to recharge them, but after fully charging them, they only worked for maybe 5 minutes before they turned off again.

      I don't know much about electronics, but I've been curious about whether it's possible to fix old electronics for a long time, and this seemed like the perfect repair project because I might just need to replace the battery.

      So I went to the local queer makerspace where I'm a member to use the soldering iron and try to do it! I don't know much about electronics and this post does not contain any safety advice because I don't know much about safety. I think it's nice to do projects in a community space where you can get help.

      step 1: cut it open

      The bike light felt like it was made of silicone, so I cut open the silicone in a haphazard way along something that vaguely looked like a seam.

      I definitely ripped some silicone in the process and it was pretty messy but I got it open and found the circuit board.

      I don't know the model number of the bike lights but there's a photo of them at the end of the post.

      step 2: remove the screws

      There were some screws attaching things together so I removed them so I could get the circuit board out.

      Mostly I tried to remove as few screws as possible because I was worried about losing them or not being able to put them back after. I probably put the screws in a bag or something.

      step 3: get the circuit board out

      I took out the circuit board. Here's what it looked like:

      You can see where the battery is attached, I think it's left of RI3 and above Q2.

      Here's what the battery looked like:

      step 4: desolder the battery

      I'd never desoldered anything before, so I found the iFixit guide to desoldering and read it. Also I asked my friends Lee and Lauria for advice.

      Here were the steps I ended up following based on the guide & the advice I got:

      1. Use a desoldering pump to remove most of the solder
      2. Once most of it is gone, kind of pull them apart to try to separate them
      3. Also try to avoid getting the battery too hot in the process by taking breaks to let it cool down. I'm not very good with a soldering iron so it took a while.
      4. The battery has an attachment that is welded to the top. For a while I thought I needed to remove this and it seemed impossible, but it turned out the replacement battery comes with that part so actually I was supposed to leave it alone.

      step 5: identify the battery

      In the picture of the battery in Step 3, you can see it says something like "3" and "LI???77". There's a piece of metal that I think is welded or something to the top of the battery. It seemed impossible and also maybe not smart to try to remove so I wasn't sure how to find out what an "LI????77" was or how to order another one.

      I've been trying to avoid using LLMs (though I will not get into that because I am exhausted by LLM discourse and I'm sure you are too), but I really had no idea how to figure out what the battery was so I asked an LLM. It gave the response "LIR2477", which (when I looked it up) looked exactly the same as my battery so I figured that was plausible.

      I would be interested to learn non-LLM ways to figure this out though. There must be a way. Lauria showed me how to use DigiKey's search which was very cool though DigiKey didn't have that part.

      (edit: someone in the replies told me that this kind of coin cell battery is named according to its dimensions, and you can use plastic calipers to measure the dimensions of the battery. So I guess a non-LLM way would be to measure the battery with calipers and try to match it to something on the List of battery sizes Wikipedia page, though that page only mentions CR 2477 and not LIR 2477. It's a good example of what's fun for me about trying to avoid LLMs, this "List of battery sizes" page is super interesting and if I use an LLM I might never find it)

      step 6: buy the battery

      I went to AliExpress and ordered:

      1. 2 batteries (I had 2 bike lights and I wanted to fix them both)
      2. some silicone glue to glue things back together

      I think the batteries were $3 each and the glue was $8.

      step 7: solder the new batteries in and glue it back together

      The parts took maybe 2 weeks to arive, and once they arrived, I went back to the makerspace and:

      • soldered in the new batteries
      • put the screws back in. The screws were very small and hard to hold, so at this point I dropped some screws on the ground and couldn't find them because they were too small. So I just used fewer screws and hoped for the best.
      • used the glue to try to put everything back together.
      • Make a somewhat halfhearted attempt to clamp the parts I was gluing together

      Then after waiting some amount of time for the glue to dry I took it home and waited 24 hours for the glue to cure.

      Also I took the old batteries to somewhere nearby that accepts old batteries.

      it works!

      The lights work! I have used them to bike at night! I still haven't needed to recharge them (and tragically I had to order a new Mini USB cable because I got rid of all my Mini USB cables, so I'm still waiting for that), so I still don't know for sure how long the lifetime of the new battery will be.

      Here's what the light looks like after re-gluing. You can see that I didn't glue very carefully. It didn't really go back together that well but I'm hoping it'll be good enough.

      I thought it was really cool that I was able to do this with extremely minimal electronics skills! It cost about $20 CAD to buy the parts, and (whether or not the repair holds up, I'll try to update this post in the future!), it was fun to try to repair something and learn something new.

  3. September 26, 2026
    1. 🔗 backnotprop/plannotator v0.27.21 release

      Follow @plannotator on X for updates

      Missed recent releases? Release | Highlights
      ---|---
      v0.27.20 | Mistral Vibe support, annotate gets the full Options menu and Settings, jj Commits panel, long lines wrap in plan code blocks
      v0.27.19 | Before/After image previews in code review, file comments as GitHub file threads, forge-correct #123 links, /plannotator-last finds the right session
      v0.27.18 | Model pickers from your installed Claude and Codex (Opus 5.5, Fable 5.1, GPT-6), unsent PR review comments survive new pushes
      v0.27.17 | Diagram files open in the diagram viewer, OpenCode switches model with agent, idle review stops polling the git remote, Tree is the default review view
      v0.27.16 | Themed diagrams on Mermaid 12, comment on any node or edge, patch-file review, embedded HTML documents render
      v0.27.15 | Plannotator TUI and Herdr Annotate announcement, element context on pinpoints, HTML links open as linked documents, All files panel, Classic diff default
      v0.27.14 | Pi plan progress survives compaction, Codex threads across rollout files, WSL browser setting, Mod+E edit mode
      v0.27.13 | Open a review on a specific base (--base, --diff-type), symlink containment on /api/doc, CI flake fix, Amp decision relay
      v0.27.12 | Unified decision control, token hover cards, local-vs-remote diff, approval notes
      v0.27.11 | OpenCode server leak fix, durable local feedback archive, unknown-subcommand fix
      v0.27.10 | Auto-viewed files on scroll, annotation undo/redo, OpenCode 2 slash commands restored, npm 12 agent terminal fix
      v0.27.9 | WebMCP browser-agent tools, HTML refresh from disk, host seams, lazy renderers, Windows uninstall fix

      What's New in v0.27.21

      A fix release built mostly from community reports. Remote and phone sessions load several times faster, code review can request changes on GitHub for real, model pickers show readable names and say where their list came from, and several OpenCode rough edges are gone. Seven pull requests, answering issues from four people.

      Remote and phone sessions load several times faster

      Every session sent the whole app to the browser as one uncompressed file, about 25 MB for plan review and 18 MB for code review, on a fresh port each time, so nothing could be cached. On a fast local connection that goes unnoticed. Over a slow tunnel, such as a phone reaching a Mac through Tailscale, it meant waiting up to two minutes before anything appeared.

      Remote-mode and --tailscale sessions now send the page compressed: brotli over --tailscale's HTTPS, gzip over plain http. Measured cold loads at 206 KB/s and 159 ms, the connection from the report:

      | Before | --tailscale | Remote mode
      ---|---|---|---
      Plan review | 118 s | 30 s | 34 s
      Code review | 86 s | 21 s | 24 s

      Local sessions send exactly the same bytes and headers as before, since compressing on the same machine gains nothing. The page is also about 1 MB smaller for everyone: KaTeX's math fonts no longer ship in two older formats that no supported browser loads. Splitting the app into separately loaded pieces, which measured around 5 to 7 seconds on the same link, is the next step and is tracked in the same issue.

      (#1619, refs #1617, reported by @giladbarnea)

      Request changes on GitHub pull requests

      In a pull request review, Post comments, then… promised a choice between requesting changes and staying neutral, and Request changes… promised the same. Neither existed: every review posted as a plain comment. The submission dialog now offers Comment or Request changes, and a Request changes review posts as REQUEST_CHANGES on GitHub, including reviews with file-level comment threads. On your own pull request the option is disabled with the reason, since GitHub refuses it. GitLab has no equivalent, so the option is disabled there and posting is unchanged.

      (#1613, closing #1611, reported by @RobertoArtiles)

      Model pickers show real names and where the list came from

      Claude Code 2.1.282 changed how it describes its models, and Plannotator started labeling specific Claude versions with their marketing tagline instead of their name: several rows read "Best for everyday, complex tasks" and could not be told apart. Every Claude picker now shows the model's name again, and older Claude Code versions keep working.

      Each Claude and Codex model picker (code review agents, Code Tour, Guided Review, Ask AI) also shows where its list came from, such as "From your installed Codex 0.155.1", or says it is using the built-in list and suggests updating or signing in to the tool. The built-in list, used only when Plannotator cannot read your installed tool's models, now includes the GPT-6 models. Guided Review defaults to GPT-6 Luna for Codex when your Codex offers it, since guides generate faster on a lighter model; a model you already picked always wins.

      If a newer model is missing from your picker, update the tool itself (codex update, or update Claude Code) and restart the review. The picker shows whatever your installed tool reports.

      (#1615, #1616)

      OpenCode fixes

      • Feedback reaches the right agent. /plannotator-last and /plannotator-annotate feedback was answered by OpenCode's default agent even when the annotated message came from a different one. Feedback now goes to the agent that wrote the message, as long as OpenCode still lists it; otherwise it is delivered exactly as before. (#1614, closing #1612, reported by @balaji-dutt)
      • Guided Review and review agents work with OpenCode v2. Plannotator started opencode run with a --dir flag that OpenCode v2 rejects. It now starts OpenCode in the review's folder and sets its working-directory environment to match, which works on both versions and also keeps OpenCode 1.x from reviewing the wrong checkout in PR, worktree and multi-repo reviews. (#1610, refs #1609, reported by @JakobHavtorn)

      Additional Changes

      • Selections elsewhere on the page survive. The document viewer cleared the whole page's text selection whenever it loaded or its content changed. It now only clears a selection inside itself. This mainly affected apps that embed the viewer next to other panels. (#1608)

      Install / Update

      macOS / Linux:

      curl -fsSL https://plannotator.ai/install.sh | bash
      

      Windows:

      irm https://plannotator.ai/install.ps1 | iex
      

      Claude Code Plugin: Run /plugin in Claude Code, find plannotator , and click "Update now".

      Pi: Update @plannotator/pi-extension to 0.27.21 and restart Pi.

      OpenCode: Clear cache and restart:

      rm -rf ~/.bun/install/cache/@plannotator
      

      What's Changed

      • fix(ui): vim selection reset only clears selections inside the viewer by @backnotprop in #1608
      • fix(agents): drop --dir from opencode run (OpenCode v2 rejects it) by @backnotprop in #1610
      • fix(review): make Request changes a real PR review event by @backnotprop in #1613
      • fix(opencode): answer /plannotator-last feedback with the annotated message's agent by @backnotprop in #1614
      • fix(core): name Claude models from displayName when the description is only a tagline by @backnotprop in #1615
      • Model pickers: show where the list came from; refresh the Codex fallback by @backnotprop in #1616
      • perf: compress the app page for remote and tailnet sessions; woff2-only KaTeX by @backnotprop in #1619

      Community

      This release is mostly answers to reports:

      • @giladbarnea measured the two-minute phone load precisely, down to the link speed and what compression alone would save, in #1617
      • @RobertoArtiles traced the missing request-changes choice through the source in #1611
      • @balaji-dutt reported OpenCode feedback reaching the wrong agent, with an agent-by-turn table, in #1612
      • @JakobHavtorn reported the OpenCode v2 --dir failure in #1609

      Full Changelog : v0.27.20...v0.27.21

    2. 🔗 smol-machines/smolvm smolvm v1.19.2 release

      What's Changed

      Full Changelog : v1.19.1...v1.19.2

    3. 🔗 @HexRaysSA@infosec.exchange AI agents can now work in IDA, using the official IDA MCP server. 🔧 mastodon

      AI agents can now work in IDA, using the official IDA MCP server. 🔧

      IDA MCP is free and open source. It's model-agnostic, so you can use any capable LLM, running locally or in the cloud.

      What's different:
      → Code Mode: a small set of tools, with agents working through IDAPython. That uses ~20% fewer tokens than popular alternatives
      → IDA Nexus: many agents and humans on the same IDBs, with changes showing instantly in the IDA GUI (and vice versa)
      → Runs headless with idalib for large-scale pipelines, and fully local for air-gapped environments
      → Works with IDA Pro, Home, Classroom and OEM

      One-command installs for Claude Code, Codex, GitHub Copilot, Pi and oh-my-pi. Any stdio MCP client works too.

      Install (requires uv):
      uvx ida-hcli mcp install

      Full details 👇
      https://hex-rays.com/blog/hex-rays-ida-mcp-server

    4. 🔗 r/Harrogate Best Lunch Deals rss

      As the title says, what are the best restaurant lunch deals in Harrogate mid- week? Seen Pranzo 2 courses for £20 and Tannin Level 2 course for £23… appreciate any other suggestions!

      submitted by /u/Various-Note-9396
      [link] [comments]

    5. 🔗 r/Harrogate Coldbath Road Reccomendations rss

      Any insight?? The old man is coming up tomorrow at 11am what is there to do on coldbath?

      I’ve only ever walked up is there anywhere for a coffee or lunch you would advise on

      submitted by /u/FrontRaspberry5060
      [link] [comments]

    6. 🔗 Anton Zhiyanov Go concurrency distilled rss

      This mini-book provides a brief overview of many concurrency topics in Go. Each topic comes with interactive examples — feel free to experiment with them by changing the code and clicking Run. There's also a PDF version with static examples.

      This is a quick refresher on Go concurrency, not a beginner's guide. If you want to learn concurrency from the ground up with practical exercises, check out my other book — Gist of Go: Concurrency.

      The book is AI-free.

      Goroutines • Channels • Select • Pipelines • Time • Context • Wait groups • Data races • Race conditions • Mutexes • Semaphores • Signaling • Run once • Object pool • Atomics • Testing • Scheduling • Diagnostics • Final thoughts

      # Goroutines The foundation of concurrency in Go is goroutines – functions started with the go keyword: func main() { var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() fmt.Println("worker 1") }() go func() { defer wg.Done() fmt.Println("worker 2") }() wg.Wait() } worker 2 worker 1 The Go runtime juggles these goroutines and distributes them among operating system threads running on CPU cores. Compared to OS threads, goroutines are lightweight, so you can create hundreds or thousands of them. Goroutines are completely independent. The main function is also a goroutine, but it starts implicitly when the program starts. When main ends, other goroutines also shut down. We use a wait group (sync.WaitGroup) to wait for goroutines to finish in the example above. A wait group has a counter inside. Calling Add(n) increments it by n, while Done() decrements it by one. Wait() blocks the calling goroutine (in this case, main) until the counter reaches zero. This way, main waits for both workers to finish before it exits. WaitGroup.Go automatically increments the wait group counter, runs a function in a goroutine, and decrements the counter when it's done: func main() { var wg sync.WaitGroup wg.Go(func() { fmt.Println("worker 1") }) wg.Go(func() { fmt.Println("worker 2") }) wg.Wait() } worker 2 worker 1 # Channels Goroutines can pass values to each other through channels. A channel is like a window where one goroutine can throw something and another can catch it: func main() { messages := make(chan string) go func() { messages <- "ping" }() msg := <-messages fmt.Println(msg) } ping Sending a value through a channel is a synchronous operation. When the sending goroutine writes a value to the channel (ch <- val), it blocks and waits for someone to receive that value (<-ch). Only then does it continue. Output channel Returning an output channel from a function and filling it within an internal goroutine is a common pattern in Go. This allows the caller to receive values through the channel while the owning function retains control of it: func generate(start, stop int) chan int { out := make(chan int) go func() { for i := start; i < stop; i++ { out <- i } }() return out } Closing a channel To signal readers that all data has been sent, the writer goroutine closes the channel with close(): func generate(start, stop int) chan int { out := make(chan int) go func() { defer close(out) for i := start; i < stop; i++ { out <- i } }() return out } The reader checks the channel's status with a second value ("comma OK") when reading: func main() { in := generate(5, 10) for { num, ok := <-in if !ok { break } fmt.Print(num, " ") } } 5 6 7 8 9 While the channel is open, the reader receives the next value and a true status. If the channel is closed, the reader gets a zero value and a false status. A channel can only be closed once. Closing it again or writing to a closed channel causes a panic. The only reason to close a channel is to signal to its readers that all data has been sent. If this isn't important to the readers, then you don't need to close it. When a channel is no longer used, Go's garbage collector will free its resources, whether it's closed or not. Channel iteration range automatically reads the next value from the channel and checks if it's closed. If the channel is closed, it exits the loop: func main() { nums := generate(5, 10) for n := range nums { fmt.Print(n, " ") } } 5 6 7 8 9 Range over a channel returns a single value, not a pair, unlike range over a slice. Directional channels You can protect yourself from accidental write/close errors by setting the channel direction. Channels can be: chan (bidirectional): for reading and writing (default); chan<- (send-only): for writing only; <-chan (receive-only): for reading only. You can't read from a send-only channel or write to a receive-only channel (nor can you close it). Channels are usually initialized for both reading and writing, and specified as directional in function parameters. Go automatically converts a regular channel to a directional one: stream := make(chan int) go func(in chan<- int) { in <- 42 }(stream) func(out <-chan int) { fmt.Println(<-out) }(stream) 42 Buffered channels Buffered channels work like a FIFO queue with a fixed-size buffer for storing values. As long as the buffer has free space, writing to the channel doesn't block the goroutine. Similarly, as long as the buffer contains values, reading from the channel doesn't block the goroutine: stream := make(chan int, 3) stream <- 11 stream <- 12 stream <- 13 fmt.Println(<-stream) fmt.Println(<-stream) 11 13 By default, if you don't specify a buffer size, a channel is unbuffered (buffer size equals zero). Buffered channels work with the built-in len() and cap() functions: stream := make(chan int, 3) stream <- 11 fmt.Println(cap(stream), len(stream)) 3 1 Reading from a closed buffered channel returns values from the buffer and a true status. Once all values are taken, it returns a zero value and a false status, like a regular channel: stream := make(chan int, 1) stream <- 11 close(stream) val, ok := <-stream fmt.Println(val, ok) // 11 true val, ok = <-stream fmt.Println(val, ok) // 0 false 11 true 0 false nil channel Like any type in Go, channels have a zero value, which is nil. Writing to or reading from a nil channel blocks the goroutine indefinitely: var stream chan int go func() { // blocks forever stream <- 1 }() // blocks forever <-stream Closing a nil channel causes a panic: var stream chan int close(stream) // panic: close of nil channel # Select The select statement is somewhat like switch, but specifically designed for channels. Here's what it does: Checks which cases are not blocked. If multiple cases are ready, randomly selects one to execute. If all cases are blocked and there is a default case, executes it. If all cases are blocked and there is no default case, waits until one is ready. Select is used to manage data flow in pipelines: // merge sends values from in1 and in2 to the output channel. func merge(in1, in2 <-chan int) <-chan int { out := make(chan int) go func() { defer close(out) for in1 != nil || in2 != nil { select { case val1, ok := <-in1: if ok { out <- val1 } else { in1 = nil } case val2, ok := <-in2: if ok { out <- val2 } else { in2 = nil } } } }() return out } // Suppose we send 10..12 to in1, 20..22 to in2, // and call merge(in1, in2) 10 11 20 12 21 22 To cancel goroutines: // process modifies values from in and send them to out // until in is exhausted or cancel is closed. func process(cancel chan struct{}, in <-chan int) <-chan int { out := make(chan int) go func() { for val := range in { select { case out <- val*10: case <-cancel: fmt.Println("canceled") return } } }() return out } // Suppose we send values 11 and 12 to in // and then call close(cancel) 110 120 canceled For non-blocking operations: // multiplier returns a function that multiplies // the input by 10 and sends it to the channel // or returns an error if the channel is busy. func multiplier(ch chan<- int) func(n int) error { return func(n int) error { select { case ch <- n*10: return nil default: return errors.New("busy") } } } func main() { nums := make(chan int, 1) multiply := multiplier(nums) err := multiply(11) fmt.Println(<-nums, err) // 110 <nil> err = multiply(12) fmt.Println(<-nums, err) // 120 <nil> err = multiply(13) err = multiply(14) fmt.Println(err) // busy } 110 <nil> 120 <nil> busy And for much more. # Pipelines A pipeline is a sequence of operations where each step takes input data, processes it in a specific way, and outputs it. The input and output of each operation is a channel. A typical pipeline looks like this: Reader : Reads input data from a file, database, or network. N processors : Transform, filter, aggregate, or enrich data using external sources. Writer : Writes the processed data to a file, database, or network. func readT any <-chan T { out := make(chan T) go func() { defer close(out) for { // read data from somewere data := // ... out <- data } }() return out } func processT any <-chan T { out := make(chan T) go func() { defer close(out) for inData := range in { // process the data outData = // ... out <- outData } }() return out } func writeT any <-chan struct{} { done := make(chan struct{}) go func() { defer close(done) for data := range in { // write the data } }() return done } Output channel A goroutine can signal other goroutines that it has finished its work using an output channel : func generate(start, stop int) <-chan int { out := make(chan int) go func() { defer close(out) for i := start; i < stop; i++ { out <- i } }() return out } func main() { nums := generate(5, 10) for n := range nums { fmt.Print(n, " ") } } 5 6 7 8 9 Done channel If a goroutine doesn't need to return results, it can signal completion using a done channel : func work() <-chan struct{} { done := make(chan struct{}) go func() { defer close(done) fmt.Println("work done") }() return done } func main() { done := work() <-done } work done Cancel channel To terminate a goroutine early, a calling goroutine can use a cancel channel : func generate(cancel chan struct{}, n int) <-chan int { out := make(chan int) go func() { defer close(out) for i := 1; i <= n; i++ { select { case out <- i: case <-cancel: return } } }() return out } func main() { cancel := make(chan struct{}) defer close(cancel) nums := generate(cancel, 10) fmt.Println(<-nums) fmt.Println(<-nums) fmt.Println(<-nums) } 1 2 3 Error handling There are three approaches to error handling in concurrent pipelines. ➊ Return on the first error: // calculate produces answers for the given numbers. func process(in <-chan int) (<-chan int, <-chan error) { out := make(chan Answer) errc := make(chan error, 1) go func() { defer close(out) for n := range in { ans, err := fetchAnswer(n) if err != nil { errc <- err // return with error return } out <- ans } errc <- nil // return with nil }() return out, errc } ➋ Use a result type: // Result contains an answer or an error. type Result struct { answer int err error } // calculate produces answers for the given numbers. func calculate(in <-chan int) <-chan Result { out := make(chan Result) go func() { defer close(out) for n := range in { ans, err := fetchAnswer(n) out <- Result{ans, err} // return answer + error } }() return out } ➌ Collect errors separately: // calculate produces answers for the given numbers. func calculate(in <-chan int, errc chan<- error) <-chan int { out := make(chan Answer) go func() { defer close(out) for n := range in { ans, err := fetchAnswer(n) if err == nil { out <- ans // send answer } else { errc <- err // or error } } }() return out } # Time Besides handling date and time, the time package offers tools for managing time-sensitive operations in concurrent programs. After time.After() returns a channel that is initially empty, but receives a value after the timeout period. It's useful for timing out operations: // withTimeout executes a function with a given timeout. func withTimeout(timeout time.Duration, fn func()) error { done := make(chan struct{}) go func() { defer close(done) fn() }() // blocks until fn completes or the timer expires, // whichever happens first select { case <-done: return nil case <-time.After(timeout): return errors.New("timeout") } } withTimeout() waits for fn() to complete, but thanks to time.After(), it won't wait longer than the timeout duration: func main() { var err error // completes in time err = withTimeout( 50*time.Millisecond, func() { fmt.Println("work done") }, ) fmt.Println("err =", err) // gets canceled on timeout err = withTimeout( 50*time.Millisecond, func() { time.Sleep(100 * time.Millisecond) fmt.Println("work done") }, ) fmt.Println("err =", err) } work done err = <nil> err = timeout Timer A timer (time.Timer) is a structure with a C channel to which it sends the current time when it triggers (expires). Timers are useful for planning future executions: done := make(chan struct{}) timer := time.NewTimer(50 * time.Millisecond) go func() { eventTime := <-timer.C // blocks for 50ms fmt.Println("work done at", eventTime) close(done) }() <-done work done at 2009-11-10 23:00:00.05 Stop() stops the timer and returns true if it hasn't expired yet, and false otherwise: // timer expires after 50ms timer := time.NewTimer(50 * time.Millisecond) go func() { eventTime := <-timer.C fmt.Println("work done at", eventTime) }() // after 10ms, the timer hasn't expired yet time.Sleep(10 * time.Millisecond) if timer.Stop() { fmt.Println("execution canceled") } else { fmt.Println("too late to cancel") } execution canceled It's often more convenient to use the time.AfterFunc() wrapper function. It waits for duration d and then executes function f: done := make(chan struct{}) work := func() { fmt.Println("work done") close(done) } // executes work after 50ms time.AfterFunc(50*time.Millisecond, work) <-done work done time.AfterFunc() returns a timer that you can cancel before execution starts: // executes the function after 50ms timer := time.AfterFunc(50*time.Millisecond, func() {}) // after 10ms, the timer hasn't expired yet time.Sleep(10 * time.Millisecond) if timer.Stop() { fmt.Println("execution canceled") } execution canceled If a timer is used in a loop, it's better to create a single timer and reset it instead of creating a new instance on each iteration: // consumer reads tokens from the input channel and alerts // if a value does not appear in a channel after an hour. func consumer(in <-chan token) { const timeout = time.Hour timer := time.NewTimer(timeout) for { timer.Reset(timeout) select { case <-in: // do stuff case <-timer.C: // log warning } } } // Suppose we send 10,000 values to the in channel // and measure memory usage. Memory used: 4 KB, # allocations: 6 Ticker A ticker is like a timer, but it keeps firing until you stop it. Tickers are useful for executing periodic tasks: // fires every 50ms ticker := time.NewTicker(50 * time.Millisecond) defer ticker.Stop() go func() { for { // waits for ticker to fire on each iteration at := <-ticker.C fmt.Println("work done at", at) } }() // enough time for the ticker to fire 3 times time.Sleep(160*time.Millisecond) ticker.Stop() work done at 2009-11-10 23:00:00.05 work done at 2009-11-10 23:00:00.10 work done at 2009-11-10 23:00:00.15 NewTicker(d) creates a ticker that sends the current time to the channel C at interval d. You must stop the ticker eventually with Stop() to free up resources. If the channel reader can't keep up with the ticker, the ticker will skip ticks. # Context The main purpose of context is to cancel operations, either manually or by timeout/deadline. The function accepts a context and uses its Done() channel to listen for cancellation: // work performs a task for 50 ms unless canceled. // Returns an error when canceled. func work(ctx context.Context) error { done := make(chan struct{}) go func() { time.Sleep(50 * time.Millisecond) fmt.Println("work done") close(done) }() select { case <-done: return nil case <-ctx.Done(): return ctx.Err() } } Cancel manually (context.Canceled error): func main() { // empty context ctx := context.Background() // manual canellation context ctx, cancel := context.WithCancel(ctx) defer cancel() done := make(chan struct{}) go func() { // takes 50 ms unless canceled err := work(ctx) fmt.Println("err =", err) close(done) }() // cancels after 10 ms time.Sleep(10 * time.Millisecond) cancel() <-done } err = context canceled Cancel by timeout (context.DeadlineExceeded error): func main() { ctx := context.Background() // cancels after 10 ms ctx, cancel := context.WithTimeout(ctx, 10*time.Millisecond) defer cancel() done := make(chan struct{}) go func() { // takes 50 ms unless canceled err := work(ctx) fmt.Println("err =", err) close(done) }() <-done } err = context deadline exceeded Cancel by deadline (context.DeadlineExceeded error): func main() { ctx := context.Background() // cancels at now + 10 ms deadline := time.Now().Add(10 * time.Millisecond) ctx, cancel := context.WithDeadline(ctx, deadline) defer cancel() done := make(chan struct{}) go func() { // takes 50 ms unless canceled err := work(ctx) fmt.Println("err =", err) close(done) }() <-done } err = context deadline exceeded Context is layered. A context object is immutable. To add new properties to a context, a new (child) context is created based on the old (parent) context. The shorter timeout between the parent and child contexts always wins. The child context can only shorten the parent's timeout, not extend it: func main() { // parent context with a 100 ms timeout const dur100ms = 100 * time.Millisecond parentCtx, cancel := context.WithTimeout(context.Background(), dur100ms) defer cancel() // child context with a 10 ms timeout const dur10ms = 10 * time.Millisecond childCtx, cancel := context.WithTimeout(parentCtx, dur10ms) defer cancel() // now the work gets canceled err := work(childCtx) fmt.Println("err =", err) } err = context deadline exceeded Multiple cancels are safe. You can call cancel() on the context as many times as you want. The first cancel will work, and the rest will be ignored. You can specify a custom cancellation cause using context.WithCancelCause(), context.WithTimeoutCause() and context.WithDeadlineCause(). This cause is accessible through context.Cause(): ctx, cancel := context.WithCancelCause(context.Background()) cancel(errors.New("the night is dark")) fmt.Println(context.Cause(ctx)) the night is dark You can register a function to execute when the context is canceled with context.AfterFunc(): ctx, cancel := context.WithCancel(context.Background()) cleanup := func() { fmt.Println("cleanup") } context.AfterFunc(ctx, cleanup) cancel() time.Sleep(10 * time.Millisecond) cleanup Context can pass additional information about a call using context.WithValue(), which creates a context with a value for a specific key. But it's generally better to avoid passing values in context. It's better to use explicit parameters or custom structs instead. # Wait groups The sync.WaitGroup type lets you wait for one or more goroutines to finish: const n = 10 var wg sync.WaitGroup wg.Add(n) for range n { go func() { defer wg.Done() fmt.Print(".") }() } wg.Wait() .......... A WaitGroup doesn't know anything about the goroutines it manages. It works with an internal counter. Calling wg.Add(1) increments the counter by one, while wg.Done() decrements it. wg.Wait() blocks the calling goroutine until the counter reaches zero. The Go method combines Add, starting a goroutine, and Done: var wg sync.WaitGroup for range 10 { wg.Go(func() { fmt.Print(".") }) } wg.Wait() .......... All methods are safe to use from multiple goroutines. Normally, all Add calls happen before Wait. But technically, there's nothing stopping you from doing some of the Add calls before Wait and some after (from another goroutine). You can call Wait from multiple goroutines. They will all block until the group's counter reaches zero. # Data races A data race happens when multiple goroutines access shared data, and at least one of them modifies it. We need to protect the data from this kind of concurrent access. A data race doesn't always cause a runtime panic. That's why Go provides a special tool called the race detector. You can turn it on with the race flag, which works with the test, run, build, and install commands. var total int // There's a data race on total. var wg sync.WaitGroup wg.Go(func() { total++ }) wg.Go(func() { total++ }) wg.Wait() fmt.Println("total:", total) total: 2 go run -race main.go ================== WARNING: DATA RACE ... 2 Found 1 data race(s) Channels are safe for concurrent reading and writing, and they don't cause data races. Ways to prevent data races: Avoid concurrent data modification (typically by using channels). Synchronize access with mutexes. Use only atomic operations. Race conditions A race condition happens when an unpredictable order of operations from multiple goroutines leads to an incorrect system state: // There's a race condition when working with balance. withdraw := func(amount int) { if getBalance() < amount { return } time.Sleep(time.Millisecond) setBalance(getBalance() - amount) } setBalance(50) var wg sync.WaitGroup wg.Go(func() { withdraw(40) }) wg.Go(func() { withdraw(40) }) wg.Wait() fmt.Println("balance:", getBalance()) balance: -30 If individual operations are concurrent-safe, Go's race detector won't find any issues. Because of this, it doesn't catch race conditions: go run -race main.go balance: -30 You can't fully eliminate uncertainty in a concurrent environment. Events will happen in an unpredictable order — that's just how concurrency works. However, you can prevent a race condition — often by protecting a composite operation with a mutex: var mu sync.Mutex withdraw := func(amount int) { mu.Lock() defer mu.Unlock() if getBalance() < amount { return } time.Sleep(time.Millisecond) setBalance(getBalance() - amount) } setBalance(50) var wg sync.WaitGroup wg.Go(func() { withdraw(40) }) wg.Go(func() { withdraw(40) }) wg.Wait() fmt.Println("balance:", getBalance()) balance: 10 Compare-and-set Sometimes you can prevent a race condition without using mutexes by applying an atomic compare-and-set operation or one of its flavors: // CompareAndSet changes the value to new if the current value equals old. // Returns true if the value was changed. CompareAndSet(old, new any) bool // CompareAndSwap changes the value to new if the current value equals old. // Returns the old value. CompareAndSwap(old, new any) any // CompareAndDelete deletes the value if the current value equals old. // Returns true if the value was deleted. CompareAndDelete(old any) bool // etc The idea is always the same: Check if the assumed (old) state matches reality. If it does, change the state to new. If not, do nothing. # Mutexes The sync.Mutex type protects shared data and parts of your code from being accessed concurrently: var total int var mu sync.Mutex var wg sync.WaitGroup for range 100 { wg.Go(func() { mu.Lock() time.Sleep(time.Millisecond) total++ mu.Unlock() }) } wg.Wait() total: 100 The mutex guarantees that only one goroutine can run the code between Lock() and Unlock() at a time. A mutex is used in these situations: When multiple goroutines are modifying the same data. When one goroutine is modifying the data and others are reading it. If all goroutines are only reading the data, you don't need a mutex. TryLock The TryLock method tries to lock the mutex, just like a regular Lock. But if it can't, it returns false right away instead of blocking the goroutine: var total int var mu sync.Mutex var wg sync.WaitGroup for range 100 { wg.Go(func() { if !mu.TryLock() { return } defer mu.Unlock() time.Sleep(time.Millisecond) total++ }) } wg.Wait() total: 1 RWMutex The sync.RWMutex type distinguishes between readers and writers. It provides two sets of methods: Lock / Unlock lock and unlock the mutex for both reading and writing. RLock / RUnlock lock and unlock the mutex for reading only. var total int var mu sync.RWMutex var wg sync.WaitGroup // 10 writers. for range 10 { wg.Go(func() { mu.Lock() defer mu.Unlock() time.Sleep(time.Millisecond) total++ }) } // 10 readers. for range 10 { wg.Go(func() { // Try switching from RLock/RUnlock to Lock/Unlock //and see how it affects the elapsed time. mu.RLock() defer mu.RUnlock() time.Sleep(time.Millisecond) _ = total }) } wg.Wait() elapsed: 10ms Here's how it works: If a goroutine locks the mutex with Lock(), other goroutines will be blocked if they try to use Lock() or RLock(). If a goroutine locks the mutex with RLock(), other goroutines can also lock it with RLock() without being blocked. If at least one goroutine has locked the mutex with RLock(), other goroutines will be blocked if they try to use Lock(). This creates a "single writer, multiple readers" setup. Locker Both sync.Mutex and sync.RWMutex implement the same sync.Locker interface: type Locker interface { Lock() Unlock() } By using Locker instead of a specific mutex type, you can build components that don't depend on a specific lock implementation. This lets the client decide which lock to use. Channel as mutex You can use a channel instead of a mutex to protect shared data: var total int lock := make(chan struct{}, 1) var wg sync.WaitGroup wg.Go(func() { lock <- struct{}{} defer func() { <-lock }() total++ }) wg.Go(func() { lock <- struct{}{} defer func() { <-lock }() total++ }) wg.Wait() total: 2 # Semaphores A semaphore is like a container with N available slots and two operations: acquire to take a slot and release to free a slot. Here are the semaphore rules: Calling acquire takes a free slot. If there are no free slots, acquire blocks the goroutine that called it. Calling release frees up a previously taken slot. If there are any goroutines blocked on acquire when release is called, one of them will immediately take the freed slot and unblock. You can implement a simple semaphore with a buffered channel, where N is the channel's size. To acquire the semaphore, send a value into the channel. To release it, take a value from the channel: // Try changing nConc and see how the elapsed time changes. const nConc = 4 const nCalls = 100 sema := make(chan struct{}, nConc) var wg sync.WaitGroup for range nCalls { sema <- struct{}{} // acquire wg.Go(func() { defer func() { <-sema }() // release time.Sleep(time.Millisecond) // do some work }) } wg.Wait() elapsed: 25ms For more complex situations, use the golang.org/x/sync/semaphore package. Rendezvous A rendezvous lets two goroutines wait for each other: There are two goroutines — G1 and G2 — and each one can signal that it's ready. If G1 signals but G2 hasn't yet, G1 blocks and waits. If G2 signals but G1 hasn't yet, G2 blocks and waits. When both have signaled, they both unblock and continue running. You can implement a simple rendezvous with a wait group: var rend sync.WaitGroup rend.Add(2) var wg sync.WaitGroup wg.Go(func() { fmt.Println("before rendezvous") rend.Done() rend.Wait() fmt.Println("after rendezvous") }) wg.Go(func() { fmt.Println("before rendezvous") rend.Done() rend.Wait() fmt.Println("after rendezvous") }) wg.Wait() before rendezvous before rendezvous after rendezvous after rendezvous Barrier A barrier is a general case of a rendezvous. It lets N goroutines wait for each other: The barrier has a counter (starting at 0) and a threshold N. Each goroutine that reaches the barrier increases the counter by 1. The barrier blocks any goroutine that reaches it. Once the counter reaches N, the barrier unblocks all waiting goroutines. You can implement a simple barrier with a wait group: const n = 4 var bar sync.WaitGroup bar.Add(n) var wg sync.WaitGroup for range n { wg.Go(func() { fmt.Println("before the barrier") bar.Done() bar.Wait() fmt.Println("after the barrier") }) } wg.Wait() before the barrier before the barrier before the barrier before the barrier after the barrier after the barrier after the barrier after the barrier # Signaling The sync.Cond (conditional variable) type lets one goroutine signal to another that it's ready, and lets the other goroutine wait for that signal. A Cond includes a mutex and has two methods — Wait and Signal. Wait unlocks the mutex and suspends the goroutine until it receives a signal. Signal wakes the goroutine that is waiting on Wait. When Wait wakes up, it locks the mutex again. cond := sync.NewCond(&sync.Mutex{}) done := false var wg sync.WaitGroup wg.Go(func() { cond.L.Lock() fmt.Println("G1 is ready to signal") done = true cond.Signal() cond.L.Unlock() }) wg.Go(func() { cond.L.Lock() for !done { cond.Wait() } fmt.Println("G2 received the signal") cond.L.Unlock() }) wg.Wait() G1 is ready to signal G2 received the signal If there are multiple waiting goroutines when Signal is called, only one of them will be resumed. If there are no waiting goroutines, Signal does nothing. You can also use the Broadcast method. While Signal wakes up only one goroutine waiting on Cond.Wait, the Broadcast method wakes up all such goroutines. You can signal with a channel: signal := make(chan struct{}, 1) go func() { // do something signal <- struct{}{} }() go func() { <-signal // do something }() And broadcast too: broadcast := make(chan struct{}) go func() { // do something close(broadcast) }() go func() { <-broadcast // do something }() go func() { <-broadcast // do something }() Broadcasting with a condition variable is limited: it only sends a signal, not the actual data, and it only works once. With channels, you can build a publish/subscribe system that doesn't have these limitations: type Publisher struct { sbox []chan int // subscription channels mu sync.Mutex // protects the state } func (p *Publisher) Subscribe() <-chan int { p.mu.Lock() defer p.mu.Unlock() sub := make(chan int, 1) p.sbox = append(p.sbox, sub) return sub } func (p *Publisher) Broadcast(v int) { p.mu.Lock() defer p.mu.Unlock() for _, sub := range p.sbox { select { case sub <- v: default: } } } # Run once The sync.Once type makes sure that the given function runs only once. If multiple goroutines call Once.Do at the same time, only one will run the function, while the others will wait until it returns: total := 0 initState := func() { total += 1 } var once sync.Once var wg sync.WaitGroup wg.Go(func() { once.Do(initState) // do something }) wg.Go(func() { once.Do(initState) // do something }) wg.Wait() total: 1 Once is perfect for one-time initialization or cleanup in a concurrent environment. Besides the Once type, the sync package also includes three convenience once-functions: // Calls f only once. func (o *Once) Do(f func()) // Returns a function that calls f only once. func OnceFunc(f func()) func() // Returns a function that calls f only once // and returns the value from that first call. func OnceValue T) func() T // Returns a function that calls f only once // and returns the pair of values from that first call. func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) # Object pool

      The sync.Pool type helps reuse memory instead of allocating it every time, which reduces the load on the garbage collector:

      pool := sync.Pool{
          New: func() any {
              buf := make([]byte, 1024)
              return &buf
          },
      }
      
      // Only allocates 4*1024 B, despite 4000 loop iterations.
      var wg sync.WaitGroup
      for range 4 {
          wg.Go(func() {
              for range 1000 {
                  buf := pool.Get().(*[]byte)
                  sink = buf
                  pool.Put(buf)
              }
          })
      }
      wg.Wait()
      
      
      
      Memory allocated: 4 KB
      

      Get takes an item from the pool. If there are no available items, it creates a new one using New (which we have to define ourselves, since the pool doesn't know anything about the items it creates). Put returns an item back to the pool.

      Things to keep in mind:

      • New should return a pointer, not a value, to reduce memory copying and avoid extra allocations.
      • The pool has no size limit. If you start 1000 more goroutines that all call Get at the same time, 1000 more buffers will be allocated.
      • After an item is returned to the pool with Put, you shouldn't use it anymore (since another goroutine might already have taken and started using it).

      # Atomics

      An operation without synchronization can only be truly atomic if it translates to a single processor instruction. Such operations don't need locks and won't cause issues when called concurrently (even the write operations).

      There are only a few atomics, and they're all found in the sync/atomic package:

      Int32     Bool
      Int64     Value
      Uint32    Pointer
      Uint64
      

      Each atomic type provides the following methods:

      • Load reads the value of a variable.
      • Store sets a new value.
      • Swap sets a new value (like Store) and returns the old one.
      • CompareAndSwap sets a new value only if the current value is still what you expect it to be.

        var n atomic.Int32 n.Store(10) swapped := n.CompareAndSwap(10, 42) fmt.Println("CompareAndSwap 10 -> 42:", swapped) fmt.Println("n =", n.Load())

        CompareAndSwap 10 -> 42: true n = 42

      Numeric types also provide an Add method that increments the value by the specified amount.

      All methods are either translated into a single CPU instruction or are otherwise guaranteed to be atomic, so they are safe to use from multiple goroutines.

      The composition of atomics is always non-atomic:

      var delta atomic.Int32
      var counter atomic.Int32
      
      func increment() {
          // Not atomic; causes a race condition.
          delta.Add(1)
          sleep(10)
          counter.Add(delta.Load())
      }
      
      // After 100 concurrent increments,
      // the final value is NOT guaranteed.
      
      
      
      counter = 9386
      

      A bulletproof way to make a composite operation atomic and prevent race conditions is to use a mutex:

      var delta int32
      var counter int32
      var mu sync.Mutex
      
      func increment() {
          // Atomic; doesn't cause a race condition.
          mu.Lock()
          delta += 1
          sleep(10)
          counter += delta
          mu.Unlock()
      }
      
      // After 100 concurrent increments, the final value is guaranteed:
      // counter = 1+2+...+100 = 5050
      
      
      
      counter = 5050
      

      Sometimes you can use an atomic type instead of a mutex to exit early:

      type Gate struct {
          closed atomic.Bool
      }
      
      func (g *Gate) Close() {
          if !g.closed.CompareAndSwap(false, true) {
              return // ignore repeated calls
          }
          // The gate is closed.
          // We can free resources now.
      }
      

      # Testing

      If your concurrent program uses channels or custom types with synchronization methods like Wait, you can use those in your tests. This way, your tests won't be much more complicated than if the code were synchronous:

      // Calc calculates something asynchronously.
      func Calc() <-chan int {
          out := make(chan int, 1)
          go func() {
              out <- 42
          }()
          return out
      }
      
      
      
      func Test(t *testing.T) {
          // Wait for the Calc goroutine to finish.
          got := <-Calc()
          if got != 42 {
              t.Errorf("got: %v; want: 42", got)
          }
      }
      
      
      
      PASS
      

      If there aren't any suitable synchronization "handles" in the code you're testing, you can use the synctest package. It exports two functions:

      func Test(t *testing.T, f func(*testing.T))
      func Wait()
      

      synctest.Test runs an isolated bubble. The bubble uses a fake clock, and you can manually control goroutine synchronization with synctest.Wait.

      synctest.Wait blocks until all goroutines in the bubble — except the one that called Wait — have either finished or are durably blocked. This lets you wait for a specific goroutine to finish or get blocked, so you can check the program's state:

      // NewProc starts the calculation.
      func NewProc() *Proc {
          p := &Proc{done: make(chan struct{})}
          go func() {
              p.res = 42
              <-p.done // (X)
              p.res = 0
          }()
          return p
      }
      
      
      
      func Test(t *testing.T) {
          synctest.Test(t, func(t *testing.T) {
              p := NewProc()
              defer p.Stop()
      
              // Wait for the goroutine to block at point X.
              synctest.Wait()
              if got := p.Res(); got != 42 {
                  t.Fatalf("got %v, want 42", got)
              }
          })
      }
      
      
      
      PASS
      

      The fake clock in synctest.Test move forward only if: ➊ all goroutines in the bubble are durably blocked; ➋ there's a future moment when at least one goroutine will unblock; and ➌ synctest.Wait isn't running. Thanks to this, time-dependent tests run instantly:

      // Calc processes a value from the input channel.
      // Times out if no input is received after 3 seconds.
      func Calc(in chan int) (int, error) {
          select {
          case v := <-in:
              return v * 2, nil
          case <-time.After(3 * time.Second):
              return 0, ErrTimeout
          }
      }
      
      
      
      func Test(t *testing.T) {
          synctest.Test(t, func(t *testing.T) {
              ch := make(chan int)
              got, err := Calc(ch) // runs instantly
      
              if err != ErrTimeout {
                  t.Errorf("got: %v; want: %v", err, ErrTimeout)
              }
              if got != 0 {
                  t.Errorf("got: %v; want: 0", got)
              }
          })
      }
      
      
      
      PASS
      

      The following operations durably block a goroutine:

      • A blocking send or receive on a channel created within the bubble.
      • A blocking select statement where every case is a channel created within the bubble.
      • Calling Cond.Wait.
      • Calling WaitGroup.Wait if all WaitGroup.Add calls were made inside the bubble.
      • Calling time.Sleep.

      Blocking on mutexes, I/O, or system calls is not considered durable, and the synctest bubble can't handle them.

      # Scheduling

      At the hardware level, CPU cores are responsible for running parallel tasks.

      At the operating system level, a thread is the basic unit of execution. There are usually many more threads than CPU cores, so the operating system's scheduler decides which threads to run and which ones to pause.

      At the Go runtime level, a goroutine is the basic unit of execution. The runtime scheduler runs a fixed number of OS threads, often one per CPU core. There can be many more goroutines than threads, so the scheduler decides which goroutines to run on the available threads and which ones to pause. The scheduler keeps switching between goroutines to make sure each one gets a turn to run on a thread, instead of waiting in line forever.

        CPU                  OS                   Go runtime
      ┌──────────┐  run on ┌──────────┐  run on ┌────────────┐
      │ Cores    │ <────── │ Threads  │ <────── │ Goroutines │
      └──────────┘         └──────────┘         └────────────┘
      

      This is how Go handles concurrency.

      Goroutine scheduler

      The goroutine scheduler's job is to run M goroutines on N operating system threads, where M can be much larger than N. Here's a very simplified version of it's algorithm:

      • If there's a free thread, assign it a goroutine from the queue.
      • If a running goroutine gets blocked (for example, while reading from a channel), put it back in the queue and assign a different goroutine to the thread.
      • If a running goroutine gets stuck in a syscall, start a new thread to run other goroutines until the blocked goroutine finishes the syscall.
      • Check the running goroutines every 10 ms. Preempt long-running goroutines and return them to the queue to prevent starvation.

        ┌─────┐┌─────┐┌─────┐┌─────┐ │ G17 ││ G18 ││ G19 ││ G20 │ queue └─────┘└─────┘└─────┘└─────┘

        ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ G15 │ │ G16 │ │ G13 │ │ G14 │ running └─────┘ └─────┘ └─────┘ └─────┘ │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Thread E │ │ Thread F │ │ Thread C │ │ Thread D │ └──────────┘ └──────────┘ └──────────┘ └──────────┘

        ┌─────┐ ┌─────┐ │ G11 │ │ G12 │ syscalls └─────┘ └─────┘ │ │ ┌──────────┐ ┌──────────┐ │ Thread A │ │ Thread B │ └──────────┘ └──────────┘

      The number of threads running Go code is controlled by the GOMAXPROCS environment variable or the runtime.GOMAXPROCS function.

      A goroutine is a structure that starts out using about 2 KB of memory, mostly for its stack. The stack can grow if needed. Since goroutines are so lightweight, you can run tens of thousands or even hundreds of thousands of them on a small machine.

      # Diagnostics

      To troubleshoot concurrent programs in production, we use metrics, profiling, and tracing.

      Metrics show how the Go runtime is performing, like how much heap memory it uses or how long garbage collection pauses take. Each metric has a unique name and a value, which can be a number or a histogram.

      You can use the runtime/metrics package to get a complete list of metrics or check the values of specific ones:

      samples := []metrics.Sample{
          {Name: "/sched/gomaxprocs:threads"},
          {Name: "/sched/goroutines:goroutines"},
      }
      metrics.Read(samples)
      
      for _, s := range samples {
          fmt.Printf("%s: %v\n", s.Name, s.Value.Uint64())
      }
      
      
      
      /sched/gomaxprocs:threads: 8
      /sched/goroutines:goroutines: 1
      

      In practice, people rarely do this manually. Instead, all metrics are automatically exported using Prometheus or OpenTelemetry libraries.

      Profiling helps you understand exactly what the program is doing, what resources it uses, and where in the code this happens. Go uses a sampling profiler that's suitable for production.

      The most commonly used profiles are CPU, which shows how much processor time each function uses, and heap, which shows how much heap memory each function uses. Goroutine, block, and mutex profiles help identify problems related to concurrency.

      The easiest way to add a profiler to your app is by using the net/http/pprof package. To collect a profile with the given name, call the /debug/pprof/{name} endpoint. To view the collected profile, use the go tool pprof utility:

      go tool pprof -proto \
        "http://localhost:6060/debug/pprof/profile?seconds=N" > cpu.pprof
      go tool pprof -http=localhost:8080 cpu.pprof
      

      You can also profile manually:

      // CPU profile.
      file, _ := os.Create("cpu.prof")
      defer file.Close()
      pprof.StartCPUProfile(file)
      defer pprof.StopCPUProfile()
      // ...
      
      
      
      // Any other profile.
      file, _ := os.Create(name + ".prof")
      defer file.Close()
      pprof.Lookup(name).WriteTo(file, 0)
      

      Tracing records certain types of events while the program is running, mainly those related to concurrency and memory. When the profiling server from the net/http/pprof package is running, call the /debug/pprof/trace endpoint to collect a trace. To view the results, use the go tool trace utility.

      You can also collect a trace manually:

      file, _ := os.Create("trace.out")
      defer file.Close()
      trace.Start(file)
      defer trace.Stop()
      // ...
      

      You can set up automatic tracing with a sliding window that's limited by size or duration. This is called "flight recording". It lets you always keep a recent trace available in case something goes wrong:

      cfg := trace.FlightRecorderConfig{
          MinAge:   5 * time.Second,
          MaxBytes: 3 << 20, // 3MB
      }
      rec := trace.NewFlightRecorder(cfg)
      rec.Start()
      defer rec.Stop()
      

      # Final thoughts

      We've covered a number of Go tools for writing concurrent programs:

      • Goroutines for running concurrent tasks.
      • Channels and select as flexible communication tools.
      • Timers and tickers for working with time.
      • Context for canceling operations.
      • Wait groups for synchronizing goroutines.
      • Mutexes to prevent race conditions.
      • Condition variables for signaling events.
      • Once for safe one-time initialization.
      • Pools to reduce garbage collector load.
      • Atomic operations.

      If you like the book, please recommend it to your friends or colleagues. If you're interested, check out my other books and projects.

      I'm glad you finished the book. Thank you, and I'll see you next time!

    7. 🔗 smol-machines/smolvm smolvm v1.19.1 release

      What's Changed

      • Let a cascade delete finish when a clone vanishes mid-cascade by @LoganGrasby in #1419
      • fix: propagate machine exec database lookup errors by @sgrove in #1331
      • Reconcile bounded late VM exit after shutdown acknowledgment failure by @sgrove in #1329
      • Size the pack VM's storage disk from the image manifest by @Bnjoroge1 in #1322
      • Resolve image manifests from the reference's own registry only by @BinSquare in #1420
      • Link the Node, Python and Rust SDKs from the README by @BinSquare in #1421
      • Restructure the README around what smolvm is for by @BinSquare in #1423
      • Honor a local image's WORKDIR, ENV and USER in machine run by @BinSquare in #1424
      • Specify the checkpoint format and name checkpoint files .checkpoint by @BinSquare in #1422
      • Bump the workspace to 1.19.1 by @BinSquare in #1425

      Full Changelog : v1.19.0...v1.19.1

    8. 🔗 hacker news ida pro references New comment by zellkernel in "Reverse Engineering Fortinet with Ablation" rss

      Ablation is a reverse engineering framework that provides the exact same core disassembly, decompilation, and binary analysis capabilities as industry-standard tools like Ghidra, IDA Pro, and Binary Ninja. Combined with an LLM, it transforms into a fully autonomous reverse engineering tool.

      Ablation is built for the modern landscape, and more importantly, the human.

      Now reverse engineering is accessible to anyone. No matter your wallet or your barrier of entry into education, you can learn about reverse engineering as you reverse engineer.

    9. 🔗 Register Spill Joy & Curiosity #101 rss

      Last week I was on Matt Swanson's podcast and we ended up sharing thoughts and vague predictions about programming languages and frameworks. Matt said that he'd be going to Rails World the week after and that he wouldn't be surprised if DHH said "Rails is over." Prescient. DHH didn't exactly say that Rails is over, but, well, some people did call the keynote a "funeral."

      But that shouldn't be surprising, right? The way we've treated languages and frameworks for the past, say, twenty years is at odds with the fact that writing code by hand is on its way out.

      I am not sure how exactly this will play out, but here are some loose thoughts:

      • Frameworks are no longer the biggest developer productivity lever. Agents are a hundred times bigger.

      • Syntax doesn't really matter anymore, as long as agents can write it well.

      • Tool ergonomics don't matter that much either, do they? Previously, I loved that go comes with go build and go test and go run, but now I wouldn't care if those commands were seventeen times longer.

      • But I think shared abstractions are still worth it. A framework gives you a pre-defined way to access a database, to do auth, to divide things into production and development… That's still handy. Not because it would cost tokens to build it myself (won't matter in the future, see below), but because I just don't want to think about it.

      • What will matter a lot in the future: performance characteristics, resource usage, failure modes, observability, debuggability, deployments, rollbacks. And all of that needs to be legible to the agent. I've tried developing something with the often praised Cloudflare Durable Objects, on which you can run JavaScript, and it was a disaster: the agent constantly thought it was writing normal Node.js JavaScript; it didn't know the runtime characteristics of the Durable Objects; it couldn't easily access the logs. In fact, there are no logs that tell you when your code gets evicted and when it gets resumed… You want the opposite to be the case: the agent should know from looking at the codebase how it will be executed and how it can see that.

      • The big force is that we're switching from "I prefer this language because I enjoy working with it" to "I prefer this language because my agent can get great results with it." Now, how would your preferences change if you switched from driving a car to controlling it remotely? You wouldn't care about heated seats and AC, would you? But you'd care about how fast it can brake, I assume.

      • Ecosystems will change. I can't remember the last time I browsed through GitHub to find a library to do a thing. The old NPM credo of "many, many tiny modules" seems even sillier now than it did ten years ago.

      • Sometimes I wonder whether the thing that makes some developers say that agents will change everything and others that they can't write good code is the language they used with the agent. 99% of the code I've had agents write was in TypeScript. Not a language I love, but, hey, who cares? And agents seem great at it. I wonder what my thoughts would be if I still were writing Rust.

      • Then again: I no longer think that "is the language well-represented in the training data?" matters as much as I thought it would. Intelligence generalizes and I've seen agents just crush custom DSLs that have not shown up in any data, ever. We previously said about humans that "if you learned 5 different languages, you kinda know them all" -- maybe that's what's going to happen with agents too? And it kinda makes sense, right? Why wouldn't a frontier model like Astra or Fable be able to use a new language as long as it can run it and have a feedback loop?

      • Very pessimistic on the future of "paper-over" languages and frameworks. You know: this language but with nicer syntax. CoffeeScript, if you're old enough to remember. Haml, Sass, Less -- not sure. What about Elm or ClojureScript? Hmm.

      • A lot of testing frameworks started with TDD in mind: you write a test in which you describe the behavior you want, you run the test to see it fail, you make it pass. Then, with growing adoption of tests, people started using those very same frameworks to add tests after everything already worked. Regression tests. Now we have the very same frameworks being used by agents to write tests god knows how and essentially no one looks at these billions of lines of test code that are generated every day now. Will we still have describe and it blocks in ten years? Just like some terminal emulators still mention baud rates? I do think that the models will get so good that they don't "need" unit tests in the same way humans needed them: to make sure something works. But maybe they won't stop writing them and we'll end up with effectively useless tests piling up?

      • I'd be incredibly surprised if formal methods and strong static typing had the boom that their fans say they will have. Vitamins, not painkillers; worse is better, etc.

      • I have three little apps that I use every day and that I had the agent write, and I have zero clue what language they're written in. I think it's JavaScript?

      • Porting from one language to another seems to be a completely different thing now compared to three years ago.

      Again: I don't know where we'll end up, but I do think being aware of the forces at play is important. It's also fun stuff to think about.

      Before we get to the J &C juice: I'll be in NYC with the Amp team Oct 5-11 and in SF the week after, Oct 12-17. My schedule will be busy and chaotic, but if you're around and want to grab a coffee, let me know!

      • We recorded a new Raising An Agent episode after I said to Quinn: "Man, I'm so full of hot takes today. I need to record a video." So, there it is: an episode full of hot takes. From a whole engineering org making everyone use Qwen, to why GDP isn't increasing if you aren't letting your agents go vertical , to why you're wasting time if you're waiting on your agent instead of the other way around, to why I've been very disappointed by a lot of "software engineers" in the last two years.

      • Expect this to continue: "If we combine all this, we see about 2.5 orders of magnitude decrease in token cost in the last year. Models are about 100x as cost-efficient per-task. Hardware is about 1.3x as energy-efficient per-token. Engines are about 1.4x as energy-efficient per-token." The strongest force in technology today. I stand by what I wrote in last week's predictions about the future of software development: "Tokens are the new computing paradigm. Everything will be re-made on top of it."

      • So, DHH lit the Ruby & Rails world on fire by giving a keynote in which he doesn't talk about Rails all that much. Instead, he said what others (hey , what's up) have said for at least the last six months: writing code by hand is over; these agents are really good; choosing Ruby over other languages due to its developer friendliness doesn't make a lot of sense anymore; old engineering tradeoffs should be revisited. But he said it in a way that only DHH can. He's very, very good at boiling things down to their essence and turning them into statements that make you choose whether you're for or against it. It's impressive, honestly. Read the Hacker News comments to get a taste. Now, predicting the next six months, I wonder when the Omarchy community will run into the question of: wait, so we now have this malleable operating system, but… if agents write and debug all the code, why do we even need it?

      • Thomas Dullien, or: Halvar Flake, gave a presentation: An age of experimentation. It's really good and I highly recommend you click through it. Here, to give you a taste: "Claim: Determinism is dying, and it's unclear how much will remain."

      • New Peter Thiel interview! Say about him what you want (and there's a lot to say), but his ability to read the vibes in the world seems to be rarely matched. What makes this interview also interesting is that the interviewer is Mathias Dopfner, quite the controversial billionaire himself. At some point in the interview, Thiel compares the twenty richest people under 30 in the US with those in Germany and says that the twenty in Germany all inherited their wealth. Guess how Dopfner got his shares of Axel Springer SE? Heavily discounted and some as gifts, from Springer's widow.

      • Attention is all you have: "If, like me and most people, you spend the major part of your day focused on your device, there's no doubt it's affecting you. And when you let someone else dictate what appears on your screen, it's the same as giving them the key to your brain."

      • "But there are pleasures to be had from books beyond being lightly entertained. There is the pleasure of being challenged; the pleasure of feeling one's range and capacities expanding; the pleasure of entering into an unfamiliar world, and being led into empathy with a consciousness very different from one's own; the pleasure of knowing what others have already thought it worth knowing, and entering a larger conversation."

      • Martin Fowler: I don't like LLMs. "I don't like them. They talk to me in this grating LLM-voice, an uncanny valley of talking to a real human. They confidently bullshit me - often giving me useful, helpful answers. But also just making stuff up with the same assurance - and with only a veneer of fake remorse when I call them out on it." He's got a point. Many times a day I think to myself "god, shut UP " when the model comes back with whatever the latest equivalent to "you're absolutely right!" is: spines, seams, belts and braces (or suspenders). But… less and less so? I parse their output more like a receipt I get handed in a shop or in a restaurant: skip the stuff at the top, ignore the gibberish at the bottom, zoom in on the stuff there in the middle.

      • I didn't know about the Moving Image Archive but was delighted when I came across its collection of animated maps.

      • This seems very neat and makes me want to build something with Go: Platform-independent SIMD.

      • "I text him that night and I said, 'Hey, I want to ask you a question. Can I coach you hard?' […] Then the next day he sees me in pre-practice, and I said, 'Just let me explain this. Do you see yourself being in the Hall of Fame someday?' And he said, 'Yeah.' And I said, 'All right. Well, I don't think your trajectory right now is steep enough to make that goal happen. I think your trajectory is five Pro Bowls, a couple All-Pro teams, phenomenal career, all-time leading rusher, but I think your trajectory, and I think your practices have to be…' And then he starts complaining to me, and I said, 'I thought you told me I could coach you hard.' And then, you know, it hit him."

      • How to Unclench. I finally read this after it made a big splash last week. It's a much faster read than I thought it would be. It's like a neat little mini-book.

      • Robert O'Callahan: "I'm resigning from Google today. This has not been an easy decision. I love my colleagues and my work environment, and being paid handsomely to solve fun puzzles has been amazing. But my team's goal is ultimately to make AI much cheaper and lower-latency, and I don't think that's good for people right now: I firmly believe AI progress is currently far too rapid (and I have doubts about the destination too)."

      • "I started to explain how it happened when they cut me off with 'Michael, I don't want the details'." Good stuff.

      • Thomas H. Ptacek and Kurt Mackay are leaving fly.io to build a phone: "Today, almost all software comes from expert strangers. But soon, strangers will stop supplying our apps, and instead ship just their building blocks. Sure, there will still be megaproject browsers and word processors. But there'll be thousands of times more applications that pull in 1/7th of the guts of a word processor to solve some idiosyncratic work or home life problem for somebody who doesn't know what a for-loop is. […] That's what we're working on: a platform that is the device we would want to have in the world I just described. So: we're building a phone." I nod my head to the boldness of entering one of the most competitive markets of all time.

      • It's totally not the point of this Obie Fernandez post, but I can't stop thinking about this part here: "Trying to make a point, I hit enter to accept Fable's first suggestion. Minutes later I hit enter again, and then again, choosing to delete some dead code. We start making a PR. My friend asks me to make sure it's set to draft. Sure, whatever. Fable does its thing. My friend checks the diff. It's a simple deletion of dead code and associated unit tests. I want to push on, but my friend begs me to stop. 'You don't understand Obie, I can't just do what you're doing, man.' I challenge him to explain why not. He explains that he has a boss and teammates and that he can't just make changes like that, he has to present plans and execute on them." It made me remember what it feels like to work in such teams, where you can't make decisions alone, where you're not trusted to make a call like "I'm going to refactor this API" or "I'm going to delete this" or "I'm going to add a new feature that lets us…" without having reached a consensus with the team. Remembering that made me feel sad, thinking: "wow, imagine what it's like to now have AI but no power, no trust, no freedom to use it?" There's no way around it: if you box AI into this little corner where all it can do is change code on your local machine and help you push it up as a PR, you're holding the leash at a fraction of its real size.

      • Intellectuals are F*cking Idiots: "Reality always wins. But Intellectuals are rewarded for their models, not reality. And the data and analysis that looks elegant on paper is often disastrous on the ground. Yet, when their models are contradicted by reality, most intellectuals don't have the courage to accept the reality, instead they double down on their models …and this is what turns them into idiots." If it's nothing else, this was entertaining!

      • I thought of this John Carmack post again, so here it is, again: "Make better decisions and fill your products with 'Give a Damn'!"

      • This is fantastic: Fixing the Portobello Police Station Clock. It's a hack, it's nerdy, it's real blogging. This is the type of stuff that made me fall in love with the Internet.

      Thoughts on the future of programming languages? Subscribe here:

    10. 🔗 HexRaysSA/plugin-repository commits sync repo: +4 releases, -1 release rss
      sync repo: +4 releases, -1 release
      
      ## New releases
      - [augur](https://github.com/0xdea/augur): 0.10.3
      - [ida-rpc](https://github.com/bkerler/ida_rpc): 0.2.0
      - [patching-ng](https://github.com/mahmoudimus/patching-ng): 0.5.0, 0.4.0
      
      ## Changes
      - [patching-ng](https://github.com/mahmoudimus/patching-ng):
        - host changed: mahmoudimus/patching → mahmoudimus/patching-ng
        - removed version(s): 0.3.0
      
  4. September 25, 2026
    1. 🔗 smol-machines/smolvm smolvm v1.19.0 release

      What's Changed

      • Honor --credential when creating a machine from a pack by @BinSquare in #1400
      • Pin the Nix flake to 1.18.2 with its real release hashes by @BinSquare in #1398
      • Launch a credentialed machine with its credentials on every start and resume by @BinSquare in #1402
      • Report a forked machine's own creation time by @LoganGrasby in #1407
      • External egress interceptor by @ankrgyl in #1405
      • Let a lease request wait for a clean pool worker by @LoganGrasby in #1408
      • Keep the pool controller from retiring a worker a lease just claimed by @LoganGrasby in #1406
      • Keep external interception fail closed across VM restarts by @BinSquare in #1409
      • Let a virtio-net machine start after machine update --no-net by @LoganGrasby in #1410
      • Upload captured checkpoints straight to object storage by @BinSquare in #1403
      • Fail a Windows virtio-net launch when a published host port is in use by @nitzzzu in #1416
      • Share one database handle per process by @LoganGrasby in #1417
      • Retry a read-then-write DB transaction that loses the write race by @LoganGrasby in #1413
      • Don't retire a paused leased fork pool worker by @LoganGrasby in #1414
      • Refuse to implicitly start a paused machine by @LoganGrasby in #1415
      • Retry an agent connect the guest hangs up during the handshake by @BinSquare in #1412
      • Keep the guest agent answering pings while every work slot is busy by @BinSquare in #1411
      • Take API credential values from the API, never from the host environment by @BinSquare in #1401
      • Bump libkrun for the restored-inode, vsock restore, kqueue and fork generation fixes by @BinSquare in #1399
      • Read the machine name from SMOLVM_MACHINE_NAME when --name is omitted by @BABTUNA in #1392
      • Bump the workspace to 1.19.0 by @BinSquare in #1418

      New Contributors

      Full Changelog : v1.18.2...v1.19.0

    2. 🔗 r/Harrogate Hedge & tree recommendations rss

      Can anyone suggest reliable, responsive and available hedge and tree care people? I just moved to the area and need to line someone up for the annual hedge trim, but have reached out to three businesses so far and none have even replied

      submitted by /u/LittleSadRufus
      [link] [comments]

    3. 🔗 Mr. Money Mustache Will the AI Bubble Destroy our Retirement? rss

      Wow, how about that stock market?

      It’s a phrase we keep having to dust off and use again, as the years go by and the market keeps surprising us.

      When it crashes, some of us worry because we see our retirement stash shrinking.

      But even when it rises to record levels and then to super-duper-crazy record valuations, we find reason to worry. Because that just means an even bigger crash is coming, right? Especially when this boom-bubble is built on the back of something as frenzied as the present Artificial Intelligence boom… right?

      For those who have been happily tuned out of this drama and just enjoying life: Congratulations! Keep up the great work. But just to give you a quick background for the purposes of this article, here’s the AI story in three points:

      • Over the past few years, AI has reached a level where it can do complex thinking and reasoning tasks that most of us thought might not happen in our lifetimes. It’s shockingly useful.
      • This has led to the fastest worldwide adoption of any technology in history: over 1.5 billion people are already using it, as are most large companies
      • And this has caused a crazy cycle of growing sales, investor enthusiasm and data center buildout that is now the largest investment cycle humans have ever made in anything: 1.8 trillion dollars has been spent since just 2022, with another trillion going out the door next year alone. Which you can compare to:

      • The cost of the 2700-foot-tall Burj Khalifa, the highest skyscraper in the world (about $2 billion in today’s dollars)

      • Or the entire US interstate highway system, 48,000 miles of wide, flat, highly engineered roads (and 55,000 massive bridges) which cross countless mountain ranges, rivers and canyons: $660 billion in today’s dollars.

      So where’s the problem?

      Investors worry that while AI is definitely a major invention, the mania around it is still too much, too fast, and thus we are due for an even bigger version of the dot com crash we had in the early 2000s. Which happens to be an interesting subject for me, since that was the boom that boosted my own early career and got me on the path to early retirement … before the ensuing crash almost cost me my job and my citizenship application.

      As an aging Internet Financial Guru, I have the privilege of getting lots of questions about this investment cycle, just as I have about past booms and busts. And to address them, I decided to not only write this blog article but also create a little talk about it - which I already gave for the first time at an event called Camp FI Midwest earlier this month. I also hope to polish it up and deliver an improved version at the Bogleheads conference in November.

      So what that means is that today you not only get a blog post, but a few silly presentation slides to go with it for extra entertainment. All with no need for a plane ticket or all that hassle of leaving your house. So let’s get into it!

      Will the AI Bubble Ruin our Retirement?

      -

      So I recently hit 21 years of retirement. This means I’ve grown pretty comfortable with the idea. But it still threw me for a loop one time when a friend asked me this question:

      “How can you be retired, and comfortable in your retirement, and sleep at night, with everything that’s going on in the world? Aren’t you worried?”

      And I was like, “No, what should I be worried about?”

      But if you’re a news watcher and a worrier yourself, you can probably think of a few things I should be worried about. Perhaps stuff like this:

      2026 worries

      You’ve got our corrupt and/or dangerous politicians like Trump and Putin menacing the world. Inflation eroding our purchasing power, the AI Bubble, the towering US National Debt, the Iran war and the Ukraine war and all these other wars that are just on the verge of tipping us all into destruction.

      But it turns out these were not the things my friend was asking about.

      Because she actually asked me this question over twenty years ago… In the year 2006. So back then we were worried about entirely different things, right?

      2006 worries

      Back then we had corrupt and/or dangerous leaders like Bush, Hussein and of course Bin Laden. There was still a war in the middle east but it was Iraq instead of Iran. We were still worried about the National Debt and Inflation. And we also worried about our overvalued stock market. But back then it was because of the housing bubble instead of the AI bubble. And instead of AI taking our jobs it was going to be outsourcing to India and China.

      Oh and here’s an interesting one: There was a big debate over Peak Oil, and whether the world was doomed because fossil fuel use was always increasing while supply was bound to decrease. And it turns out the opposite happened as you'll see below.

      So then after all this worry in 2006, what ended up happening?

      -

      Well we did get the Great Financial Crisis, which was caused by a combination of irrational exuberance over increasing house prices combined with a foolish degree of fancy leverage in the financial instruments used to issue mortgages. And it was the biggest crash since the Great Depression.

      But even in that craziest of situations, look how minor it looks in the big picture. A $100,000 investment still ended up ballooning into $852,000 today. And if you look carefully in here you can just see the Covid Crash tucked in there in 2020. Remember that?

      And not only that, the rest of the world got a lot better too:

      -

      The portion of people living in extreme poverty dropped from 20% in 2005 to only 8% over the next 20 years. Infant mortality - children who die in their first year of life - was cut in half. Clean energy became a thing as solar panels became about 40 times cheaper. So solar generation now makes up the vast majority of all the new generation we add today (the equivalent of 647 nuclear power plants of peak capacity added last year, but with much cheaper and easier solar panels). And sales of pure electric cars have gone from zero up to about a quarter of all the cars sold on Earth last year, on its way to 100%, which is where they already are in Norway.

      So if we circle back to that question my friend asked me 20 years ago: do you think I should have focused my energy on worrying about an uncertain future, or optimism?

      Which of course brings up the question: Is it different this time?

      Maybe the US national debt is finally big enough to really start messing with us. Maybe the AI bubble is way bigger than the housing bubble and we won’t bounce back from this next stock market crash. Or maybe AI will spin out of control into a super intelligence that takes over the planet and realizes it does not need us any more.

      And the financial media loves to spin a good scary story about all of this. But you know what the fear mongers all seem to be missing?

      It’s the fact that at the core, our prosperity does not come from the financial system or the stock market, which are just some made-up numbers on some computers.

      Really, Prosperity comes from Productivity.

      -

      We first covered this lesson right here on MMM, in the 2014 Classic entitled, “Why we are not really all doomed”. And sure enough, twelve years later the timeless lessons therein remain just as true now as they were back then.

      And the lesson is really simple: the world of capitalism is always choppy and prone to wild swings. But if you peek beneath the waves, there are real people in there, doing real work and coming up with real inventions.

      In fact, the very idea of an “economy” or a financial system is just another human invention. If the whole system crashes because we don’t like the numbers we see in there, we can literally make up some new numbers and then get back to work. Which is exactly what we did in 2008 and many times before that, and it seems to have worked just fine.

      Meanwhile, we still have every tool we ever invented to boost our productivity. And our standard of living, and our economic growth, and our possibility of retiring early, all come from high productivity.

      But even Productivity and Prosperity aren’t all that important.

      Because once we reach a certain standard of living, human happiness kind of tops out in that dimension. In first-world countries, we blew past those minimum requirements several decades ago. And then we start looking for other factors to maximize our overall happiness. All anybody really wants is to lead the happiest, most satisfying life they can manage

      And when you think of it that way, your wealth is really just part of your life situation, which is part of this little green slice of the happiness pie.

      Approximate non-scientific factors in happiness

      Genetics is unfortunately the biggest slice - some people are just plain happier than others. But there are also quite a few choices that are within your control.

      Focusing on good close relationships and being kind to people. Practicing Gratitude and Optimism as your lifelong philosophy. And making sure you pack as many healthy (outdoor) activities into your days as you can.

      So, I didn’t invent anything new in this blog post. And most of it is just a repackaging of the same stuff I’ve been writing about since 2011. And yet some regular MMM readers who presumably know all this stuff are STILL afraid. Afraid to retire, afraid of things in their unknown future. Can we fix this?

      I think the biggest problem is Fear Itself.

      -

      The problem is that as a Human Being, you are basically a Danger Detection Machine. And this pervasive background fear is just a trait we evolved to protect ourselves from danger.

      But there’s a weird thing about our detection machinery. We can adapt and learn to function even in very dangerous environments, but when the danger goes away, we keep looking for stuff to worry about. In other words, there is a problem with fear: for many of us, it never really goes away. It just keeps moving the goalposts.

      While Nature has wisely endowed us with a fear of predators and disease, if you solve those things you’ll just start worrying about whether or not you can get enough food. And supplies, and protecting yourself against scarcity.

      If you succeed, next you’ll be worrying about if you can fit in with your tribe, because if you don’t, you might be exiled.

      And if you don’t have to worry about that, you’ll go back to worrying about money for different reasons. Thanks to our tendency to use comparisons rather than absolutes (sometimes called "anchoring bias" and the "contrast effect") when judging our lives, just getting out of poverty isn’t enough. We just move on to wanting more money.

      And then, when some people get enough money to have a comfortable upper- middle-class lifestyle, they take the logical next step of starting a Homeowner's Association, so they can worry about the unauthorized weeds on their neighbor’s lawn. Or maybe a Political Action Committee in hopes of controlling the color of their future neighbors' skin.

      And even if all the lawns and gardens are green and perfect and people get really rich, they just move on to worry about tax strategy, leaving a huge inheritance for their children, and avoiding RMDs.

      Required minimum Distributions. This is when, if you get to 72 years old and you have too much money , the government will start making you withdraw some of that money so you can spend it and pay taxes on it. And people actually worry about this. I shit you not.

      But wait! You can actually fight back against fear. It’s just a bit tricky because it requires some self awareness. But you have the advantage, because you have the ability to think rationally. And your fear is quite clearly irrational.

      -

      And the first step is to understand your own history. Are you afraid of certain things because of your childhood?

      I might have been afraid of running out of money, because in my family there was always a perceived shortage of the stuff, possibly enforced by my Dad’s scarcity mentality. And he was aware that his own fears of scarcity came from childhood as well: he grew up in a truly low income family in the 1940s and 50s, under the care of parents who had lived through the Great Depression of the 1930s.

      Even more significant is that survivors of trauma and abuse will very likely end up with fear issues in adulthood. It’s natural, and unfortunately abuse is way more widespread even in this country than we would like to admit. This makes it harder to eliminate, but it still helps to understand that your past is what is causing these feelings, rather than an objective understanding of the present.

      Identify the Fear: One you understand yourself, it helps to talk through the nature of your fear in more detail. This is also often taught in therapy. What would happen if it really came true? What would the worst case scenario be? It’s often not all that bad.

      In one post, why you’ll probably never run out of money, I went to great lengths to imagine what it would mean for a wealthy person like yourself to actually let the well run dry, and let’s say you’ll probably never come close.

      Once you identify the fear, you can start taking action. And one of the best and most accurate slogans ever is that action cures fear. It’s because it makes the unfamiliar, familiar. And you get confidence that you can really create change. And then that learning creates Resilience - the ability to adapt to ANY new situation, which is a trait also known as Badassity. And with sufficient Badassity, nothing is scary.

      -

      Think about it: if you had the skills and abilities to handle ANY situation, skills in the mental and physical and even the realms of emotion and wisdom, would you really have to worry about anything?

      The next thing you can do is tune out all the unnecessary crap from your daily mental diet. And for most Americans, this means the daily news.

      The news is not helping you to keep informed, it’s just poisoning your mind with a hand-picked selection of the scariest stories of each day. If you want to learn about something, go find a book on the subject. And if you want to make a difference, the only thing that counts is not the news stories you watch - it's only the actions that you take - in other words, your Positive Behaviors.

      As a human being, you are wired to solve problems with both your body and your mind, and you are meant to do it outdoors. So if you spend all your days in a house and a car and in an office using the computer, of course you are going to have some mental health problems. This should not be a surprising thing you need to ask your therapist about, it is an expected result of not living the life that you were literally created to live as a Human being!

      So you need to focus on learning, moving, and solving problems. And you need your food and drink intake to be in alignment with all of this too. Life will get a lot less scary as soon as you are living life in the way you were built to live it.

      Then finally, you can start swapping out the fear-mongers in your life, for other positive, productive, optimistic people. And watch how much the positive mentality rubs off on you. In the FI community, at least the subgroup of people that I like to spend time with, nobody focuses on fear. It’s all just about living the best life we can, and helping other people do so as well.

      So now that we know how to fight back against fear, we can start using a new approach when we go into anything new. And that approach is something I like to call Everything is an Experiment.

      Everything is an Experiment

      And this applies whether you are talking about something tiny like a new paint color. But it scales up to bigger things like switching to a new car, or a new house, meeting new friends, looking for new love by going through the sometimes hell of first dates, a new job, or even quitting your last job by taking an early retirement.

      So let's bring this back around to AI, the thing that everybody seems to be afraid of right now.

      I think of our new AI era as just being another giant experiment. And because of this, I don’t find it even remotely scary. But I do find it endlessly fascinating.

      First of all, it’s a change. A potentially unknown one, and for some people that’s scary.

      But instead of just leaving it there and then doom-scrolling ominous news articles about it, why not learn why people are so excited about AI? As someone with a lifelong background in technology and economics (and a general lack of political affiliation), I can see a lot more potential upside than downside. And that mainly comes from the fact that I see AI as just a very fancy form of Cognitive Nail Gun.

      -

      It will boost our efficiency, which means our productivity, which means prosperity. Because it’s really just a super smart brain that we all get to tap into at a fairly negligible cost.

      Unfortunately, it will also displace workers like every other new productivity technology. It’s already doing so in things like entry level office jobs in software and finance. It will eventually replace car and truck drivers, and so on. But in exchange, these services will become cheaper, and new industries will be created because of the new technology. Much like the Internet itself, and then the enormous industries unleashed by smartphones and mobile apps.

      And one unfortunate side effect of these new inventions is that they tend to concentrate their rewards on the people who own them. If you own a house building company and employ a bunch of carpenters and give nail guns to the best of those carpenters, you can now lay off the other half and still get more done. If you own a company and roll out AI to the workers, you can do the same thing.

      And if you’re a big public company, your shareholders also benefit, which happens to include most of the people reading this. As you’ve seen when looking at your portfolios over the past two years.

      Artificial Intelligence is just like any other new thing that pops into your life: it's an opportunity to make some choices. And it is up to you to decide if this is something to be afraid of, or to use as a trigger for learning, action, and living a more interesting life.

      So that's my little talk (and surprisingly long blog post) on Fear versus the AI bubble.

      I hope that if you do feel fearful about this or any other issue in your future life, you'll take it as a cue to learn more about your own fear and emotions and manage them as the root cause they are, rather than just trying to avoid the symptoms. Because in the wise words of my friends The Donegans:

      -

      Everything you want in life, that you don 't already have,
      lies on the other side of Fear.
      Because if you weren't afraid to go out there and get it, you would already have it.

    4. 🔗 Hex-Rays Blog Hex-Rays IDA MCP Server rss

      Hex-Rays IDA MCP Server

      We are happy to introduce the official IDA MCP server! This free and open source software connects your IDA installation with AI agents, enabling them to disassemble, decompile, and support reverse engineering tasks. It works great with models like Gemini, Qwen, or Opus using familiar harnesses like Claude Code, Codex, or Pi. Across our internal malware analysis-focused benchmark, IDA MCP’s “code mode” architecture reduced token consumption by approximately 20% compared to popular alternatives.

    5. 🔗 3Blue1Brown (YouTube) The Phone Number puzzle rss

      Part of a monthly series of puzzles: https://momath.org/mindbenders/

    6. 🔗 pydantic/monty v1.0.0 - 2026-09-25 release

      What's Changed

      New Contributors

      Full Changelog : v0.0.23...v1.0.0

    7. 🔗 HexRaysSA/plugin-repository commits sync repo: +1 plugin, +5 releases rss
      sync repo: +1 plugin, +5 releases
      
      ## New plugins
      - [patching-ng](https://github.com/mahmoudimus/patching) (0.3.0)
      
      ## New releases
      - [augur](https://github.com/0xdea/augur): 0.10.2
      - [ida-mcp](https://github.com/hexrayssa/ida-mcp): 20260924.0.3, 20260924.0.2, 20260924.0.1
      
    8. 🔗 Filip Filmar Fuchsia Internals, Vol. II: The Build System and Toolchains rss

      Volume II of the Fuchsia Internals series takes on the part of the system that is widely regarded as opaque on first contact: the build. The opacity has one principal cause: the multi-toolchain model, in which a single source target may be compiled many times, once per toolchain context, each producing distinct outputs. Once that idea clicks, the rest follows. The full PDF is at the bottom.

      A caveat on these reports: they are auto-generated, so take the specifics with a grain of salt. In my own reading they hold up well and read as generally correct, but verify against the source before you rely on any one detail.

    9. 🔗 New Music Releases Philip Glass - The Hours (from "The Hours") rss

      Philip Glass - a new release is available:

      • 2026-09-25: The Hours (from "The Hours") (Single)

      Amazon: Canada | Deutschland | France | United Kingdom | United States

      Visit muspy for more information.

    10. 🔗 New Music Releases Max Richter - While the Flood Rages rss

      Max Richter - a new release is available:

      • 2026-09-25: While the Flood Rages (Single)

      Amazon: Canada | Deutschland | France | United Kingdom | United States

      Visit muspy for more information.

    11. 🔗 New Music Releases Paul Oakenfold - Big Brother Theme 2026 rss

      Paul Oakenfold - a new release is available:

      • 2026-09-25: Big Brother Theme 2026 (Single)

      Amazon: Canada | Deutschland | France | United Kingdom | United States

      Visit muspy for more information.

    12. 🔗 New Music Releases Linkin Park - Unshatter Film Soundtrack (Live in São Paulo) rss

      Linkin Park - a new release is available:

      • 2026-09-25: Unshatter Film Soundtrack (Live in SĂŁo Paulo) (Soundtrack)

      Amazon: Canada | Deutschland | France | United Kingdom | United States

      Visit muspy for more information.

    13. 🔗 New Music Releases Armin van Buuren - Take Me Home rss

      Armin van Buuren - a new release is available:

      • 2026-09-25: Take Me Home (Single)

      Amazon: Canada | Deutschland | France | United Kingdom | United States

      Visit muspy for more information.

    14. 🔗 New Music Releases Faithless - We Come 1 rss

      Faithless - a new release is available:

      • 2026-09-25: We Come 1 (Single)

      Amazon: Canada | Deutschland | France | United Kingdom | United States

      Visit muspy for more information.

    15. 🔗 New Music Releases Kaskade - ORIGIN // rss

      Kaskade - a new release is available:

      • 2026-09-25: ORIGIN // (Album)

      Amazon: Canada | Deutschland | France | United Kingdom | United States

      Visit muspy for more information.

    16. 🔗 New Music Releases The Ocean - Solaris rss

      The Ocean - a new release is available:

      • 2026-09-25: Solaris (Album)

      Amazon: Canada | Deutschland | France | United Kingdom | United States

      Visit muspy for more information.

    17. 🔗 Ampcode News Less Noise rss

      Amp now collapses your agent's step-by-step work even more, since you care about what the agent did, not how it got there. (You can still expand the steps when needed.)

      A year ago, watching your agent's every step sometimes helped, and you probably were only running one agent.

      Now? You have lots of agents. You should be giving them harder, deeper work and verification demands. They should run for longer on their own, without you watching them from up close. If you have the patience to watch your agents work, you're giving them too short a leash.

      Ten separate rows of commands, reads, searches, and edits become one expandable summary. Both are followed by the same assistant reply, fading at the second line.