before ┌────────┐ wants to run ┌───────────┐ ┌──────┐ │ Claude │ ─────────────▶ │ your hook │ ─────────▶ │ bash │ └────────┘ │ (watches) │ always └──────┘ └───────────┘ after ┌────────┐ wants to run ┌───────────┐ allowed? ┌──────┐ │ Claude │ ─────────────▶ │ your hook │ ─────────▶ │ bash │ └────────┘ │ (decides) │ └──────┘ ▲ └─────┬─────┘ │ "no, and here is why" │ exit 2 └──────────────────────────┘green is the new arrow. the rest of the loop already existed.
Until now a hook could only watch Claude work: it saw every command and could log it, but not stop it. Now a hook that exits with code 2 blocks the command before it runs, and whatever the hook printed goes back to Claude as a message. Claude reads the reason like it reads anything else you say, and tries another way.
before model ──▶ ┌────────────────┐ ──▶ http ──▶ you │ buffer: wait │ wait 4s, then │ for the whole │ everything at once │ answer │ └────────────────┘ after model ──▶ http ──▶ you each token the moment it exists first word in 80ms
People reported that the chat looked frozen on long answers. It was not frozen. One component was holding the entire reply until the model finished, then sending all of it at once. The fix removed that component, so each word is sent the moment it exists. Nothing got faster. It just stopped waiting.
before app.cjs ── require('./lib.mjs') ──▶ ERR_REQUIRE_ESM rewrite app.cjs as ESM, or await import() everywhere after app.cjs ── require('./lib.mjs') ──▶ the module, synchronously same call, same shape, same as it always was for CommonJSthe one rule: the ES module must not use top-level await.
For years a CommonJS file could not pull in an ES module without rewriting itself or sprinkling async imports everywhere. Node now lets require() load an ES module directly, as long as that module does not wait on anything at its top level. Most libraries do not, so most of the old wall is simply gone.
before let count = 0 the compiler guesses this is state $: double = count * 2 and this is derived, from the $ label after let count = $state(0) let double = $derived(count * 2) you say it. the compiler no longer guesses.the rules stopped being magic inside .svelte files only; they now work in any .js file too.
Svelte used to decide what was reactive by looking at how you wrote an assignment, which worked until it did not. Version 5 gives you a handful of words, called runes, to mark state and derived values yourself. Less magic, and the same words work outside components, so a store is just a function.
before src ──▶ ┌───────────┐ ──▶ bundle 48s │ webpack │ └───────────┘ now src ──▶ ┌───────────┐ ──▶ bundle 9s │ turbopack │ └───────────┘same code in, same bundle out. only the box in the middle changed.
The tool that turns your source files into one bundle for the browser was swapped. The new one had been optional for a year while it caught up on every edge case the old one handled. Last week it passed the full test suite, so it became the default. You do not change anything. Your build is just shorter.
before thread 1 ──▶ [ GIL ] ──▶ runs thread 2 waits thread 3 waits one at a time, however many cores you own after (the free-threaded build, python3.13t) thread 1 ──▶ runs thread 2 ──▶ runs thread 3 ──▶ runs each on its own corea separate build for now. the default build still has the lock.
For thirty years one lock let only one thread run Python code at a time, so threads helped with waiting but never with computing. 3.13 ships a build without that lock. Pure Python work spreads across cores. It is marked experimental, the default build keeps the lock, and some C extensions need updating first.
before ┌──────────┐ ┌──────────────┐ ┌──────────────┐ │ lockfile │ ───────────▶ │ the registry │ ─────▶ │ node_modules │ └──────────┘ every pkg, └──────────────┘ └──────────────┘ every time (network) after ┌──────────┐ seen it? ┌──────────────┐ yes ┌──────────────┐ │ lockfile │ ───────────▶ │ global cache │ ─────▶ │ node_modules │ └──────────┘ └──────┬───────┘ └──────────────┘ │ no, fetch it ▼ the registrythe cache is the new box. every arrow after it already existed.
Every install used to ask the package registry about every package, even one already on your disk from last week. Now the list of what your project needs is checked against a cache on your machine first. Only the packages it has never seen go out over the network. On a plane, with a project you have built before, install just works.
before tsc, written in TypeScript, running on Node VS Code's codebase: type-check in 77.8 s after tsgo, the same compiler, line for line, in Go VS Code's codebase: type-check in 7.5 sa port, not a rewrite: the same checker, the same errors, the same behaviour.
The TypeScript team is porting the compiler from TypeScript to Go, keeping the design identical so the output stays the same. Native code gives about half the speedup. The other half comes from doing work on several cores at once, which the old compiler, running on one JavaScript thread, could never do.
before ┌────────┐ │ agent │ ─────▶ your project ~/.ssh ~/Documents the rest └────────┘ same permissions as you. it can open all of them. after ┌────────┐ ┌──────────────────────────┐ │ agent │ ─────▶ │ sandbox │ └────────┘ │ ┌────────────────────┐ │ │ │ │ your project files │ │ │ │ └────────────────────┘ │ │ └──────────────────────────┘ │ └─── ✕ ───▶ ~/.ssh ~/Documents the rest
The agent used to run with the same permissions as you. If it decided to read your keys or edit a file two folders up, nothing stopped it. Now it runs inside a boundary that contains only the project you opened. Anything outside is not hidden, it is simply not there. The agent asks you when it needs something past the wall.
before pkg a ──▶ pkg b ──▶ pkg c ──▶ pkg d ──▶ done 9.8s one at a time, each waits for the one before it now pkg a ──┐ pkg b ──┤ pkg c ──┼──▶ done 0.4s pkg d ──┘ all at once, then reconcile what they asked for
Working out which versions of which packages fit together used to be a chain: ask about one, wait for the answer, ask about the next. Now every question is asked at the same time and the answers are reconciled at the end. The result is the same set of versions. The wait is the longest single answer instead of the sum of all of them.