What it took to ship PiG 0.5.0

PiG 0.5.0 follows Pi 1.3.0 plus the Pi main-branch commits up to 1402973a. This post goes through what changed, how much lighter it got and why, how we check that PiG still behaves like Pi, and where to go next if you want to dig in.
What changed
PiG now covers every one of Pi's semantic interfaces on 1.3.0, including Chord, Pi Durable and remote execution environments over SSH. Piglets can keep, replace or strip any part of PiG on the Go SDK. Go, Rust and Python extensions get the same conversation tools that TypeScript has in Pi. And 0.5.0 uses less CPU and memory than 0.4.1 on every task we measured. For example, pig --version takes 5 ms of CPU, where Pi on Node takes 282.
Durable deserves a plain description, because it is easy to make it sound vague. Pi Durable is a stateful agent whose state lives on a server, separate from the client (TUI, web UI, mobile) and from the chat history, so a session survives a disconnected client and any client can attach to it. PiG runs Durable natively in Go. In 0.5.0 its cost no longer grows with your history, a session finds its next wake without walking every live task, and the Cloudflare host follows Pi 1.3.0 and is tested in CI. The code is in durable/.
PiG is free and open source under the MIT licence. Nothing is held back for a hosted or paid tier. Piglets, the SDKs, Durable and buffon are all there for anyone to use, fork and build on.
The test suite grew with the release. PiG checks itself against the real Pi across far more of its behavior, and every difference those checks found is closed and listed in a public ledger, including 29 bugs that Pi still has and PiG fixes first. By file, the port map covers every Pi source file. Most are fully ported, some are partial, and work on the partial ones continues. By interface, every one of Pi's semantic interface IDs is closed, and a strict gate keeps the gap list empty.
If you only read one more section, the Piglet slots section shows what you can now build, and buffon lets you rerun every number here yourself. Buffon is at michaelkinsy.github.io/buffon.
0.4.1 to 0.5.0
Area | 0.4.1 | 0.5.0 |
| Pi version followed | Pi 1.0.3 | Pi 1.3.0+1402973a, Pi's newest |
| Interface coverage | Pi 1.x surface | Every Pi semantic interface, held by a zero-gap gate |
| Durable | Ported | Lighter: cost no longer grows with history; code mode and tool search |
| Cloudflare | Earlier Pi facade | Follows Pi 1.3.0, suite in CI |
| Piglets/SDK | Shareable configurations | Slots to keep, replace or strip any part; Go SDK Piglets; equal tools in Go, Rust and Python |
| Long-session cost | Grew with session length in model lookup, branch summary and Durable turns | Model lookup and branch summary scale linearly; Durable turns no longer copy history |
| Robustness testing | Regular CI | Large parallel stress sweeps, race detector, many CPUs |
How we prove that PiG behaves like Pi
The default is byte parity, which means PiG prints the same bytes that Pi prints for the same input. A test never takes its expected result from PiG's own output. The expected result comes from Pi itself or from a rule that must always hold. Three practices carry this.
- Tests against the real Pi. Differential tests run Pi's own code next to PiG's on the same input and compare the output byte for byte. They cover how wide a character is on screen, how each model provider streams its answer, and how Markdown and streamed file writes are drawn.
- Divergence records. Sometimes PiG has to differ from Pi. Each such difference is called a divergence and gets a numbered record. The record lists where the difference lives in the code, the evidence for it, and the condition that will remove it. The owner approves every record.
- Speed must not show. A speed-up that changes even one byte of output counts as a divergence and needs approval. All the speed work below was held to that rule.
Divergences add to Pi. They do not take Pi behavior away. A divergence adds a feature Pi does not have, fixes a Pi bug that is still open upstream, or adapts to a platform. One example is that OpenRouter sessions show the cost OpenRouter actually bills, where Pi shows an estimate that can be well off. 0.5.0 also retired divergences. Pi fixed several differences itself, for example how a read line range with a string offset is shown (D154).
How much faster 0.5.0 is
We measured with buffon, the open harness benchmark I built for the Pi community, on one Linux server (Intel Xeon Platinum 8462Y+). Every harness ran on the same pinned cores at a fixed 2.8 GHz with turbo off, so the absolute times are higher than on a machine that boosts. Compare the relative results, not the milliseconds, with numbers from other machines. The run used the 0.5.0 release candidate, and each figure is the median of the clean rerun. The table gives CPU time in milliseconds and peak memory in MiB. Lower is better.
Task | PiG 0.5.0 | PiG 0.4.1 | Pi-Bolt | Pi (Node) |
--version | 5 ms / 4 MiB | 18 ms / 9 MiB | 11 ms / 7 MiB | 282 ms / 58 MiB |
One prompt (-p) | 16 / 7 | 64 / 16 | 44 / 19 | 424 / 63 |
| One prompt with tools | 29 / 11 | 119 / 18 | 56 / 22 | 518 / 86 |
| RPC session | 71 / 21 | 438 / 21 | 108 / 30 | 675 / 89 |
| Interactive start | 61 / 15 | 145 / 26 | 55 / 19 | 536 / 73 |
| Resume a long session | 69 / 21 | 137 / 25 | 50 / 23 | 443 / 68 |
| Streaming a long answer in the TUI | 403 / 18 | 2,451 / 37 | 925 / 36 | 2,850 / 113 |
| One prompt, 10 TypeScript extensions | 934 / 217 | 2,191 / 509 | 109 / 34 | 676 / 106 |
| RPC, 10 TypeScript extensions | 2,016 / 421 | 9,430 / 595 | 194 / 44 | 950 / 148 |
0.5.0 uses less CPU and memory than 0.4.1 on every row. Without extensions, PiG uses less than Pi on every row, and less than Pi-Bolt on the version check, one prompt, one prompt with tools, the RPC session and TUI streaming.
We also lose on some rows. Pi-Bolt is slightly ahead on interactive start and on resuming a long session. With TypeScript extensions, PiG is slower than both Pi and Pi-Bolt, because it starts a separate Node host to run them. That is much better than 0.4.1, but it is still a loss.
The release notes have the full table and the method.
buffon is meant to be impartial. It runs the same Pi (TypeScript) extensions for every harness, and other extension formats can be added. buffon stands on the shoulders of the Pi community, Pi, Pi-Bolt, durable-bench, and everyone who posts their evals and keeps their work open source. It is MIT licensed and will stay free.
The same extensions in Go
This comparison is our own run, not part of buffon, and it covers PiG only. We took buffon's benchmark extension, ported it to Go and fused the copies into a Piglet. An equivalence check confirmed that the Go extensions behave the same as the TypeScript ones. They give the same commands, tools, tool results and command results. buffon labels this run provisional, because a warm-up round had to wait for the machine to go quiet. A repeat run agreed within a few percent on every cell.
With ten extensions in print mode, the Go Piglet takes 67 ms of CPU, where PiG with the TypeScript originals takes 941 and Pi-Bolt takes 109. In RPC mode the Go Piglet takes 521 ms, which is much less than the 2,004 ms of the TypeScript extensions, but Pi-Bolt still wins with 194. The Go extensions run inside the PiG process, and the TypeScript extensions run in a Node host, so these numbers describe that choice and not the language alone. The scripts, the Piglet sources and the raw results are in go-ext-comparison.tar.gz, attached to the release.
We thank the Pi-Bolt project for publishing its benchmarks in the open so that anyone can rerun them. Their suite pointed us at PiG's slowest paths.
The patterns behind the slow paths
Most of the speed came from removing a few patterns that appeared again and again. Each one is shown with a real example from this release.
Repeating work on a growing buffer. A streamed file write highlighted the whole file again each time a new piece arrived. A 1,500-function file allocated 1.35 GB over 60 updates, and now allocates 48 MB. Tool-call arguments were parsed again from the start for every token. The code that builds a long answer took 3.4 seconds and 7.7 GB of allocations for 50,000 streamed pieces, and now takes 0.15 seconds and 49 MB. Each of these turns a long stream into quadratic work, where the work grows with the square of the input.
Sending data that nobody reads. An extension that listened for message updates received the whole partial message twice for each new piece of text. It now receives only the change. An event that no extension listens to now costs nothing.
Encoding the same thing again and again. The extension host encoded model lists, session data and the command list on every update. It now encodes them once and again only when they change. A completions request encoded its history several times, and it now reuses one buffer. A 75-turn request is now built in 233 KB instead of 617 KB.
Too many small allocations. The startup header redrew the pig head and wrapped the key hints again on every frame, which was most of the objects a streaming turn allocated. Checking whether the tool set had changed serialised every tool schema, which took 4,418 allocations and now takes 56.
Too many hand-offs per piece of text. Each streamed piece passed between many coroutines, the small cooperative tasks PiG uses to run Pi's asynchronous code. The OpenAI path made about 24 of these hand-offs per piece and now makes about 4. A test keeps that number from creeping back up.
Keeping what will never be used again. A soak test, which runs tens of thousands of turns to watch memory over time, found three caches that held old data. Each was small per turn and added up over a long session. All three are gone, and each has a test that failed before the fix and passes after it.
Building tables too early. Large tables such as the emoji sets and the Markdown patterns were built when PiG started. They are now built on first use, so startup is faster and most sessions never pay that cost.
Pathological input. Markdown with 4,000 nested emphasis markers took 17 seconds and 16 GB. It now takes 0.16 seconds and 19 MB, and the output is identical.
A long session still grows slowly in memory, because PiG keeps every entry for the /tree view, as Pi does. Going lower would change how /tree behaves, so that is a future decision and not something we changed quietly.
We also name where PiG is still slower. The TinyGo build of the Durable core, which exists only for Cloudflare and JavaScript hosts, starts cold more slowly than Pi's. The first use of code mode costs a few milliseconds. TypeScript extensions use more memory at startup under PiG than under Pi.
Following Pi to its newest commit
We moved the target several times in the last days of the cycle, to Pi's main branch, to the Pi 1.3.0 release and then to 1.3.0 plus 1402973a. Every Pi commit up to 1402973a is ported, including Chord link protocols 3 and 4, so PiG and Pi Chord hosts can link to each other, and Pi's newest telemetry work, which adds trace propagation, span links and metrics across the agent, Chord and Durable. Chord is Pi's library for connecting agent hosts and sharing state between them.
The late ports matter to users. Invalid final tool-call arguments are now reported, and the tool no longer runs with half the arguments. One resolver now handles model names for --model, --models, /model and auth printing, and it names the providers when a name is ambiguous. Changing the GPT-6 thinking level keeps the prompt cache.
Piglets and the slot system
A Piglet is a named, shareable PiG configuration. It picks a model, tools, extensions, skills and settings, and it can be built into a signed Piglet Binary that runs as its own program. In 0.5.0 a Piglet can also change PiG itself, through one model we call slots.
Every part of PiG that a Piglet can change is a slot. The slots cover built-in tools, slash commands, built-in extensions, model APIs, features such as themes or HTML export, and the interactive frontend. For each slot a Piglet does one of three things. It keeps PiG's own part, which is the default. It replaces the part with a Go member fused into the Binary, which today is possible for the frontend, so a Piglet can draw its own interactive mode. Or it strips the part, so it is gone at run time and a Piglet Binary leaves its code out entirely.
A strip list can also run in keep mode. It then names what stays, and everything else is stripped, including built-ins that a later PiG adds. A minimal Piglet stays minimal as PiG grows.
Slots and strip lists inherit through extends, so one Piglet can be the base of another. A child can strip more, restore a part with extends.remove, and bring a stripped built-in back only when it says so with extends.allowWiden. Stripping does not close anything off. A stripped Piglet still loads your own extensions in any SDK it keeps.
Stripping also makes Binaries smaller. In one test build, a Piglet Binary with one fused Go extension came out much smaller with everything stripped. If you embed PiG in your own Go program, the same switches are Go build tags such as pig_strip_mcp.
Piglets also became easier to share and to trust. pig piglet publish --to npm publishes one so anyone can add it with pig piglet add. pig piglet tree and pig piglet lineage show how installed Piglets extend each other and what each link adds or removes. pig piglet verify --source recomputes the digests of a source and fails when a signed Binary records different ones.
Extensions in four languages, on equal terms
Extensions in Go, Rust and Python now get the same conversation tools that TypeScript extensions get in Pi. They can draw a conversation the way the main transcript does, read the footer data and follow branch changes, and their tool and command events run through one shared conformance suite that checks all four SDKs. TypeScript extensions can run on Bun instead of Node, and the Bun download is verified against a pinned signature. When an SDK changes, pig extension upgrade rewrites an older Go extension with tested rules, and a build that fails only on changed SDK names says exactly what changed.
Keeping up with Pi, everywhere
0.5.0 ports Chord, Pi's library for connecting agent hosts and sharing state between them, with its host manager. It ports Pi Durable's code mode, deferred tools with tool search, nested tool calls, and remote execution environments over SSH. Termux on Android has its own fixes and documentation. PiG's Durable benchmarks run on Kubernetes.
The Cloudflare Durable host now follows Pi 1.3.0. Its facade and the oracle it is tested against were ported to Pi 1.3.0's tool results, and Pi's async context now travels with each host event, checked against a multi-turn program run on the real Pi. The Cloudflare suite runs in CI on both Go toolchains. Host crossings whose continuation only starts the next await are fused, so a warm turn makes fewer trips across the host boundary.
Durable itself got lighter. Harness turns no longer copy the history, a session recomputes its next wake without walking every live task on each commit, request cost stays flat as more requests are in flight, task listings page from storage, and a launched code mode tool call has started before the script goes on. 0.5.0 is also more robust under heavy load. We stress-tested it far harder than any earlier release, with large parallel stress sweeps of every Go package under the race detector, on machines with many CPUs. That fixed rare edge cases under heavy concurrency in Durable sessions, Chord publication (now in Pi's single-queue order), the interactive mode and provider stream start, each fixed at the cause with a test. Error reporting is clearer too. A failing extension now reports its own error instead of a generic "process exited".
How we build it
Every change goes through a test run against the real Pi and a review before it lands, and heavy verification runs as one grouped pass on a dedicated build machine. Each release reruns the full suite, and the performance gates run on quiet, pinned cores so a speed claim means something.
What comes next
- Keep following Pi's main branch continuously, so each PiG release ships soon after the Pi release it tracks.
- Republish Pigpen's Packages for the new SDK.
- Publish buffon, the open harness benchmark I built for the Pi community, so anyone can rerun or contribute.